feat(providers): add Pi coding agent - #7211
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Effect service conventions review of the new Pi provider code. Two error-modeling issues found; everything else (namespace subpath imports, Effect.catchTags usage, layer/driver construction acquiring ChildProcessSpawner/FileSystem/IdAllocatorV2/ServerConfig from the environment, Schema.TaggedErrorClass failures) follows the conventions.
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
One consistency finding on the new web provider icon wiring. Everything else in the web scope (providerDriverMeta.ts, ProviderModelsSection.tsx, contextWindow.ts, session-logic.ts, AddProviderInstanceDialog.tsx) is registry/data-only and matches the existing per-driver patterns.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
One finding on the Pi icon geometry after the tile removal. Everything else in the web scope (provider icon map, driver meta, model placeholder, display-name and picker option entries) follows the existing per-provider patterns.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Effect service conventions: one finding — a raw child-process stderr payload is copied into a log annotation. Elsewhere in this repo process output is reported as lengths only (stdoutLength/stderrLength, lineLength), with explicit tests asserting stderr is not retained in diagnostics.
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
Two log annotations in the new Pi adapter copy Pi's raw wire error text into the observability stream. Everything else in the Pi files (error attributes, cause handling, driver/layer shape, dependency acquisition) matches the repo's adapter conventions.
Posted via Macroscope — Effect Service Conventions
7c18cde to
f8c8600
Compare
A prompt that only runs an extension slash command never starts an agent run, so pi emits no agent_settled and the turn spun forever. The deferred id-less prompt ack is the completion signal for that shape: on ack with no agent activity observed, probe get_state and settle the turn when Pi reports idle. The probe result re-enters the event queue so the check stays ordered behind any agent activity Pi emitted first. Also carries concurrent review-fix work from the shared worktree: additional PiRpc request-lifecycle hardening beyond the settle probe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every tool lifecycle event passed the current time as `startedAt`, so each update rewrote the start timestamp and a completed tool always rendered with zero duration. The turn now remembers the first time it saw each `toolCallId` and reuses it for later updates and completion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The command-only settle probe dropped a failed `get_state` on the floor. Pi emits no agent events for a pure extension command, so the turn stayed active and the session rejected every later turn until restart. The probe now queues a `probeFailed` record instead of swallowing the error, so the same ordered handler settles the turn. The existing no-agent-activity guard still keeps a genuinely running turn open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`ProviderDriverError.message` is derived from `detail`, so interpolating
`cause.message` duplicated the underlying failure into the wrapper message
while the cause already carried it. The sibling mapping in this same file
("Failed to build Pi orchestration adapter.") already used the bounded
form, so the two wrappers now match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Turn boundaries now capture pi session-tree refs: each provider turn's nativeTurnRef becomes its first user entry id and the thread's nativeConversationHeadRef tracks the tree leaf, giving rollback a durable target. rollbackThread re-roots the active branch with pi's fork command, so reverting a checkpoint also rewinds the provider conversation instead of marking it divergent. Thread titles sync into pi's session name so T3 threads stay identifiable in pi's own /resume listing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`switch_session` reset the applied model and thinking caches but left `appliedSessionName`, so a newly selected session whose thread title matched the previous one skipped `set_session_name` and kept the old name in pi's `/resume` listing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two review findings on the session-tree work: - When `get_entries` failed, `lastKnownLeaf` was left pointing at an entry Pi may have already advanced past. The next successful capture then used that stale `since` cursor, so its window spanned several turns and the first user entry it found belonged to an earlier one, aiming rollback too far back. A failed capture now marks the cursor stale; the next capture re-syncs it and skips the turn-start ref for that one turn instead. - Log only the length of pi's stderr. The chunk is unbounded remote output and can carry credentials or prompt text, matching the `stderrLength` form used elsewhere in the server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…elined `leafCursorStale` was only cleared inside `captureTurnTreeRefs`, but `registerThread` and `rollbackThread` also re-baseline `lastKnownLeaf` from a full `get_entries`. After one failed capture the flag therefore stuck, so later turns kept skipping their `nativeTurnRef` and rollback across them failed with no captured session-tree entry. Both re-baseline sites now clear the flag, and only leave it set when the listing itself failed. An empty tree is a success with no leafId. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CommandPolicy.ensureRollback rejects every rollback command unless providerRollbackReturnsSnapshot accompanies the rollback capabilities, so checkpoint reverts on Pi threads failed with 'rollback must return a providerInstanceId thread snapshot'. rollbackThread already returns the updated provider thread; declare it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The official pi subagent extension delegates work to separate pi processes through a 'subagent' tool and streams per-task results in its tool details. Project each delegated task into V2's native subagent surface — live progress, model, per-task success/failure, and final output — so extension-driven subagents get real subagent cards instead of an opaque tool row. Any other tool named subagent without that shape is ignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on the subagent surface: - A non-zero subagent `exitCode` now counts as a failure regardless of whether the parent tool has ended. Gating it on `completed` let a finished child report "completed" while `piSubagentOutput` returned its stderr as the result, and left exit-code-only failures "running" until the parent tool finished. - Fall back to `stderr` with `||` rather than `??`, so a failure carrying an empty `errorMessage` no longer hides a non-empty `stderr`. - Log only the length of pi's extension-error and rejected-steer payloads. Both are unbounded remote output that can carry prompt text or credentials, matching the `stderrLength` form used in `PiRpc.ts`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stop aborts pi's in-flight tools, which pi reports as tool_execution_end with isError. Mapping every error end to failed painted a red failed command card under a correctly interrupted run. Error ends on an interrupted turn now close as interrupted, and aborted subagent tasks follow the same rule, matching how OpenCode presents the same path. Reported from the provider manual live-test pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five spec-fidelity fixes against pi's rpc.md: - a session_before_switch extension veto now fails the resume instead of silently adopting whichever session stayed active - failed compactions surface as a failed compaction item with the error instead of disappearing - empty input/editor dialog answers deliver as values (the spec's 'extension receives ""'), no longer converted to cancels - editor dialogs show their prefill inside the question text so the user is not editing blind - text generation passes --no-extensions so an unanswerable extension dialog cannot stall commit-message generation until its timeout Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Six review findings from the protocol-gap round: - A recovered auto-retry no longer terminalizes as failed. Pi emits the erroring `message_end` before retrying, and `auto_retry_end` success left that failure in place, so `agent_settled` failed the whole turn. - A failed `prompt` send finalizes the turn again. The fire-and-forget change moved the send off `request`, dropping the cleanup, so a send failure left `activeTurn` set and wedged every later turn. - `startTurn` now holds the session permit, so a new turn cannot start while the previous `finalizeTurn` is still awaiting `get_entries`. That race let the old finalizer capture the new turn's entry and publish idle after the new turn had already published active. - `switch_session` clears the model and thinking baselines too, so "Pi default"/"inherit" cannot replay the previous session's defaults. - Termination re-checks liveness before SIGTERM and before escalating to SIGKILL. Signalling a pid that already exited can hit an unrelated process once the OS recycles the pid or pgid. - Windows tears down the whole tree with `taskkill /T`, matching `AcpSessionRuntime`. `process.kill` reached only pi itself and left extension subprocesses holding inherited stdio handles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Capability rejections surfaced their internal diagnostic string, so a
checkpoint revert on a provider without rollback support showed 'pi
cannot satisfy rollback_snapshot for command <uuid>: rollback must
return a providerInstanceId thread snapshot'. userFacingDispatchErrorMessage
now renders known policy rejections as provider-named prose ('Pi did not
report its rewound conversation state, so the checkpoint was not
restored.') and keeps the diagnostic form for logs and unknown codes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pi core has no MCP client. V2 already minted a scoped t3-code bearer before openSession. This writes a T3-owned extension into the server cache and spawns pi --mode rpc --extension <that file> with T3_MCP_URL and T3_MCP_BEARER_TOKEN. Each MCP tool is registered under its original name. User launchArgs are preserved. The first turn receives the shared orchestration instructions.
Official subagent uses --no-session, so T3 could project cards but could not open the child. Inject a T3-owned override that persists --session, reports sessionFile, and binds each result as a child thread. Follow-up sends allocate a new RPC and resume with switch_session. Duplicate subagent registrations abort Pi, so spawn disables extension discovery and drops the official tool from launchArgs.
Pi discovery used one High-capped picker for every reasoning model. get_available_thinking_levels is session-scoped, so catalog discovery now reads each get_available_models thinkingLevelMap instead. Extra High and Max appear only when the map has a non-null entry.
…3 tools The T3 subagent override spawns pi with --no-extensions, which silently cost users every other extension they had installed. Re-discover the user's extensions (agent dir, plus project .pi/extensions only under standing trust) and re-add them with explicit --extension flags. Child subagent spawns now also attach the T3 MCP extension so t3_thread_* tools survive the nested spawn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes the remaining coverage gaps against pi's RPC surface: - forkThread clones pi's session tree and switches the live process back to the source, so T3-side thread forks map to native clones. - readThreadSnapshot reconstructs the conversation from get_entries, giving handoff and preview surfaces a real transcript. - setStatus/setWidget extension calls project as keyed live work-log rows that update in place and close on settle. - Session-start extension dialogs (e.g. project trust) are buffered and attached to the next turn instead of being cancelled unseen. - Stop-with-restart aborts the turn and terminates the pi process, and transport death during an interrupt reads as interrupted, not failed. - A recovered auto-compaction clears the stashed model error so the turn completes instead of reporting the pre-compaction failure. - Opening a subagent's child thread while its task is still running is refused with a clear message instead of corrupting the child session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prepending T3 orchestration instructions to the first user message meant the message no longer started with "/", so pi never expanded slash commands (extension commands, prompt templates, skills) on a thread's first turn. Found live: /t3-demo reached the model as plain text. The T3 MCP extension now delivers the same instructions through pi's real system-prompt channel (a before_agent_start hook), and the adapter sends the user's text untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a91b8e2 to
be48afa
Compare
be48afa to
4a6d708
Compare
Problem
Pi users depend on their existing models, auth, extensions, skills, context files, and native session history. T3 Code did not have a Pi provider, and a shallow CLI wrapper would lose the customization and session behavior that make Pi useful.
Solution
This adds Pi as an Early Access provider on Orchestrator V2:
subagentextension, deduplicates explicit extension arguments, and restores trusted user and project extensions.The MCP bridge, resumable subagent work, and per-model thinking support originated in @mwolson's stacked contribution and retain that authorship.
Related work
Closes #397.
Closes #402.
Addresses #6685.
This supersedes the Pi implementations in #2211, #2748, #2800, #2812, #2831, #2856, #3818, #3947, #4355, #4445, #5688, #5882, and #6319.
This incorporates StiensWout#34 by @mwolson.
Validation
Proof
Provider discovery
End-to-end Pi turn
Context meter persists across turns
The active context stays at 21k/272k while Pi's cumulative session total rises from 90k to 111k after the next completed turn.
Configured thinking default
Pi's effective setting appears as the labeled default while the internal selection continues to inherit from Pi.
Native resumable subagent
Native steering
Checkpoint and Pi session-tree rollback
Stacked on #2829 (
t3code/codex-turn-mapping) until that branch lands.Built by GPT-5.6 Sol in T3 Code through Codex.