Single source of truth for what each agent provider supports through the
bridge (#387). The Python module bridge/core/provider_capabilities.py is
authoritative; this file is rendered from it and
bridge/tests/test_provider_capabilities.py fails CI when they drift apart.
| State | Meaning |
|---|---|
supported |
Implemented, exercised by tests, and (where noted) verified live. |
degraded |
Partially implemented or missing live evidence; the reason names the gap. |
unsupported |
Not implemented for this provider yet. |
unknown |
Cannot be judged until the named dependency lands. |
Non-supported states carry a machine-readable reason and, where one
exists, the tracking issue dependency.
The behavioral runtime axes are executable: bridge/tests/test_runtime_conformance.py
runs the shared AgentRuntime conformance suite against every adapter
(currently CodexRuntime over a scripted fake app-server, PiriRuntime
over a scripted fake RPC process, plus the normative in-memory reference
runtime) with no live provider calls, and a
negative harness proves that contract-violating runtimes fail the suite.
New provider adapters (#354 successors) must pass this suite and add a
column here before landing.
| Capability | claude | codex | crush | piri |
|---|---|---|---|---|
runtime_adapter — Provider-neutral runtime adapter: Sessions and turns run behind the AgentRuntime seam and pass the runtime conformance suite. |
supported — ClaudeRuntime adapts the Claude Agent SDK to AgentRuntime, passes the runtime conformance suite, and is the only Claude path since the #584 slice C-2 cutover removed the legacy direct SDK path and its kill-switch flag. |
supported — CodexRuntime adapts the app-server protocol to AgentRuntime and passes the runtime conformance suite. |
supported — CrushRuntime adapts the crush server HTTP+SSE API to AgentRuntime and passes the runtime conformance suite over a scripted fake transport. |
supported — PiriRuntime adapts Piri's headless JSONL RPC protocol to AgentRuntime and passes the runtime conformance suite. |
session_resume — Session resume: Re-attach to an existing provider session by its stable id. |
supported — SDK sessions resume by persisted session_id (CCC_RESUME_PERSISTED_SESSIONS) while the transcript exists. |
supported — thread/resume re-attaches by thread id and rejects a mismatched returned thread. |
supported — Sessions resume by id after the adapter verifies existence through the workspace session read; the requested id is preserved. |
supported — --session-id re-attaches by exact Piri session id and the adapter rejects a mismatched sessionId returned by get_state. |
text_streaming — Streaming answer text: User-visible answer text arrives as incremental deltas within a turn. |
supported — SDK stream deltas drive Telegram draft updates when CCC_TELEGRAM_STREAMING is enabled. |
supported — item/agentMessage/delta notifications normalize to TextDeltaEvent. |
supported — SSE message/updated text-part growth is diffed into TextDeltaEvent increments. |
supported — RPC message_update text_delta events normalize to TextDeltaEvent. |
reasoning_stream — Normalized reasoning stream: Provider reasoning is normalized as private ReasoningDeltaEvent and never delivered to the user. |
supported — On the default adapter path, SDK ThinkingBlock content normalizes to ReasoningDeltaEvent (#599) and the consume loop keeps it private. |
supported — item/reasoning textDelta and summaryTextDelta normalize to ReasoningDeltaEvent and stay private. |
supported — reasoning parts normalize to private ReasoningDeltaEvent increments and are never user-delivered. |
supported — RPC message_update thinking_delta events normalize to ReasoningDeltaEvent and stay private. |
message_boundaries — Intra-turn message boundaries: Distinct assistant messages inside one turn are delimited so interim answers can deliver before tool work continues. |
supported — Each SDK AssistantMessage frame is a message boundary in the reader path. |
supported — item/completed for agentMessage items normalizes to MessageCompletedEvent. |
supported — A new assistant message id after emitted text normalizes to MessageCompletedEvent, so multi-message turns delimit cleanly. |
supported — Assistant message_end events normalize to MessageCompletedEvent before the terminal agent_settled sequence. |
tool_event_stream — Tool lifecycle events: Tool execution start and completion surface as normalized paired events. |
supported — SDK tool_use/tool_result blocks drive tool status in the reader path. |
supported — item/started and item/completed normalize to ToolStartedEvent/ToolCompletedEvent pairs. |
supported — tool_call and tool_result parts normalize to ToolStartedEvent/ToolCompletedEvent pairs keyed by tool_call_id. |
supported — tool_execution_start/tool_execution_end normalize to paired ToolStartedEvent/ToolCompletedEvent values. |
interactive_approvals — Interactive approvals: Privileged actions pause for an explicit allow/deny decision; an omitted or failing handler is fail-closed deny. |
supported — SDK permission callbacks gate tool use through the provider-neutral bounded/redacted Telegram snapshot. One-shot tokens bind request and exact-display fingerprints; changed arguments, expiry, terminal/replaced generations, and late replies deny. Body-free asked/terminal decisions use the strict owner-only approval ledger. |
supported — Approval server requests normalize to the same snapshot, fingerprint binding, exact-once decision audit, and fail-closed generation/expiry checks; a missing or failing handler denies. |
supported — permission_request envelopes route through the fail-closed approval chain and resolve via permissions/grant; an omitted, failing, or late handler denies. |
unsupported — Deliberately unrestricted: built-in tools execute directly under the bridge OS account and optional extension confirm dialogs are auto-approved. |
turn_interrupt — Turn interrupt: In-flight work can be cancelled; interrupting an idle session is a safe no-op. |
supported — /stop cancels the active task and interrupts the SDK client. |
supported — turn/interrupt targets the exact active turn id; interrupted turns end with error code 'interrupted'. |
supported — interrupt posts the agent session cancel; the canceled finish reason normalizes to ErrorEvent(interrupted). |
supported — RPC abort interrupts the active turn, drains through agent_settled, and idle interrupt is a safe no-op. |
turn_serialization — Per-session turn serialization: Concurrent sends on one conversation execute strictly one turn at a time. |
supported — The conversation FIFO serializes requests until a terminal result releases the lock. |
supported — A per-thread turn lock serializes send_turn calls on the same thread. |
supported — A per-session asyncio lock serializes turns on one crush session. |
supported — A per-session turn lock serializes prompts on the persistent RPC process. |
session_browsing — Stored-session browsing: Stored sessions can be listed and read back in bounded, normalized form. |
supported — /resume lists and previews SDK transcripts from the projects directory. |
supported — SessionBrowser is implemented over thread/list and thread/read with bounded output. |
supported — Bounded list/read over the workspace sessions and messages endpoints. |
unsupported — Piri RPC 0.83 has exact-id resume but no bounded stored-session list/read API; /resume therefore accepts an explicit Piri session id only. |
model_discovery — Model discovery: Selectable models are enumerated from the provider at runtime. |
degraded — The Claude /model list is a static curated set in the bridge, not enumerated from the provider. |
supported — model/list responses normalize to ModelInfo including reasoning-effort metadata. |
supported — Models enumerate from the workspace providers endpoint, which reflects the crush-configured providers and models. |
supported — get_available_models responses normalize to ModelInfo including thinking-level metadata. |
usage_metering — Usage metering: Account/session usage normalizes into provider-tagged UsageSnapshot values. |
supported — Rate-limit events and result usage parse via parse_claude_rate_limit_event and parse_claude_result; provider budgets use the body-free autonomous-mode ledger only, while interactive usage remains recorded and ungated. |
supported — Account rate limits, account usage, and thread token usage parse and merge into UsageSnapshot; provider budgets use the body-free autonomous-mode ledger only, while interactive usage remains recorded and ungated. |
degraded — Session-level prompt/completion tokens and cost parse from the workspace session read; account windows and UsageRecorder plumbing are not wired yet. (depends on #926) |
degraded — The body-free ledger records Piri request attempts, but RPC 0.83 does not expose normalized token totals, account quota, or reset windows. |
terminal_stall_release — Terminal-event stall release: A vanished completion event releases the conversation after a bounded grace with exactly-once buffered answer delivery. |
supported — The Claude reader applies CCC_TERMINAL_STALL_SECONDS with exactly-once buffered delivery, while body-free delegated-task lifecycle events defer that short guard and remain bounded by CCC_DELEGATED_TASK_STALL_SECONDS (#411, #874). |
supported — The provider-neutral consumer shares the same stall guard and closes abandoned iterators (#411). |
supported — The provider-neutral consumer shares the same stall guard and closes abandoned iterators (#411). |
supported — The provider-neutral consumer shares the bounded stall guard and the adapter aborts and drains abandoned RPC turns. |
async_completion_delivery — Out-of-turn completion delivery: Assistant output that finishes outside the bridge's exact active turn has a documented bounded delivery or inactive-session recovery boundary. |
supported — Live SDK unsolicited output uses the optional session handler, while terminal Claude transcript notifications use bounded provider-specific dead-session recovery. Its replay remains provider-specific and explicitly at-least-once. |
degraded — Only turn/completed for the exact bridge-owned active turn is delivered. There is still no detached ownership signal or negotiated protocol version, so otherwise-valid unowned completions are never auto-delivered: they are journaled body-free (route-bound only under a declared durable-delivery capability, reclaimed body-free on the next user turn), with no thread/read replay and no Claude transcript inference. Runtime reports a machine-readable degraded boundary and supports_async_completion_delivery=false. (depends on #646) |
degraded — Only exact-active-turn completions are handled; crush exposes no detached-ownership signal or negotiated protocol version, so unowned completions are not delivered. (depends on #926) |
unsupported — Piri RPC output is owned only while the bridge's exact prompt is active; there is no detached completion ownership or replay contract. |
external_wait — External CI wait + resume: An agent promise to continue after external CI is backed by a durable, exact-head wait registry with a journaled terminal-to-conversation wake. |
supported — Provider-neutral: the agent-side CLI binds registrations through the bridge-published file-backed active-turn route; the bridge monitor polls GitHub checks with exact-head pinning, journals terminal transitions before the wake, and resumes through a bridge-owned external_event turn (#740). This route is independent of in-memory approval-generation leases. |
supported — Provider-neutral: same registry, monitor, and continuation contract as the Claude path (#740); it remains independent of approval-generation leases. |
supported — Provider-neutral: same registry, monitor, and continuation contract as the claude path (#740). |
supported — Provider-neutral: the same active-turn route, exact-head registry, monitor, journal, and external_event continuation contract apply. |
lifecycle_observability — Lifecycle observability: Explicitly enabled, allowlisted lifecycle metadata normalizes into one body-free observation contract for bounded audit and evidence surfacing. Compaction checkpoint/reinjection is tracked separately under memory_postcompact_reinject. |
supported — With CCC_LIFECYCLE_AUDIT opt-in, installed hooks and live AgentEvents normalize allowlisted prompt/tool/notification/turn/session metadata into body-free records. The ledger is bounded, owner-only, and fail-open; evidence warnings may enqueue optional body-free owner spool records. Final Telegram delivery is outside this capability. |
supported — With CCC_LIFECYCLE_AUDIT opt-in, allowlisted active-turn tool/turn/approval events normalize into body-free records; unsupported notification methods are ignored and account telemetry remains separate. The ledger is bounded, owner-only, and fail-open; evidence warnings may enqueue optional body-free owner spool records. Final Telegram delivery and compaction semantics are outside this capability. |
supported — With CCC_LIFECYCLE_AUDIT opt-in, crush SSE envelopes normalize via normalize_crush_event into the same body-free records; the ledger is bounded, owner-only, and fail-open. Final Telegram delivery is outside this capability. |
supported — With CCC_LIFECYCLE_AUDIT opt-in, normalized Piri prompt/tool/turn/session events enter the same bounded, owner-only, body-free, fail-open observation ledger. Final Telegram delivery is outside this capability. |
| Capability | claude | codex | crush | piri |
|---|---|---|---|---|
memory_session_resume — Memory: session resume: Conversation context resumes from the provider thread/session id. |
supported — Persisted SDK session ids resume with full provider-side context. |
supported — Thread ids persist per conversation and resume through thread/resume; live cold resume verified 2026-07-15. |
supported — Sessions resume by id with provider-side context; the crush server persists session history in its data dir. |
supported — Piri session ids persist per Telegram conversation and resume through --session-id with exact get_state verification. |
memory_read_bootstrap — Memory: read bootstrap: The MEMORY/USER/local/Wiki/resume startup snapshot is recognized at session start. |
supported — SessionStart injects the bounded local snapshot via claude/hooks/load-memory.sh. |
supported — The AGENTS.override.md materializer runs before thread start/resume; promoted after the 2026-07-15 live gate (#419). |
unsupported — No startup-snapshot materializer is wired for crush sessions. (depends on #926) |
supported — Audience-scoped Piri sessions use isolated transcript directories, disable automatic AGENTS/CLAUDE discovery, run the bounded ccc materializer, and append only the generated scope-local AGENTS.md context. |
memory_postcompact_reinject — Memory: post-compaction reinjection: Instruction/memory meaning survives compaction and cold resume. |
supported — PostCompact re-injects the bounded snapshot through claude/hooks/load-memory.sh. |
degraded — Cold resume re-reads the refreshed global snapshot, but the app-server exposes no official PreCompact/PostCompact event and turn/completed is not treated as compaction. Provider compaction checkpoint/reinjection therefore remains unverified. |
degraded — crush exposes no compaction lifecycle event to hook. (depends on #926) |
supported — Capable Piri builds (RPC protocolVersion 1, set_append_system_prompt) inject the scoped snapshot over RPC at session start and re-inject a refreshed snapshot on compaction_end, idempotent per (sessionId, compactionEntryId); legacy builds keep the cold-start CLI append with no mid-session refresh. |
memory_writeback_distill — Memory: write-back distill: Durable facts are extracted from provider threads for the memory sinks. |
supported — Session-reset, explicit, checkpoint, and shutdown triggers enter the same provider-neutral journal and strict v1 contract. Managed bridge sessions suppress the legacy hook distiller to prevent duplicate writes. |
supported — Session-reset, explicit, opt-in bounded checkpoint, and bounded shutdown-queue triggers, extraction, and the local sink are scheduled. Provider/model/turn-byte/duration accounting and shared warn/enforce cost gates are body-free; Wiki candidates enter a local human-review queue. The extractor factory defaults to the main Codex runtime and supports explicit override. |
unsupported — No distill snapshotter is wired for crush sessions. (depends on #926) |
supported — Session-reset, explicit, checkpoint, and shutdown triggers enter the provider-neutral journal. A secure bounded Piri JSONL snapshot preserves source provider provenance through the isolated tool-free Piri extractor and all sinks; provider-default follows the node's Kimi/GLM configuration. |
memory_sink_local — Memory: local sink: resume/local structured facts are written replay-safe with provenance. |
supported — Distill writes resume.md and local structured facts (claude/hooks/distill/resume-write.sh, local-facts.sh). |
supported — Supported session-reset triggers bind an opaque audience route, and an independently leased worker writes replay-safe local facts/resume. |
unsupported — Local memory sink is not wired for crush. (depends on #926) |
supported — Audience-routed Piri jobs use the same independently leased replay-safe local facts/resume sink while retaining provider=piri provenance. |
memory_sink_wiki_candidate — Memory: Wiki candidate sink: Only human-gated Wiki candidates are generated; nothing auto-merges. |
supported — Distill emits human-gated Wiki candidates via claude/hooks/distill/wiki-queue.sh. |
supported — Validated candidates are atomically queued in owner-only per-job records; the sink performs no Wiki write, branch, PR, or merge. |
unsupported — Wiki-candidate sink is not wired for crush. (depends on #926) |
supported — Validated Piri candidates enter the same owner-only human-review queue; the sink still performs no Wiki write, branch, PR, or merge. |
memory_roundtrip — Memory: read/write round-trip: A durable fact written in session A is recalled by an isolated later session B. |
supported — SessionEnd write-back feeds the next SessionStart snapshot; both hook directions carry executable tests (memory-hooks.test.sh, distill/*.test.sh). |
supported — The hermetic audience-scoped test and an approved isolated live provider A→distill→local index→B run on 2026-07-23 both recalled one durable fact exactly once; local, Wiki-candidate, and Honcho sink states remain independently replayable (#465). |
unsupported — Depends on the distill/writeback chain, which crush does not wire yet. (depends on #926) |
supported — The hermetic audience-scoped Piri A→snapshot→distill→local index→B bootstrap test recalls one durable fact with provider=piri provenance. |