Agent-session adapters
This page is for an AI agent or maintainer diagnosing live session behavior. Do not infer a capability from an adapter name or from a hook that happens to exist. Start with the compiled, fixture-backed truth:
ovld agent-session capabilities <agent> --jsonovld doctorThe four capability states are operational instructions: supported is fixture-proven,
unsupported means do not attempt it, not-implemented is buildable work, and unverified
means establish evidence before changing behavior. Source descriptors live at
connectors/adapters/<agent>/harness-capabilities.yaml; generated CAPABILITIES.md files must
not be edited.
Shared diagnostic order
Section titled “Shared diagnostic order”- Confirm
OVERLORD_SESSION_CHANNEL_IDexists in the launched process. Without it, the hook must exit before spawningovld; silence is correct. As of the Latch v2 cutover, neitherovld launchnor a runner-dispatched launch currently callscreateSessionChannel, so expect this variable to be absent for every session today — that is the current state of launch wiring, not evidence of a broken adapter. Do not spend time debugging a specific connector on this signal alone. - Run
ovld doctorand repair stale managed files withovld agent-setup <agent>. - Read the adapter’s static capability maximum, then verify the installed descriptor and live binding separately. The catalog alone does not prove that the installed harness invoked a hook or that project policy enabled an optional path.
- Identify the path: callback script, in-process extension, or control-plane sidecar.
- Keep mission lifecycle hooks (
ovld protocol) separate from live session hooks (ovld agent-session). The former can work while the latter is intentionally unbound. - For a decision failure, verify exact native stdout and timeout before investigating REST. A malformed or timed-out callback must emit nothing.
Claude Code
Section titled “Claude Code”Shape: callback plugin. Observe: UserPromptSubmit, PostToolUse, SessionStart.
Decide (not-implemented — target shape): PermissionRequest. Inject
(not-implemented — target shape): asyncRewake during a turn plus a Stop boundary.
All decide.* and inject.* capabilities are not-implemented for Claude today
per connectors/adapters/claude/CAPABILITIES.md; the wire shapes below describe
the design to build against, not live behavior. Run
ovld agent-session capabilities claude --json to confirm current status before
relying on either path.
UserPromptSubmitrecords follow-up activity.PostToolUsesends its native payload to localcapture-change; only an objective-bound event that the Claude codec normalizes asfile.editedcan recorddeclared_edit/directpath evidence.- The decision response is Claude’s
hookSpecificOutputshape. Cursor’s flatpermissionresponse is never valid here. asyncRewakereports Delivered only after Claude’s documented stderr plus exit-2 signal is emitted. The Stop path can block once to continue delivery.- If a callback fires twice, inspect Claude plugin registrations and Cursor compatibility configuration; Cursor can load Claude hooks.
- Fork/subagent native identity remains a declared hazard. Do not bind a child session by cwd or assume it shares the parent’s native id.
- Two named hazards from
connectors/adapters/claude/CAPABILITIES.mdexplain thenot-implementedstatuses above rather than just restating them: the shippedPermissionRequesthook script (scripts/permission-hook.sh) calls aovld protocol permission-requestsubcommand that does not exist, backgrounds the call with& disown, and is gated on a mission-id environment variable a launched session does not always set — it is structurally incapable of returning a decision, and it is not even registered inhooks/hooks.json(onlyUserPromptSubmit,PostToolUse, andStopare). The shippedStophook similarly callsovld protocol hook-event --hook-type Stop, which the CLI rejects, and parses adeliveryStatusfield nothing produces.
Shape: callback plugin using documented hooks. Observe: prompt, pre/post tool, lifecycle.
Decide (not-implemented — target shape): PermissionRequest. Inject
(not-implemented — target shape): Stop-boundary delivery.
All decide.* and inject.turnBoundary are not-implemented for Codex today
per connectors/adapters/codex/CAPABILITIES.md; treat the details below as the
design to build against, not live behavior.
- Codex hooks require a supported hook-era version. An installed plugin on an older binary can look healthy while never invoking hooks.
PostToolUsesends its native payload to localcapture-change; only an objective-bound event that the Codex codec normalizes asfile.editedrecords path evidence. Shell and no-path callbacks record unavailable health without scanning the worktree.- The response is nested
hookSpecificOutput.decision.behavior;askdeliberately emits no bytes so Codex owns the native prompt. - Do not start an app-server decision subscriber beside the hook adapter. That creates two authorities capable of resolving one permission.
- The plugin root fallback is
~/.codex/plugins/overlord. If managed scripts exist elsewhere, refresh withovld agent-setup codexrather than editing generated paths. - Stop injection is turn-boundary behavior, not mid-turn steering.
Cursor
Section titled “Cursor”Shape: callback scripts. Observe: postToolUse tool results and file edits. Decide
(not-implemented — target shape): per-tool shell, MCP, and generic pre-tool events.
Inject (supported): stop returning followup_message.
Per connectors/adapters/cursor/CAPABILITIES.md, decide.shell/decide.mcp/
decide.anyTool are not-implemented and decide.universal is unsupported —
none of the Decide paths below are live yet. inject.turnBoundary and
inject.nextTurn are fixture-proven and accurate as described.
postToolUseis the file-evidence path. The Cursor codec admits exact Write/Edit paths, leaves known read/search/fetch callbacks silent, and records unavailable health without a path for shell, generic, unmapped, or mutation-capable no-path callbacks.- Cursor has no universal
PermissionRequestevent. Never restore the old manifest claim; usebeforeShellExecution,beforeMCPExecution, orpreToolUseaccording to the descriptor. - Native output is flat:
{ "permission": "allow|deny|ask" }. Claude’s nested response may be accepted only by an optional compatibility shim and is not Overlord’s contract. - Keep
failClosedunset. Otherwise a missing CLI, timeout, or backend loss becomes a denial instead of Cursor’s native fallback. - Cursor can read Claude hook configuration. Inspect both namespaces when commands duplicate or a Claude-shaped response appears.
followup_messageis recorded as Queued (turn boundary), never Delivered. Cursor’s command allowlist may also take precedence over a hook response; an emitted allow is not proof it was decisive.
Shape: in-process TypeScript extension. Observe: input, tool call, agent start.
Decide (not-implemented — target shape): optional async tool_call interceptor.
Inject (not-implemented — target shape): sendUserMessage with confirmation.
decide.shell and every inject.* capability are not-implemented for Pi today
per connectors/adapters/pi/CAPABILITIES.md; the details below describe the
design to build against, not live behavior.
- Pi has no native permission dialog. A timeout, CLI failure, or transport failure lets the tool
run. Remote decisions require both
OVERLORD_PI_REMOTE_DECISIONS=enabledandOVERLORD_PI_PROJECT_REMOTE_DECISIONS=enabled. - Current builds do not expose those two gates as workspace/project settings. Treat the decision interceptor as a development path unless the launcher deliberately supplies both; observation and injection do not require them.
- The extension uses an owner-only temporary payload file because
pi.exechas no stdin API. It must remove the file infinally; payload values must never move into argv. - Delivered is confirmed only after
sendUserMessagesucceeds and the input lease is confirmed. - The extension API is version-coupled. Treat an unsupported Pi version as a disabled extension, not permission to guess at event shapes.
OpenCode
Section titled “OpenCode”Shape: supervised control-plane sidecar. There are no callback scripts.
- A supervised launch must start
ovld agent-session sidecar --agent opencode --port <port>, bind OpenCode to loopback, and protect the control port with a per-launch password. The bundled agent catalog does not yet start this sidecar automatically. Never expose the port to the LAN or browser. - On reconnect, the sidecar re-reads
/permissionand/questionbefore resubscribing to/event. Replays are expected and deduplicate by producer event id. - The TUI and Overlord can truly answer concurrently on the event bus. A TUI win must mark the request resolved elsewhere; do not apply callback-only waiter assumptions.
- Injection uses
POST /session/{id}/prompt_async. OpenCode’s standingalwayspermission is intentionally hidden from remote controls.
Antigravity
Section titled “Antigravity”Shape: callback compatibility plugin, currently fixture-proven only at tier 0.
- Mission lifecycle capture may work while agent-session observe/decide/inject remains unverified. Do not promote UI controls based on the agent name or resemblance to Gemini.
PreToolUsecurrently returns allow for legacy telemetry and is not a remote decision path.- Native hook names are harness-specific. Record a native fixture before changing mappings or claiming post-tool or stop behavior.
- Upgrade work belongs in a separate verification objective with executable fixtures.
Development verification
Section titled “Development verification”yarn connectors:capabilities:checkyarn test:coreyarn test:cliyarn test:backendThe capability check executes recorded payload, script I/O, source guard, normalized-event, and decision-codec fixtures. A callback/extension decision claim requires a decision-codec fixture that pins its redacted card and exact allow/deny/defer bytes.
Related: Architecture · The agent protocol · Live agent sessions
