t3-code-android-nightly/docs/orchestration-v2/provider-capability-system.md
Julius Marminge de34391427
feat(orchestrator): introduce new orchestrator (#2829)
Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Vitalii Yehorov <vitalyiegorov@gmail.com>
Co-authored-by: Jake Leventhal <jakeleventhal@me.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Alex Southwell <saphid@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nicholas Wasmiller <derped@mineperial.com>
Co-authored-by: PB <poilmb@gmail.com>
Co-authored-by: Exotic <118054752+extoci@users.noreply.github.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: Gabriel De Andrade <30420087+gabrielelpidio@users.noreply.github.com>
Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com>
Co-authored-by: scratchyone <scratchywon@gmail.com>
Co-authored-by: Dominic Roy <dominic@sdko.org>
Co-authored-by: chukfinley <chuk@chuk.dev>
Co-authored-by: Primož Ajdišek <bigpod@bigpod.si>
Co-authored-by: benthecarman <benthecarman@live.com>
Co-authored-by: NaveDanan <nave0712@gmail.com>
Co-authored-by: aaditagrawal <103925638+aaditagrawal@users.noreply.github.com>
Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com>
Co-authored-by: Nick Anisimov <n.anisimov.23@gmail.com>
Co-authored-by: MacKinley Smith <smithmackinley@gmail.com>
Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: AKolenda <akole779@mtroyal.ca>
Co-authored-by: Guillermo Casanova <75276669+Gigioxx@users.noreply.github.com>
Co-authored-by: Otavio Salvador <otavio@ossystems.com.br>
Co-authored-by: Shirish Pothi <183252392+shirishpothi@users.noreply.github.com>
Co-authored-by: Ishaan Kothari <ishaanko.mail@gmail.com>
Co-authored-by: Bob Fowler <bob@rjf.ca>
Co-authored-by: Anton Bezdenezhnykh <gamer392@yandex.ru>
Co-authored-by: ValeraZSD <48602572+ValeraZSD@users.noreply.github.com>
Co-authored-by: Ephraim <ephraim39hr14m@gmail.com>
Co-authored-by: Ryan Ilano <ryanilano@users.noreply.github.com>
Co-authored-by: Alex <me@pixp.cc>
Co-authored-by: maco <gosarmarcel7@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Tristan Knight <admin@snappeh.com>
Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: oliver <97427849+flamboh@users.noreply.github.com>
Co-authored-by: kamkm <99585688+Kamkmgamer@users.noreply.github.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
2026-10-02 12:22:22 -07:00

9.3 KiB

Provider Capability System

V2 must not assume every provider can do what Codex can do. Provider behavior should be expressed through capabilities and policies, not provider-name conditionals spread across orchestration.

Capability Shape

type ProviderCapabilities = {
  sessions: SessionCapabilities;
  threads: ThreadCapabilities;
  turns: TurnCapabilities;
  streaming: StreamingCapabilities;
  tools: ToolCapabilities;
  approvals: ApprovalCapabilities;
  planning: PlanningCapabilities;
  subagents: SubagentCapabilities;
  context: ContextCapabilities;
  checkpointing: CheckpointCapabilities;
  identity: IdentityCapabilities;
};

Capabilities should be versioned and emitted by each adapter at session start.

Session Capabilities

type SessionCapabilities = {
  supportsMultipleProviderThreadsPerSession: boolean;
  supportsModelSwitchInSession: boolean;
  supportsProviderSwitchingViaHandoff: boolean;
  supportsRuntimeModeSwitchInSession: boolean;
  pendingRequestsSurviveRestart: boolean;
};

Policy examples:

  • If model switching is unsupported, start a new provider session.
  • If pending requests do not survive restart, mark old requests not_resumable.

Provider-thread resumption is not a capability. It is a required adapter primitive. Adapters must be able to start from a stored provider cursor/session/thread handle or return a runtime resume failure.

Thread Capabilities

type ThreadCapabilities = {
  canCreateEmptyThread: boolean;
  canReadThreadSnapshot: boolean;
  canRollbackThread: boolean;
  canForkThread: boolean;
  canForkFromTurn: boolean;
  canForkFromSubagentThread: boolean;
  exposesNativeThreadId: boolean;
};

Codex can expose strong thread ids and rollback snapshots. Claude may support native forking through its own model/session primitives. Other providers may only support synthetic app forks.

Turn Capabilities

type TurnCapabilities = {
  exposesNativeTurnId: boolean;
  emitsTurnStarted: boolean;
  emitsTurnCompleted: boolean;
  supportsInterrupt: boolean;
  supportsActiveSteering: boolean;
  supportsSteeringByInterruptRestart: boolean;
  supportsQueuedMessages: boolean;
  terminalStatusQuality: "strong" | "weak" | "none";
};

If terminalStatusQuality is weak, V2 should use adapter policy to infer terminal state, but still mark correlation strength accordingly.

supportsActiveSteering means the provider can modify an in-flight turn directly. supportsSteeringByInterruptRestart means V2 can implement app-level steering by interrupting the active turn and starting a replacement attempt. Most providers should support the latter if they support interruption and normal follow-up turns.

Streaming Capabilities

type StreamingCapabilities = {
  streamsAssistantText: boolean;
  streamsReasoning: boolean;
  streamsToolOutput: boolean;
  streamsPlanText: boolean;
  emitsMessageCompleted: boolean;
};

If message completion is not emitted, the normalizer closes assistant messages at root run terminal.

Tool Capabilities

type ToolCapabilities = {
  exposesToolItemIds: boolean;
  emitsToolStarted: boolean;
  emitsToolCompleted: boolean;
  emitsToolOutput: boolean;
  supportsMcpTools: boolean;
  supportsDynamicToolCallbacks: boolean;
};

Weak providers may produce only textual tool summaries. The adapter can still create ordered turnItems and execution nodes by scoped ordinal.

Approval Capabilities

type ApprovalCapabilities = {
  supportsCommandApproval: boolean;
  supportsFileReadApproval: boolean;
  supportsFileChangeApproval: boolean;
  supportsApplyPatchApproval: boolean;
  approvalsHaveNativeRequestIds: boolean;
  approvalCallbacksAreLiveOnly: boolean;
  approvalsCanOriginateFromSubagents: boolean;
};

The UI should show pending approvals from any execution node, including subagents, if they are respondable.

Planning Capabilities

type PlanningCapabilities = {
  emitsPlanUpdated: boolean;
  emitsTodoList: boolean;
  emitsProposedPlan: boolean;
  supportsStructuredQuestions: boolean;
  planDeltasHaveItemIds: boolean;
};

Mapping policy:

  • emitsTodoList: update live run progress.
  • emitsProposedPlan: create accept/implementable plan artifacts.
  • supportsStructuredQuestions: create respondable user-input requests.

If a provider only emits plain assistant text, the app should not pretend it has structured plans.

Subagent Capabilities

type SubagentCapabilities = {
  supportsSubagents: boolean;
  exposesSubagentThreadIds: boolean;
  emitsSubagentLifecycle: boolean;
  canWaitForSubagents: boolean;
  canCloseSubagents: boolean;
  canForkSubagentThread: boolean;
};

When exposesSubagentThreadIds is true, subagent provider threads become addressable ProviderThread records. When false, subagents can still be shown as nested execution nodes if the provider exposes enough lifecycle information.

Context Handoff Capabilities

Context handoff controls provider switching and provider-thread reconstruction.

type ContextCapabilities = {
  acceptsSystemContext: boolean;
  acceptsDeveloperContext: boolean;
  acceptsSyntheticUserContext: boolean;
  canGenerateSummaries: boolean;
  canConsumeHandoffSummaries: boolean;
  supportsDeltaHandoff: boolean;
  supportsFullThreadHandoff: boolean;
  maxRecommendedHandoffChars: number | null;
};

Policy examples:

  • If supportsDeltaHandoff is available, return to a previous provider thread with a summary of only off-provider runs.
  • If delta handoff is unsupported or low quality, create a new provider thread with a full thread summary.
  • If the provider cannot accept explicit context, provider switching should require synthetic user context or be marked unsupported.
  • If no provider can generate summaries, use the app's configured summarization provider or a local summarizer capability.

Checkpoint Capabilities

Checkpointing is primarily app-owned, but provider conversation rollback is provider-dependent.

type CheckpointCapabilities = {
  appCanCheckpointFilesystem: boolean;
  supportsNestedCheckpointScopes: boolean;
  providerCanRollbackConversation: boolean;
  providerRollbackReturnsSnapshot: boolean;
  providerCanReadConversationSnapshot: boolean;
};

If provider rollback is unsupported, filesystem rollback can still happen, but provider conversation state must be restarted or marked divergent. Nested checkpoint scopes are app-owned; providers only affect whether their nested provider conversations can be rolled back to match restored filesystem state.

Identity Capabilities

type IdentityCapabilities = {
  nativeThreadIds: "strong" | "weak" | "none";
  nativeTurnIds: "strong" | "weak" | "none";
  nativeItemIds: "strong" | "weak" | "none";
  nativeRequestIds: "strong" | "weak" | "none";
};

This controls how the normalizer correlates events:

  • strong: use native id as scoped provider ref.
  • weak: use native id plus ordinal/fingerprint.
  • none: allocate by scoped ordinal.

Degradation Policies

Every feature should declare what happens when capability is missing.

Examples:

interrupt unsupported
  -> stop session if allowed, otherwise mark unsupported

active steering unsupported
  -> interrupt active turn and restart the run as a steering replacement attempt

fork unsupported
  -> synthetic fork from app projection if policy allows

rollback unsupported
  -> restore filesystem checkpoint, restart provider context, mark provider state divergent

nested checkpoint unsupported
  -> capture only root-run checkpoint and record child filesystem activity as uncheckpointed

provider switch return unsupported
  -> create fresh provider thread with full app-thread summary

structured approvals unsupported
  -> provider runs under configured sandbox policy; no approval UI

plan_updated unsupported
  -> no live todo UI; rely on assistant messages

The UI should not hide unsupported behavior behind provider-specific errors. It should receive typed capability results.

Adapter Contract

Each provider adapter should expose:

type ProviderAdapter = {
  getCapabilities(): ProviderCapabilities;
  startSession(input): ProviderSession;
  ensureProviderThread(input): ProviderThread;
  sendRun(input): ProviderTurnStartResult;
  steerRun?(input): void;
  interrupt(input): void;
  respondToRequest(input): void;
  readThreadSnapshot?(input): ProviderThreadSnapshot;
  rollbackThread?(input): ProviderThreadSnapshot | void;
  forkThread?(input): ProviderThread;
  streamEvents(): Stream<ProviderAdapterEvent>;
};

Optional methods are guarded by capabilities. The orchestration layer should not call optional methods without checking capability or going through a policy wrapper.

Raw provider frames should be logged by the provider transport/runtime as bounded diagnostics. Adapter streams should expose normalized provider events that the V2 normalizer can turn into app orchestration events.

Capability-Driven UI

The UI should receive capability-informed affordances:

  • show or hide fork action by fork source.
  • show interrupt as available, destructive fallback, or unavailable.
  • show approvals as respondable or expired.
  • show plan/todo panels only when structured plan artifacts exist.
  • show rollback only when app checkpoint exists, and annotate provider rollback support.

This keeps the product predictable across Codex, Claude, Cursor, OpenCode, and future providers.