Skip to main content

Adapters

Adapters are the bridge between the orchestrator and the agent that does the work. Every adapter implements the same contract: receive a dispatch bundle, produce a staged turn result. The orchestrator does not care whether the work is done by a human, a local subprocess, an MCP stdio server, a remote API, or an external agent service — it only cares that the result conforms to the protocol.

Before you wire a runtime, read the adjacent operator surfaces:

For the canonical reference of all five runtimes, three authority levels, and which combinations are valid, see the Runtime Matrix.

Package export

If you are building a non-CLI runner and want to reuse the shipped adapters, import them through the public package boundary:

import {
ADAPTER_INTERFACE_VERSION,
dispatchLocalCli,
dispatchApiProxy,
dispatchMcp,
dispatchRemoteAgent,
printManualDispatchInstructions,
waitForStagedResult,
readStagedResult,
} from 'agentxchain/adapter-interface';

Do not import deep source paths like cli/src/lib/adapters/local-cli-adapter.js. That is implementation detail, not contract.

For a runnable example that uses this boundary with dispatchLocalCli, see examples/external-runner-starter/run-adapter-turn.mjs.

Shared adapter contract

Every adapter follows a three-phase lifecycle:

1. Dispatch

The orchestrator writes a dispatch bundle to .agentxchain/dispatch/turns/<turn_id>/. The bundle contains everything the agent needs to do its work.

2. Wait

The orchestrator waits for the adapter to signal completion. How it waits depends on the adapter type (polling, subprocess exit, HTTP response).

3. Collect

The orchestrator reads the staged result from .agentxchain/staging/<turn_id>/turn-result.json, validates it against the protocol schema, and either accepts it into the pipeline or marks it as failed.

┌─────────────┐ dispatch bundle ┌──────────────┐
│ Orchestrator │ ──────────────────────► │ Adapter │
│ │ │ │
│ │ ◄────────────────────── │ │
│ │ staged result │ │
└─────────────┘ └──────────────┘

Filesystem contract

Dispatch bundle

The orchestrator writes these files to .agentxchain/dispatch/turns/<turn_id>/:

FilePurpose
ASSIGNMENT.jsonStructured assignment metadata: run ID, turn ID, role, phase, runtime ID, write authority, staging path, budget reservation
MANIFEST.jsonIntegrity manifest listing all dispatch files with SHA-256 checksums, written after after_dispatch hooks complete
PROMPT.mdThe role prompt from .agentxchain/prompts/<role>.md with variable interpolation, protocol rules, and role-scoped workflow-kit responsibilities for the current phase when applicable
CONTEXT.mdAssembled context from previous turns: accepted decisions, open objections, files changed, verification evidence, and the phase-wide Workflow Artifacts table when workflow-kit is present
ASSIGNMENT.json
{
"run_id": "run_a1b2c3",
"turn_id": "turn_x7y8z9",
"role": "dev",
"phase": "implementation",
"runtime_id": "local-dev",
"write_authority": "authoritative",
"staging_result_path": ".agentxchain/staging/turn_x7y8z9/turn-result.json",
"reserved_paths": [],
"allowed_next_roles": ["dev", "qa", "eng_director", "human"],
"attempt": 1,
"deadline_at": null,
"assigned_sequence": 3,
"budget_reservation_usd": 2
}

Staged result schema

The adapter must write a valid turn result to .agentxchain/staging/<turn_id>/turn-result.json. The schema is consistent across all adapters:

turn-result.json
{
"schema_version": "1.0",
"run_id": "run_...",
"turn_id": "turn_...",
"role": "dev",
"runtime_id": "local_cli-claude",
"status": "completed",
"summary": "Implemented the authentication module...",
"decisions": [
{
"id": "DEC-005",
"category": "implementation",
"statement": "Use bcrypt for password hashing",
"rationale": "Industry standard, constant-time comparison"
}
],
"objections": [
{
"id": "OBJ-003",
"severity": "low",
"against_turn_id": "turn_prev",
"statement": "PM spec did not address rate limiting",
"status": "raised"
}
],
"files_changed": [
{ "path": "src/auth.ts", "action": "created" },
{ "path": "src/auth.test.ts", "action": "created" }
],
"verification": {
"status": "passed",
"commands": ["npm test", "npm run lint"],
"evidence_summary": "All 14 tests pass. No lint errors.",
"machine_evidence": [
{ "command": "npm test", "exit_code": 0, "stdout_tail": "14 passing (1.2s)" }
]
},
"artifact": {
"type": "code",
"ref": "src/auth.ts"
},
"proposed_next_role": "qa",
"phase_transition_request": null,
"run_completion_request": null
}

Required fields: All fields shown above are required. The objections array is required and must be a valid array. For review_only roles, the protocol enforces a challenge requirement: at least one objection must be raised. Other roles may submit an empty objections array.

Custom phases are operator-defined, not scaffold-generated. If you extend routing with phases like design or security_review, you must also define their gate files in gates, and phase_transition_request may only target the immediate next declared phase. If you want those phases to have scaffolded docs or structural validation, declare workflow_kit explicitly in agentxchain.json; otherwise custom phases rely on requires_files alone and do not inherit the built-in planning / implementation / qa workflow artifacts.

When workflow_kit is present, the dispatch bundle splits visibility from accountability on purpose:

  • CONTEXT.md renders the full Workflow Artifacts table for the current phase so the agent can see the whole contract.
  • PROMPT.md renders only the workflow-kit responsibilities assigned to the current role.
  • Ownership resolves from owned_by first. If owned_by is absent, accountability falls back to the current phase entry_role.
  • review_only ownership means attestation, not file authorship. Those roles see "reviewing and attesting" guidance in PROMPT.md, must confirm the artifact exists and is acceptable, and must escalate if a required artifact is missing because they cannot write repo files directly.

manual adapter

The manual adapter is designed for human-in-the-loop workflows where a person reads the dispatch, does the work, and stages the result themselves.

How it works

  1. The orchestrator writes the dispatch bundle and prints instructions to the terminal.
  2. The adapter polls .agentxchain/staging/<turn_id>/turn-result.json every 2 seconds.
  3. When the file appears and is valid JSON, the adapter collects it.
  4. Default timeout: 20 minutes (1,200,000 ms). Configurable via --timeout.

Configuration

agentxchain.json (excerpt)
{
"roles": {
"pm": {
"title": "Product Manager",
"mandate": "Protect user value, scope clarity, and acceptance criteria.",
"write_authority": "review_only",
"runtime": "manual-pm"
}
},
"runtimes": {
"manual-pm": {
"type": "manual"
}
}
}

The manual adapter's poll interval (2 seconds) and timeout (20 minutes) are built-in defaults. They are not set in agentxchain.json — configure timeout via --timeout on agentxchain step.

When to use

  • Planning turns where a human PM writes the spec
  • Code review turns where a senior engineer reviews the work
  • Any turn where you want full human control over the output

local_cli adapter

The local_cli adapter spawns a local subprocess and feeds it the dispatch bundle. The default scaffold targets Claude Code with claude --print --dangerously-skip-permissions --bare and stdin prompt transport. Those extra flags are not cosmetic: unattended governed implementation turns need file-write access, and plain claude --print can stop at a permission prompt or macOS keychain auth read without ever staging a result. Any local CLI tool that can accept a prompt and produce a valid turn result works — configure it at scaffold time with --dev-command and --dev-prompt-transport instead of hand-editing JSON afterward.

How it works

  1. The orchestrator writes the dispatch bundle.
  2. The adapter spawns the configured command as a child process.
  3. The prompt is delivered via one of three prompt transport modes.
  4. The adapter waits for the subprocess to exit.
  5. On exit, if a valid staged result file exists, the adapter collects it (regardless of exit code).
  6. On timeout, the adapter sends SIGTERM, waits 10 seconds, then SIGKILL.

Prompt transport modes

ModeHow the prompt reaches the agentBest for
argv{prompt} placeholders in command/args are replaced with the full prompt textSimple tools that accept prompt as a CLI argument
stdinPrompt is piped to the process's standard inputClaude Code (--print mode), streaming tools
dispatch_bundle_onlyNo prompt delivery; the subprocess reads PROMPT.md and CONTEXT.md from diskAgents that manage their own file I/O

If prompt_transport is not set explicitly, the adapter infers the mode: if {prompt} appears in the command or args, it uses argv; otherwise it defaults to dispatch_bundle_only.

Configuration

agentxchain.json (excerpt)
{
"roles": {
"dev": {
"title": "Developer",
"mandate": "Implement approved work safely and verify behavior.",
"write_authority": "authoritative",
"runtime": "local-dev"
}
},
"runtimes": {
"local-dev": {
"type": "local_cli",
"command": ["claude", "--print", "--dangerously-skip-permissions", "--bare"],
"cwd": ".",
"prompt_transport": "stdin"
}
}
}

Timeout and signal handling

EventAction
Subprocess exits with staged result presentCollect staged result (success)
Subprocess exits without staged resultMark turn as failed with exit code
Timeout reachedSend SIGTERM to process
10 seconds after SIGTERMSend SIGKILL to process
SIGKILL sent without staged resultMark turn as failed with reason timeout

Default timeout when no turn deadline is set: 20 minutes (1,200,000 ms).

Non-default local CLI examples

The scaffold ships with Claude Code as the default, but --dev-command and --dev-prompt-transport let you target any local tool at scaffold time.

tip

These examples omit --goal and doctor for brevity. For the full governed bootstrap path, see Getting Started.

Tool that accepts prompt as a CLI argument:

agentxchain init --governed --dev-command my-agent run {prompt} -y

This scaffolds prompt_transport: "argv" and replaces {prompt} with the full prompt text at dispatch time. The generated runtime:

{
"type": "local_cli",
"command": ["my-agent run {prompt}"],
"cwd": ".",
"prompt_transport": "argv"
}

Tool that reads dispatch files directly (no prompt piping):

agentxchain init --governed --dev-command ./scripts/dev-agent.sh --dev-prompt-transport dispatch_bundle_only -y

The agent script receives no prompt on stdin or argv — it reads PROMPT.md and CONTEXT.md from the dispatch directory itself. The generated runtime:

{
"type": "local_cli",
"command": ["./scripts/dev-agent.sh"],
"cwd": ".",
"prompt_transport": "dispatch_bundle_only"
}

Tool that reads prompt from stdin (like the Claude default):

agentxchain init --governed --dev-command "claude --print --dangerously-skip-permissions --bare" --dev-prompt-transport stdin -y
{
"type": "local_cli",
"command": ["claude --print --dangerously-skip-permissions --bare"],
"cwd": ".",
"prompt_transport": "stdin"
}

If you supply --dev-command without {prompt} and without --dev-prompt-transport, init rejects the scaffold. This is intentional — ambiguous prompt delivery is worse than a clear error.

When to use

  • Dev turns with Claude Code (verified default) or any local coding CLI configured via --dev-command
  • QA turns with automated test runners
  • Any turn where a local tool can accept a prompt and produce a valid turn result

mcp adapter

The mcp adapter connects to a Model Context Protocol server and calls one explicit governed-turn tool. In the current shipped slice, it supports:

  • local stdio
  • remote streamable_http

:::info v1 scope This slice is tool-contract based. AgentXchain does not claim that any arbitrary MCP server can execute a governed turn. The runtime is valid only when the target server exposes the configured tool (default: agentxchain_turn) and that tool returns a valid AgentXchain turn result. Supported MCP transports in this slice are stdio and streamable_http. Deprecated SSE transport is not part of this contract. :::

How it works

  1. The orchestrator writes the dispatch bundle.
  2. The adapter either spawns the configured stdio server or connects to the configured streamable_http endpoint.
  3. The adapter initializes an MCP client connection and runs tools/list.
  4. It verifies that the configured tool exists.
  5. It calls that tool with governed turn metadata, dispatch file paths, and the rendered PROMPT.md and CONTEXT.md content.
  6. The tool returns the turn result either as structuredContent or JSON text in a text content block.
  7. The adapter stages that result at .agentxchain/staging/<turn_id>/turn-result.json.

This is synchronous in v1, like api_proxy. There is no separate polling phase.

:::note Streamable HTTP Interop MCP streamable_http POST requests must negotiate both JSON and event-stream responses. If you build or probe a remote MCP server outside the SDK path, send Accept: application/json, text/event-stream or expect transport-level rejection from SDK-based servers. :::

Required tool contract

The MCP server must expose one tool named by tool_name. The default is agentxchain_turn.

The adapter sends an argument object containing:

ArgumentDescription
run_idCurrent run identifier
turn_idCurrent turn identifier
roleAssigned role for this turn
phaseCurrent governed phase (default: planning, implementation, qa; custom phases are supported via routing config and advance only in declared order)
runtime_idRuntime identifier from config
project_rootAbsolute path to the project root
dispatch_dirAbsolute path to the dispatch turn directory containing ASSIGNMENT.json, PROMPT.md, CONTEXT.md
assignment_pathAbsolute path to ASSIGNMENT.json
prompt_pathAbsolute path to PROMPT.md
context_pathAbsolute path to CONTEXT.md
staging_pathAbsolute path where the adapter will write the staged result (the tool does not write this file)
promptRendered PROMPT.md content as a string
contextRendered CONTEXT.md content as a string (empty string if no context file)

The tool must return a valid turn-result object. The adapter accepts the result in three forms:

  1. structuredContent (preferred) — the turn result as a direct object in the MCP response
  2. JSON text — a text content block containing the turn-result JSON string
  3. Nested SDK wrapper — some MCP SDK versions wrap the response in a toolResult envelope; the adapter unwraps this automatically

The adapter stages the result, then the normal orchestrator validation and acceptance flow runs unchanged.

:::tip Validation leniency The adapter accepts any object that has at least one identity field (run_id or turn_id) and one lifecycle field (status, role, or runtime_id). Missing fields are handled downstream by the turn-result validator. Your tool does not need to produce every field to be staged successfully. :::

Timeout

The default timeout for MCP dispatch is 20 minutes (1,200,000 ms). If the turn has a deadline_at, the remaining time until deadline is used instead. The timeout applies to both the tools/list and tools/call requests, and resets on progress notifications from the server.

Configuration

Local stdio runtime

agentxchain.json (excerpt)
{
"roles": {
"dev": {
"title": "Developer",
"mandate": "Implement approved work safely.",
"write_authority": "authoritative",
"runtime": "mcp-dev"
}
},
"runtimes": {
"mcp-dev": {
"type": "mcp",
"command": "node",
"args": ["./examples/mcp-echo-agent/server.js"],
"tool_name": "agentxchain_turn",
"cwd": "."
}
}
}
FieldRequiredDescription
typeyesMust be "mcp"
transportnoMCP transport. Defaults to stdio. streamable_http is supported for remote endpoints
commandyes for stdioExecutable to spawn (string or string array)
argsnoArguments when command is a string (not valid with array command)
tool_namenoTool to call on the server (default: agentxchain_turn)
cwdno for stdioWorking directory for the server process (relative to project root)
urlyes for streamable_httpAbsolute HTTP or HTTPS MCP endpoint
headersno for streamable_httpStatic string request headers attached to MCP HTTP requests

Remote streamable HTTP runtime

agentxchain.json (excerpt)
{
"roles": {
"dev": {
"title": "Developer",
"mandate": "Implement approved work safely.",
"write_authority": "authoritative",
"runtime": "mcp-remote-dev"
}
},
"runtimes": {
"mcp-remote-dev": {
"type": "mcp",
"transport": "streamable_http",
"url": "http://127.0.0.1:8787/mcp",
"tool_name": "agentxchain_turn",
"headers": {
"x-agentxchain-project": "demo"
}
}
}
}

For streamable_http, the runtime stays synchronous from AgentXchain's point of view: tools/list, tools/call, stage result, validate, accept. If the server returns 405 on GET, the MCP SDK still proceeds over POST request flow. AgentXchain does not require SSE support in this slice.

Example servers

Two reference MCP servers are shipped:

ExampleTransportUse case
examples/mcp-echo-agent/stdioLocal agents on the same machine
examples/mcp-http-echo-agent/streamable_httpRemote agents, hosted services, containers

Both implement the full agentxchain_turn tool contract and return a validator-clean no-op governed turn result. The only difference is transport: stdio is spawned per-turn by the adapter; HTTP is a long-running server you start independently.

For a real governed-project wiring path showing both transports, see the governed-todo-app example. Use either echo server as a starting point for building your own governed MCP agent.

When to use

  • Local agent runtimes that already expose an MCP stdio server
  • Remote MCP servers that expose a governed-turn tool over streamable_http
  • Connector work where you want a governed turn contract over MCP instead of raw subprocess prompts
  • Tool-driven local integrations that can return structured turn output directly

api_proxy adapter

The api_proxy adapter calls an LLM API directly, handles retries, tracks token usage, and writes the staged result on behalf of the model.

:::info Write authority support The api_proxy adapter supports review_only and proposed write authority roles. Roles with authoritative write authority cannot bind to api_proxy runtimes — the adapter does not support tool use or direct repo writes.

  • review_only: The model returns a structured review verdict. The orchestrator materializes it to .agentxchain/reviews/.
  • proposed: The model returns a proposed_changes array with structured file proposals. The orchestrator materializes them to .agentxchain/proposed/<turn_id>/ for review — they are not applied to the working tree.
  • Accepted proposal artifacts are rendered into subsequent review_only dispatch context so peer reviewers can inspect the staged proposal, not just a filename list.
  • Operators manage proposals with agentxchain proposal list|diff|apply|reject. proposal apply <turn_id> compares the current workspace against the proposal's captured source snapshot before writing. If the file diverged meanwhile, apply fails closed instead of silently overwriting it. proposal apply <turn_id> --force is the explicit operator override for that conflict. proposal reject <turn_id> --reason "..." marks the proposal as rejected. Selective apply is supported with --file <path>. :::

These restrictions are operational, not academic. An api_proxy QA turn can return a structured verdict and request completion, but it cannot directly author .planning/acceptance-matrix.md or .planning/ship-verdict.md. A proposed api_proxy turn can propose code changes, but those proposals are staged for human or peer review, not applied automatically. Operators apply or reject proposals explicitly using agentxchain proposal apply or agentxchain proposal reject. If your completion gate depends on repo-local files, those files must contain real content from a writable or manual path.

How it works

  1. The orchestrator writes the dispatch bundle.
  2. The adapter assembles a prompt from PROMPT.md and CONTEXT.md.
  3. If preflight tokenization is enabled, the adapter evaluates the token budget and trims context to fit the model's context window.
  4. The adapter calls the configured LLM API with retry logic.
  5. On success, the adapter parses the model's response into a turn result and stages it.
  6. Token usage and cost are recorded in the turn metadata.

Supported providers

ProviderStatus
AnthropicSupported (Claude Opus, Sonnet, Haiku)
OpenAISupported for chat-completions-compatible review models (for example gpt-4o, gpt-4o-mini)
GoogleSupported for Gemini models via the generateContent API (for example gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash)
OllamaSupported for local models (Llama, Mistral, Gemma, Phi, etc.) via OpenAI-compatible API — no API key required

Anthropic, OpenAI, Google, and Ollama are supported. OpenAI support uses the Chat Completions endpoint with JSON output. Google support uses the Gemini generateContent endpoint with responseMimeType: "application/json". The API key is passed as a query parameter per Google's API convention. When Gemini blocks a prompt or halts with a non-STOP finish reason, AgentXchain preserves that Google-specific reason in the extraction-failure message instead of collapsing it into a generic parse error. Ollama support uses the OpenAI-compatible Chat Completions endpoint at http://localhost:11434/v1/chat/completions by default, with optional auth. Tool use, background execution, and streaming are out of scope for api_proxy.

Custom endpoint override

For supported providers, you may override the default API endpoint with an optional base_url field on the runtime:

api_proxy endpoint override
{
"type": "api_proxy",
"provider": "openai",
"model": "gpt-4o-mini",
"auth_env": "OPENAI_API_KEY",
"base_url": "http://127.0.0.1:4010/v1/chat/completions"
}

Use this for local mocks, enterprise gateways, Azure-style front doors, or self-hosted deployments that speak the same provider-family request/response contract. This is an endpoint override, not a new provider type: provider still determines request format, auth headers, and error classification, and base_url stays within existing provider families only. base_url, when present, must be an absolute http or https URL.

Error classification

The adapter classifies every error into a named error class with a retryable flag:

Error classTriggerRetryable
auth_failureHTTP 401, 403No
model_not_foundHTTP 404No
invalid_requestHTTP 400 (non-context)No
context_overflowHTTP 400 with context/token keywordsNo
rate_limitedHTTP 429 (transient)Yes
rate_limitedHTTP 429 with spend/budget limitNo
provider_overloadedHTTP 529Yes
network_failureConnection errorYes
timeoutRequest timeoutYes
response_parse_failureMalformed JSON responseYes
turn_result_extraction_failureValid JSON but no extractable turn resultYes
unknown_api_errorHTTP 500 or unclassifiedYes

Each classified error includes error_class, message, recovery instructions, retryable flag, http_status, and raw_detail for auditability.

Retry policy

Retry behavior is configured via a retry_policy object in the runtime config. The retry policy applies uniformly to all retryable error classes — there is no per-HTTP-status retry schedule.

Default policy (when retry_policy.enabled: true with no overrides):

FieldDefault
max_attempts3
base_delay_ms1000
max_delay_ms8000
backoff_multiplier2
jitter"full" (randomized delay up to computed backoff)
retry_onAll retryable error classes

Retry traces (attempts, delays, outcomes) are persisted as audit artifacts in the staging directory.

Model tier and retry budget

Not all models produce schema-conformant turn results on every attempt. Cheaper or smaller models may require governed retries for valid output. In live testing, claude-haiku-4-5-20251001 required 2–3 attempts to produce a valid governed turn result due to schema violations such as invalid proposed_next_role values and malformed artifacts_created arrays. Larger models (claude-sonnet-4-6, claude-opus-4-6) produce conformant results more reliably on the first attempt.

Budget implication: When using the api_proxy adapter with cheaper models, plan for retry overhead. A max_attempts: 3 policy means worst-case API spend is 3× the single-call cost. For claude-haiku-4-5-20251001 at ~$0.004 per governed turn, worst-case is ~$0.012. For claude-opus-4-6 at ~$0.90 per turn, worst-case is ~$2.70. Choose your model tier based on both quality requirements and retry budget tolerance.

Supported models with built-in cost tracking: claude-sonnet-4-6, claude-opus-4-6, claude-haiku-4-5-20251001, gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o3, o3-mini, o4-mini, gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash. Models not in this list still work but report $0 cost in turn telemetry. Override or add per-model cost rates via config --set:

agentxchain config --set budget.cost_rates.deepseek-v3.input_per_1m 0.27
agentxchain config --set budget.cost_rates.deepseek-v3.output_per_1m 1.10

For bulk rate tables covering many models at once, edit budget.cost_rates in agentxchain.json directly.

tip

The governed retry mechanism (rejectTurn → re-dispatch) is part of the protocol, not a workaround. It is designed to handle exactly this kind of model-level schema non-conformance. The retry policy, error classification, and audit trail work together to ensure that retry overhead is visible and auditable.

Preflight tokenization

When enabled, the adapter evaluates the token budget before making the API call. If the assembled prompt exceeds the model's context window minus a safety margin, the adapter trims context to fit.

provider_local preflight tokenization is currently Anthropic-only. OpenAI runtimes may use api_proxy, but if they enable preflight_tokenization, config validation fails closed because the repo does not yet ship an OpenAI local tokenizer.

preflight_tokenization config
{
"preflight_tokenization": {
"enabled": true,
"tokenizer": "provider_local",
"safety_margin_tokens": 2048
}
}

Preflight artifacts (effective context, token budget report) are persisted in the dispatch directory.

Token usage and cost tracking

The adapter records token usage from provider telemetry:

{
"adapter_meta": {
"model": "claude-sonnet-4-20250514",
"provider": "anthropic",
"input_tokens": 12450,
"output_tokens": 3820,
"cost_usd": 0.073,
"retries": 0
}
}

input_tokens and output_tokens are always populated when the provider returns usage telemetry. usd is populated when the model exists in AgentXchain's pinned rate table; otherwise it remains 0 instead of inventing pricing.

Configuration

agentxchain.json (excerpt)
{
"roles": {
"qa": {
"title": "QA",
"mandate": "Challenge correctness and ship readiness.",
"write_authority": "review_only",
"runtime": "api-qa-openai"
}
},
"runtimes": {
"api-qa-openai": {
"type": "api_proxy",
"provider": "openai",
"model": "gpt-4o-mini",
"auth_env": "OPENAI_API_KEY",
"max_output_tokens": 4096,
"timeout_seconds": 120,
"retry_policy": {
"enabled": true,
"max_attempts": 3,
"base_delay_ms": 1000,
"max_delay_ms": 8000,
"backoff_multiplier": 2,
"jitter": "full"
}
},
"api-qa-anthropic": {
"type": "api_proxy",
"provider": "anthropic",
"model": "claude-sonnet-4-6",
"auth_env": "ANTHROPIC_API_KEY",
"max_output_tokens": 4096,
"timeout_seconds": 120,
"preflight_tokenization": {
"enabled": true,
"tokenizer": "provider_local",
"safety_margin_tokens": 2048
}
},
"api-qa-google": {
"type": "api_proxy",
"provider": "google",
"model": "gemini-2.5-flash",
"auth_env": "GOOGLE_API_KEY",
"max_output_tokens": 4096,
"timeout_seconds": 120
},
"local-dev": {
"type": "api_proxy",
"provider": "ollama",
"model": "llama3.2",
"max_output_tokens": 4096,
"timeout_seconds": 180
}
}
}

Use an Anthropic runtime when you want local preflight tokenization. Use an OpenAI runtime when you want synchronous review-only dispatch to a chat-completions-compatible OpenAI model. Use a Google runtime for Gemini models — the adapter handles the generateContent endpoint format, API-key-as-query-parameter auth, and Gemini-specific usage telemetry automatically. Use an Ollama runtime for local models — no API key needed, no cloud dependency, and the same governed turn contract as cloud providers.

If you need to hit a mock or proxy endpoint, add base_url to the runtime block. Keep the provider field aligned with the endpoint contract you expect the adapter to speak.

remote_agent adapter

The remote_agent adapter dispatches governed turns over HTTP to an external agent service. This is the connector that proves protocol replaceability across non-local execution boundaries — the remote service receives a governed turn envelope and returns a valid turn-result JSON in a single synchronous response.

Unlike api_proxy (which speaks a specific provider's API), remote_agent posts the full AgentXchain turn envelope and expects the remote service to return a complete turn result. This makes it suitable for any external agent platform that implements the agentxchain_turn contract over HTTP.

In v1, remote_agent is an honest non-local JSON bridge, not a remote filesystem writer. It supports review_only and proposed roles. It does not support authoritative roles because the adapter has no proven local workspace mutation path.

Request envelope

The adapter POSTs a JSON body containing:

  • run_id, turn_id, role, phase, runtime_id — governed identity
  • dispatch_dir — path to the dispatch bundle on the local machine (informational)
  • prompt — rendered PROMPT.md content
  • context — rendered CONTEXT.md content

Response contract

The remote service must return HTTP 200 with Content-Type: application/json and a body that is a valid AgentXchain turn result (same schema as any other adapter's staged result).

Error handling

  • Non-2xx → dispatch failure with HTTP status in the error
  • Non-JSON response → dispatch failure
  • JSON but missing turn-result fields → dispatch failure
  • Timeout → dispatch failure with timedOut: true
  • Network error → dispatch failure

All failures mark the run as blocked with the turn retained for retry via agentxchain step --resume.

Write-authority boundary

  • review_only is supported. Accepted reviews materialize a derived artifact under .agentxchain/reviews/<turn_id>-<role>-review.md.
  • proposed is supported. Accepted proposals materialize under .agentxchain/proposed/<turn_id>/ and can be applied with agentxchain proposal apply.
  • authoritative is not supported in v1. If you need direct repo writes, use local_cli or add a future connector with an explicit workspace-bridge contract.

Security

Authorization headers are sent to the remote service but are never echoed into dispatch logs or governance artifacts. Headers matching authorization, x-api-key, cookie, and proxy-authorization are redacted to [REDACTED] in all log output.

Config

{
"runtimes": {
"remote-review": {
"type": "remote_agent",
"url": "https://agent.example.com/agentxchain/turn",
"headers": {
"authorization": "Bearer replace-with-real-token"
},
"timeout_ms": 120000
}
}
}
FieldRequiredDefaultDescription
typeYesMust be "remote_agent"
urlYesAbsolute HTTP or HTTPS URL of the remote agent service
headersNo{}String-to-string request headers
timeout_msNo120000Request timeout in milliseconds (positive integer)

Header values are sent exactly as configured. The runtime does not interpolate ${VAR} placeholders inside headers; pre-expand secrets before writing agentxchain.json.

Common validation failures

  • decisions[].id must match DEC-NNN. Dynamic IDs like DEC-BRIDGE-20260409 fail validation even if the rest of the payload is correct.
  • review_only roles must raise at least one objection. A QA review with an empty objections array is rejected by the protocol challenge requirement.
  • proposed remote turns must include proposed_changes[]. A summary-only dev response is not enough for acceptance or proposal apply.

Comparison with other adapters

remote_agent is not the same as:

  • api_proxy — speaks Anthropic/OpenAI/Google/Ollama provider APIs; remote_agent speaks the AgentXchain turn-result contract directly
  • mcp — uses the MCP tool protocol over stdio or streamable_http; remote_agent uses plain HTTP POST/response
  • local_cli — spawns a local subprocess; remote_agent calls a remote service

Use remote_agent when you have an external agent service that can accept governed turn envelopes and return structured turn results over HTTP.

Implementing a new adapter

AgentXchain adapters stay deliberately thin. If you are adding a runtime, implement the transport boundary and stop there.

Minimal checklist:

  • Implement dispatch: consume the governed turn envelope and deliver it to the runtime.
  • Implement wait: block until the runtime finishes, times out, or fails definitively.
  • Implement collect: return a valid staged turn result or a transport-level failure.
  • Read the dispatch bundle from .agentxchain/dispatch/turns/<turn_id>/.
  • Write the staged result to .agentxchain/staging/<turn_id>/turn-result.json or return the equivalent JSON to the orchestrator surface that will materialize it there.
  • Do not write orchestrator state, mutate routing, or bypass validation. Adapters transport work; the orchestrator owns governance.

If you need the full connector contract, examples, or packaging guidance, continue with Build Your Own Connector.

Comparison table

Featuremanuallocal_cliapi_proxymcpremote_agent
AutomationNone — human does all workFull — subprocess runs autonomouslyFull — API call runs autonomouslyFull — MCP tool call runs autonomouslyFull — HTTP call runs autonomously
LatencyMinutes (human speed)Seconds to minutesSecondsSeconds to minutesSeconds to minutes
CostHuman timeTool license + computeAPI tokensTool/runtime dependentRemote service dependent
Prompt transportN/A (human reads files)argv, stdin, or dispatch_bundle_onlyHTTP request bodyMCP stdio tools/list + tools/callHTTP POST with turn envelope
Retry logicN/AExit code basedConfigurable exponential backoff with jitterNone in v1None in v1 — retry via step --resume
Timeout handlingConfigurable poll timeoutSIGTERM → 10s grace → SIGKILLHTTP timeout + retry policyRequest timeout via MCP client callConfigurable timeout_ms (default 120s)
Cost trackingN/AN/AAutomatic token tracking; USD when the model has a pinned rate tableN/AN/A — remote service reports cost in turn result
Write authorityAnyAnyreview_only or proposed onlyAnyreview_only or proposed only
Best forPM review, human QA, approvalsClaude Code (default), or any local CLI via --dev-commandDirect Anthropic, OpenAI, Google Gemini, or Ollama (local) review callsLocal MCP-based agent runtimes with an explicit governed-turn toolExternal agent services, hosted agent platforms, cross-network governed execution