docs/COORDINATOR-ADAPTERS.md

Coordinator adapters

The coordinator owns separate execution sessions. It does not attach to arbitrary existing chats, change the research assistant's permissions, or send gameplay commands. Agent registration/inspection is distinct from task approval. Use GitCafe epic #14 and adapter workstreams #16–17 for follow-up.

Installed contracts

RouteLibrary / protocolAuthenticationVerified implementation
CodexExisting local CLI, tested sandbox version 0.163.0; app-server JSON-RPCExisting local Codex login store; no credential copyingSeparate owned thread/turn, streaming, operator input, file/test approval, confined Bun tests, interruption, exact-turn reconciliation
ACP@mastra/acp 0.4.3 AcpAgent.connectionSelected executable's existing account; no automatic loginExplicit permission handler, session identity, streaming, refusal choices, cancel notification, owned process lifecycle
A2AExplicit 0.3 or 1.0 JSON-RPC bindingServer-only credential reference scoped to exact execution originCard inspection, nonblocking submit or SSE, early task receipt, GetTask, same-task input, CancelTask, unknown after unresolvable receipt
TincanExisting installed CLI via fixed argvExplicit local Tincan profileExact peer/profile send, owned request receipt and result reconciliation; cancellation/input unsupported

Mastra dependencies are pinned in the application lockfile: core 1.79.0, ACP 0.4.3, client-js 1.56.0, LibSQL 1.27.0. A2A uses a direct lifecycle transport because the installed core A2AAgent API does not expose public task cancellation. Sending has no retry loop.

Local launch profiles

ACP_PRESETS provides OpenCode's opencode acp argument shape and the standalone claude-agent-acp executable shape. These are suggestions, not proof that either executable is installed, logged in, or has certified filesystem confinement. Generic profiles use an absolute executable plus an argv array. The UI must not describe a detected claude executable as an ACP endpoint without an actual compatible adapter.

ACP's documented default chooses the first permission option. Our adapter always supplies onPermissionRequest. Unknown/uncertified tool permissions resolve to the provider's reject choice, never the first allow choice. Client filesystem and terminal capabilities are disabled. External ACP executables may still own tools outside that protocol boundary; they remain research-only until an OS-enforced profile has separate negative-test evidence. The library internally merges its parent environment; our adapter explicitly masks unrelated server credentials before spawning. Do not mark arbitrary third-party executables as trusted merely because they speak ACP.

Scoped Codex editing

Coding uses rs_workspace with list, read, write, and conditionally run_tests, plus native readOnly sandboxing and networkAccess:false. Native shell, unified execution, multi-agent, skills search, images, web, MCP and plugins are disabled for execution threads. The existing research thread is untouched.

Every write becomes a persisted operator request. The response is bound to the attempt, provider thread and turn. A denied change is returned as denied so the model may propose an alternative. Paths must remain in the approved canonical root. Traversal, absolute paths, symlinks, multiply linked files, credential files and runtime metadata are rejected. Reads/writes are bounded at 128 KiB. No arbitrary shell, package manager, network client or background worker exists in this tool surface. Test execution is a separate app-mediated capability described below.

run_tests is available only after device-specific OS sandbox proof. It is never inferred from an edit-only certificate. test-runner.ts uses fixed /usr/bin/sandbox-exec + Bun argv, explicit workspace-relative test files, a deny-default macOS Seatbelt policy, read-only workspace access, private scratch writes, no network, and no process forks. Credential paths, Git config, package-manager credentials and install destinations are denied. Tests requiring subprocesses, network access, workspace writes or dependencies outside the approved root will fail within this boundary. There is no unconfined fallback. The installed Codex schema does not support newer restricted-read-root fields shown in online docs; the separate test sandbox enforces its own actual read boundaries.

certifyCodexConfinement(connection, evidenceDirectory, signal) runs operator-triggered, deterministic scratch probes without a model turn, login, external agent or game session. It verifies sibling/traversal/symlink denial, blocked unsupported install/background operations, root overlap, a real provider-sandbox outside write attempt, and blocked localhost network access against a working unsandboxed baseline. Evidence is local and excludes raw stderr or credentials. A matching certificate binds the Codex executable SHA-256 and lasts two hours; inspection itself lasts ten minutes. Its optional test-runner certificate separately binds the Bun runtime and sandbox-exec SHA-256 values. The service keeps certificates outside model-controlled inputs; it enables read/edit capabilities and adds run_tests only when all actual test-runner probes pass.

The first local run passed on 2026-10-26 using the existing Codex 0.163.0 binary. Evidence was saved under data/coordinator/confinement/. This is sandbox verification, not a completed live model task or remote-agent pilot. The subsequent test-runner proof executed actual scratch Bun tests on macOS26.5.1, verified ten positive/negative cases and independently confirmed cancellation/process exit. Protocol-fixture integration also executed real confined tests after approval and confirmed Stop awaited their exit. Before claiming full provider acceptance, complete a selected live-model scratch edit/test pilot and certify an actual ACP coding runtime. ACP and network peer code enforcement remain gaps.

Network transport

network-policy.ts validates URL components, exact origins, every DNS answer and private HTTP permissions. It pins the actual socket lookup to a validated address, preserving HTTPS host verification. Redirects are rejected. Link-local, metadata, multicast, mapped IPv4 and transitional address forms cannot bypass private-host checks. Approved private/tailnet HTTP must resolve only to private addresses. Responses have byte/time bounds.

Pass a mutable PeerNetworkPolicy to createCoordinatorAdapters/createAdapters; the service populates it only from explicit operator registrations. Default policy denies every origin. Card execution endpoints must match the registered execution origin. Credential resolution receives both reference and origin; the service must check the exact binding before supplying headers. Credentials never enter receipts. Artifact URLs are source data, never automatically fetched; any future fetch must use the same transport policy.

A2A 0.3 uses message/send, message/stream, tasks/get, tasks/cancel; 1.0 uses SendMessage, SendStreamingMessage, GetTask, CancelTask. SSE receipt events are persisted before later progress. Closed streams/AbortSignal are not remote cancellation. A missing task stays unknown; nothing is silently resent. Tincan also routes reconciliation through the receipt's original peer/profile, never whichever profile is currently selected.

Verification and limits

Run bun test server/coordinator/adapters. On macOS this includes real OS-sandbox negative tests and actual test-process cancellation; non-macOS hosts skip device-specific tests and cannot advertise this runner. Protocol fixtures cover both A2A versions (including real localhost SSE), no-resubmit recovery, pinned DNS, redirects, metadata denial, immutable Tincan receipts, Codex exact-session approvals, denied writes, native escalation refusal, restart reconciliation, genuine interruption acknowledgement and ACP explicit denial/cancellation uncertainty. Mock provider sessions prove client behavior only; they do not prove remote accounts, remote enforcement or live provider execution.

Cancellation returns requested, confirmed, unsupported, or unknown. Only a provider terminal cancelled/interrupted state is confirmation. ACP's cancel notification does not acknowledge cessation; Tincan has no verified cancellation method in the installed contract. The scheduler owns the 30-second confirmation window and later reconciliation.

Reference docs checked through Context.dev: ACP, Codex app-server, A2A specification. Local generated Codex JSON schemas and installed Mastra types were checked when current website docs exceeded installed CLI capabilities.

Confined test limits and lifecycle

Every test request identifies 1–16 explicit .test/.spec files and produces a separate operator approval bound to run_tests. Approval starts the process asynchronously so UI input requests do not wait for the test duration. Bun uses a fixed five-second per-test timeout; the whole process is bounded by two minutes and the remaining task deadline. Combined stdout/stderr is capped at 64 KiB. Overflow, timeout or cancellation kills the owned process group and waits for exit; no process can fork a surviving child under the verified policy. Result artifacts distinguish passed, failed, cancelled, timed_out and output_limit. Provider interruption is requested only after the test process has exited.