AI-SDLC
AI-SDLC
Api reference

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.

PropertyValue
CLI commandclaude -p --model <model> --allowedTools <tools>
stdinPrompt sent via stdin
AuthANTHROPIC_API_KEY (inherited from environment)
Model overrideAI_SDLC_MODEL env var (default: claude-sonnet-4-5-20250929)
RegistrationAlways available (built-in)
import { ClaudeCodeRunner } from '@ai-sdlc/orchestrator';

CopilotRunner

Invokes GitHub Copilot CLI in --yolo autonomous mode.

PropertyValue
CLI commandcopilot -p <prompt> --yolo [--model <model>]
stdinNone (prompt is a CLI argument)
AuthGH_TOKEN or GITHUB_TOKEN (passed through environment)
Model overrideAI_SDLC_COPILOT_MODEL env var
RegistrationAvailable when GH_TOKEN or GITHUB_TOKEN is set
import { CopilotRunner } from '@ai-sdlc/orchestrator';

CursorRunner

Invokes Cursor CLI agent with NDJSON stream output.

PropertyValue
CLI commandcursor-agent --print <prompt> --force --output-format=stream-json [--model <model>]
stdinNone (prompt is a CLI argument)
AuthCURSOR_API_KEY
Model overrideAI_SDLC_CURSOR_MODEL env var
RegistrationAvailable 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.

PropertyValue
CLI commandcodex exec - --full-auto --json [-m <model>]
stdinPrompt written to stdin (Codex reads from -)
AuthCODEX_API_KEY
Model overrideAI_SDLC_CODEX_MODEL env var
RegistrationAvailable 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.

PropertyValue
TransportHTTP POST to /v1/chat/completions endpoint
AuthAPI key via Authorization: Bearer header
RegistrationAvailable when OPENAI_API_KEY or LLM_API_KEY + LLM_API_URL is set
import { GenericLLMRunner } from '@ai-sdlc/orchestrator';

Ollama (local models)

There is no dedicated Ollama runner class — Ollama exposes an OpenAI-compatible chat-completions endpoint that GenericLLMRunner already speaks. This is also how Gemma (and any other model you ollama pull) runs — via the OpenAI-compatible HTTP path, not via a Claude-Code model alias.

Generic form (works today with any three env vars):

export LLM_API_URL=http://localhost:11434/v1/chat/completions
export LLM_API_KEY=ollama          # any non-empty value; Ollama ignores it
export LLM_MODEL=gemma4:31b
ai-sdlc run --issue 42 --runner generic-llm

ollama preset form (first-class, named preset — mirrors openai/anthropic):

export OLLAMA_MODEL=gemma4:31b      # defaults apiUrl to http://localhost:11434/v1/chat/completions
ai-sdlc run --issue 42 --runner ollama

Override the endpoint with OLLAMA_API_URL (exact URL, takes precedence) or OLLAMA_HOST (host[:port], normalized into the /v1/chat/completions URL — matches the env var Ollama itself reads):

export OLLAMA_MODEL=gemma4:31b
export OLLAMA_HOST=127.0.0.1:11500  # -> http://127.0.0.1:11500/v1/chat/completions

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):

PrioritySourceHow to set
1 (highest)Programmatic injectionnew Orchestrator({ runner: myRunner })
2--runner <name> flagai-sdlc run --runner copilot
3AI_SDLC_RUNNER_PLUGINexport 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:

RunnerRequired Env Var(s)Source
claude-code(always available)built-in
copilotGH_TOKEN or GITHUB_TOKENenv
cursorCURSOR_API_KEYenv
codexCODEX_API_KEYenv
openaiOPENAI_API_KEYenv
anthropicANTHROPIC_API_KEYenv
generic-llmLLM_API_URL + LLM_API_KEYenv
ollamaOLLAMA_MODEL (overridable via OLLAMA_API_URL/OLLAMA_HOST)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 prompt — buildPrompt(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. Commit — git add -A and git commit with the configured message template and co-author

Environment Variables

VariableDefaultDescription
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_MODELclaude-sonnet-4-5-20250929Model 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_TIMEOUT900000 (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_TEMPLATEfix: resolve issue #{issueNumber}\n\n{issueTitle}Commit message template
AI_SDLC_COMMIT_CO_AUTHORClaude <noreply@anthropic.com>Co-author for commits
OLLAMA_MODEL(none)Selects the ollama runner preset; model name (e.g. gemma4:31b)
OLLAMA_API_URLhttp://localhost:11434/v1/chat/completionsExact chat-completions URL override for the ollama preset
OLLAMA_HOST(none)Host[:port] override for the ollama preset (normalized to the /v1/chat/completions URL); ignored if OLLAMA_API_URL is set