Skip to content

Latest commit

 

History

History
254 lines (182 loc) · 9.36 KB

File metadata and controls

254 lines (182 loc) · 9.36 KB

Agent Runners

The orchestrator invokes AI coding agents through the AgentRunner interface. Runners are auto-discovered from environment variables via the RunnerRegistry.

AgentRunner Interface

Every runner implements:

interface AgentRunner {
  run(ctx: AgentContext): Promise<AgentResult>;
}

The orchestrator provides context (issue details, codebase profile, constraints) and the runner spawns the agent, collects output, and commits changes.

Available Runners

ClaudeCodeRunner

Invokes Claude Code CLI in --print mode.

Property Value
CLI command claude -p --model <model> --allowedTools <tools>
stdin Prompt sent via stdin
Auth ANTHROPIC_API_KEY (inherited from environment)
Model override AI_SDLC_MODEL env var (default: claude-sonnet-4-5-20250929)
Registration Always available (built-in)
import { ClaudeCodeRunner } from '@ai-sdlc/orchestrator';

CopilotRunner

Invokes GitHub Copilot CLI in --yolo autonomous mode.

Property Value
CLI command copilot -p <prompt> --yolo [--model <model>]
stdin None (prompt is a CLI argument)
Auth GH_TOKEN or GITHUB_TOKEN (passed through environment)
Model override AI_SDLC_COPILOT_MODEL env var
Registration Available when GH_TOKEN or GITHUB_TOKEN is set
import { CopilotRunner } from '@ai-sdlc/orchestrator';

CursorRunner

Invokes Cursor CLI agent with NDJSON stream output.

Property Value
CLI command cursor-agent --print <prompt> --force --output-format=stream-json [-m <model>]
stdin None (prompt is a CLI argument)
Auth CURSOR_API_KEY
Model override AI_SDLC_CURSOR_MODEL env var
Registration Available when CURSOR_API_KEY is set

The runner parses NDJSON output to extract the final assistant message for use as the PR summary.

import { CursorRunner, parseStreamJson } from '@ai-sdlc/orchestrator';

CodexRunner

Invokes OpenAI Codex CLI in --full-auto --json mode.

Property Value
CLI command codex exec - --full-auto --json [-m <model>]
stdin Prompt written to stdin (Codex reads from -)
Auth CODEX_API_KEY
Model override AI_SDLC_CODEX_MODEL env var
Registration Available when CODEX_API_KEY is set

The runner includes an enhanced token usage parser that first tries NDJSON usage/token_usage events from stderr (accumulating across multiple events), then falls back to regex.

import { CodexRunner, parseTokenUsage } from '@ai-sdlc/orchestrator';

GenericLLMRunner

Invokes any OpenAI-compatible chat completions API over HTTP.

Property Value
Transport HTTP POST to /v1/chat/completions endpoint
Auth API key via Authorization: Bearer header
Registration Available when OPENAI_API_KEY or LLM_API_KEY + LLM_API_URL is set
import { GenericLLMRunner } from '@ai-sdlc/orchestrator';

Selecting a Runner

--runner <name> flag

The ai-sdlc run command accepts a --runner <name> flag to select any registered runner by name:

ai-sdlc run --issue 42 --runner copilot
ai-sdlc run --issue 42 --runner cursor
ai-sdlc run --issue 42 --runner claude-code   # explicit built-in default

If the specified name is not registered, the command fails immediately with an actionable error listing the available runners — no silent fallback.

AI_SDLC_RUNNER_PLUGIN environment variable

To plug in a custom runner (e.g. Kiro, a proprietary agent CLI, or a test double) without forking the package, set AI_SDLC_RUNNER_PLUGIN to the path of a module that exports an AgentRunner:

export AI_SDLC_RUNNER_PLUGIN=/path/to/my-runner.mjs
ai-sdlc run --issue 42

The module must export a default export or a named runner export satisfying the AgentRunner interface:

// my-runner.mjs  (ESM)
export default {
  async run(ctx) {
    // Invoke your agent, commit changes, return AgentResult
    return { success: true, filesChanged: ['src/fix.ts'], summary: 'Done.' };
  },
};

Or with a named export:

// my-runner.mjs
export const runner = {
  async run(ctx) { /* ... */ }
};

If the module cannot be imported or does not export a valid AgentRunner, the pipeline fails immediately with an actionable error — no silent fallback.

Runner precedence

The full precedence chain (first match wins):

Priority Source How to set
1 (highest) Programmatic injection new Orchestrator({ runner: myRunner })
2 --runner <name> flag ai-sdlc run --runner copilot
3 AI_SDLC_RUNNER_PLUGIN export AI_SDLC_RUNNER_PLUGIN=/path/to/runner.mjs
4 (default) ClaudeCodeRunner (always available)

Env-discovered runners do not auto-select. Setting GH_TOKEN, OPENAI_API_KEY, ANTHROPIC_API_KEY, etc. registers the corresponding runner so it is selectable by name (--runner copilot), but the mere presence of an ambient env var does not silently switch the default runner — these vars are commonly set for unrelated tools. Absent an explicit --runner/AI_SDLC_RUNNER_PLUGIN/programmatic selection, the default is always ClaudeCodeRunner. Select an env-discovered runner explicitly with --runner <name>.

Runner Registry

The RunnerRegistry manages discovery and selection of runners:

import { createRunnerRegistry } from '@ai-sdlc/orchestrator';

const registry = createRunnerRegistry();

// List all available runners
const available = registry.listAvailable();
console.log(available.map(r => r.name));
// ['claude-code', 'copilot', 'cursor', ...]

// Get a specific runner
const runner = registry.get('copilot');

// Get the default runner (first available)
const defaultRunner = registry.getDefault();

Auto-Discovery

discoverFromEnv() registers runners based on environment variables:

Runner Required Env Var(s) Source
claude-code (always available) built-in
copilot GH_TOKEN or GITHUB_TOKEN env
cursor CURSOR_API_KEY env
codex CODEX_API_KEY env
openai OPENAI_API_KEY env
anthropic ANTHROPIC_API_KEY env
generic-llm LLM_API_URL + LLM_API_KEY env

Manual Registration

You can register custom runners:

import { RunnerRegistry } from '@ai-sdlc/orchestrator';

class MyCustomRunner implements AgentRunner {
  async run(ctx: AgentContext): Promise<AgentResult> {
    // Custom agent invocation logic
  }
}

const registry = new RunnerRegistry();
registry.register('my-agent', new MyCustomRunner());

OpenShell Sandbox Integration

When the sandboxId field is set on AgentContext, CLI-based runners execute the agent inside an NVIDIA OpenShell sandbox. The runner prefixes the spawn command with openshell sandbox connect <id> --, so instead of:

claude -p --model claude-sonnet-4-5-20250929 --allowedTools Edit,Write,...

it becomes:

openshell sandbox connect aisdlc-issue-42-1711316400 -- claude -p --model claude-sonnet-4-5-20250929 --allowedTools Edit,Write,...

This provides kernel-level isolation (Landlock filesystem policies, seccomp syscall filtering, network policy enforcement) without any changes to the agent itself. The orchestrator's executePipeline() automatically passes sandboxId when a SecurityContext with an OpenShell sandbox is configured.

See Security > OpenShell for setup details.

Common Pattern

All CLI-based runners (Claude Code, Copilot, Cursor, Codex) follow the same subprocess pattern:

  1. Build promptbuildPrompt(ctx) constructs a prompt from issue details, constraints, codebase context, and episodic memory
  2. Spawn CLI — Run the agent CLI as a child process with appropriate flags
  3. Collect output — Buffer stdout and stderr
  4. Parse token usage — Extract input/output token counts from stderr for cost tracking
  5. Git diff — Run git diff --name-only and git ls-files --others to find changed files
  6. Commitgit add -A and git commit with the configured message template and co-author

Environment Variables

Variable Default Description
AI_SDLC_RUNNER_PLUGIN (none) Path to a custom runner plugin module (ESM/CJS, must export default or named runner). Fails fast on invalid module.
AI_SDLC_MODEL claude-sonnet-4-5-20250929 Model for ClaudeCodeRunner
AI_SDLC_COPILOT_MODEL (CLI default) Model override for CopilotRunner
AI_SDLC_CURSOR_MODEL (CLI default) Model override for CursorRunner
AI_SDLC_CODEX_MODEL (CLI default) Model override for CodexRunner
AI_SDLC_RUNNER_TIMEOUT 900000 (15 min) Runner timeout in ms (supports duration strings)
AI_SDLC_LINT_COMMAND (none) Lint command injected into agent prompts
AI_SDLC_FORMAT_COMMAND (none) Format command injected into agent prompts
AI_SDLC_COMMIT_MESSAGE_TEMPLATE fix: resolve issue #{issueNumber}\n\n{issueTitle} Commit message template
AI_SDLC_COMMIT_CO_AUTHOR Claude <noreply@anthropic.com> Co-author for commits