SNA

Runtimes

Runtime adapters, AgentProvider internals, and runtime-specific behavior.

Each runtime is implemented as a class behind a common AgentProvider interface. Adapters live in core/providers/{claude-code,codex,opencode,grok,cursor}.ts. ACP-speaking runtimes (Grok Build, Cursor) share core/providers/acp/base.ts.

Supported runtimes

SNA is designed to let a product choose the runtime that fits the workflow instead of baking in one CLI forever. The table below is a product-level overview; for exact feature support, see the Feature × runtime matrix.

RuntimeBest fitStrengthsTradeoffs
Claude CodeReliable local coding sessions with Anthropic models and rich permission behavior.Mature CLI behavior, good tool-use UX, detailed token/cost metadata, strong streaming for thinking and tool input deltas.Stateless per call/session, so there is no daemon pooling. Permission approval is hook-based rather than bidirectional over SNA.
CodexLow-latency product integrations that need many sessions, one-shot jobs, or frequent runtime reuse.RuntimePool reuses a daemon, supports cwd changes per thread, emits rich item lifecycle events and shell output deltas.Codex-specific configuration has its own native shape; tool input deltas are not exposed like Claude Code's stream-json path.
OpenCodeApps that already use OpenCode or want an HTTP daemon with OpenCode model/provider configuration.Pooled daemon, SDK-backed HTTP path, good fit for repeated prompts in one cwd.Daemon is single-cwd, some runtime mutations require respawn, and event detail depends on what OpenCode's stream exposes.
Grok BuildACP-based sessions with Grok Build and a public protocol surface.Shares the SNA ACP adapter, has bidirectional permission requests, straightforward stateless session lifecycle.No runtime pooling, model/cwd changes require respawn, and metadata is thinner than Claude/Codex for token and tool detail.
CursorUsers who already live in Cursor and want SNA to drive the Cursor subscription-backed headless CLI.Uses cursor-agent with existing Cursor login, ACP permissions, Cursor model selection, and model-id reasoning variants.No daemon pooling, model/cwd changes require respawn, and setup depends on the user's Cursor CLI authentication state.

The rest of this page describes the adapter internals behind that product-facing choice.

AgentProvider

interface AgentProvider {
  name: string;
  isAvailable(): Promise<boolean>;
  spawn(opts: SpawnOptions): AgentProcess;
  complete(opts: CompleteOptions): Promise<CompletionResult>;
  // ...
}

interface AgentProcess {
  send(message: string, opts?): void;
  interrupt(): void;
  setModel(model: string): void;
  setPermissionMode(mode: PermissionMode): void;
  applyPatch(patch: SessionPatch): PatchResult;
  respondToPermission(approved: boolean): void;
  kill(): void;
  on(event: string, handler): void;  // emits AgentEvent
}

applyPatch() is the per-field dispatch hook used by PATCH /agent/session. Each runtime adapter decides which fields it can apply in-place vs which require a respawn-with-history-replay.

Runtime strategy

RuntimeTransportStatePool?
claude-codeclaude -p (one-shot) plus claude --resume (replay)stateless per callno
codexcodex app-server daemon, JSON-RPC over stdiopersistent daemon, multi-threadyes
opencodeopencode serve daemon plus SDK HTTPpersistent daemon, single-cwdyes
grokgrok agent stdio (Agent Client Protocol over stdio — Grok Build CLI)stateless per sessionno
cursorcursor-agent acp (Agent Client Protocol over stdio — Cursor CLI)stateless per sessionno

The Codex and OpenCode runtime adapters share daemons through RuntimePool. When a session opens, the pool is queried by (provider, cwd, config) and an existing daemon is reused if compatible, sparing the cold-start cost (≈2s for Codex, ≈3s for OpenCode).

Grok Build was the first SNA runtime whose wire protocol is a public standard (ACP). With Cursor added as the second ACP-speaking runtime, the shared logic now lives in core/providers/acp/base.ts — a JSON-RPC pump, the initialize/session/new handshake, the session/updateAgentEvent translation, bidirectional session/request_permission (with bypassPermissions auto-approve), and the cross-runtime resource-block history injection. Each subclass keeps only what genuinely differs: CLI path resolution, spawn args, optional auth step (Cursor's authenticate({methodId:"cursor_login"})), vendor extension notification prefixes (_x.ai/* for Grok, cursor/* for Cursor), and runtime-specific tool-call unwraps. Cross-runtime resume serialises SNA's canonical history into a single ACP resource block on the first session/prompt — see Cross-runtime history replay on the feature matrix page.

Cursor uses the user's existing cursor-agent login (macOS Keychain entries cursor-access-token / cursor-refresh-token) — and therefore the Cursor subscription. No CURSOR_API_KEY is required; SNA passes the spawn's environment through unchanged, and the child process picks up the same credentials the user would use interactively. Reasoning level is encoded into the model id itself (gpt-5.3-codexgpt-5.3-codex-high for level 4); applyCursorReasoning in reasoning-level.ts does that rewrite, and models outside the effort family (composer-*, auto, etc.) are left unchanged.

RuntimePool

  • prepare(opts, provider): eagerly warm up a daemon for a known cwd.
  • findExisting(spawnOpts): strict match for session spawns (config must align).
  • findCompatible(provider, cwd): loose match for one-shot completion() / runOnce() (ignores MCP, hooks, permission mode).
  • shutdown(handle): graceful teardown when refcount hits zero.

Claude Code does not pool. Its CLI is stateless, so per-call spawns are competitive with daemon-resume for typical session granularity.

For why SNA keeps high-fidelity native runtime adapters instead of treating ACP as the only runtime layer, see SNA and ACP.

Cross-runtime knobs

Two latency knobs flow through every runtime:

  • reasoningLevel: 0..5: runtime-agnostic, lightest to heaviest. Translates to Claude Code's --effort, Codex's model_reasoning_effort, Grok Build's --effort, and Cursor's model-id suffix (e.g. gpt-5.3-codex-high) via reasoning-level.ts. OpenCode currently ignores this field.
  • providerOptions.serviceTier: Codex-only. Mirrors Codex's /fast slash command (values: "priority", "flex", "batch"). Intentionally not auto-mapped to Claude Code because Claude Code's /fast is a different MODEL variant with separate billing.

For a per-feature compatibility view across all runtimes, see Feature × runtime matrix.

On this page