Skip to content

feat(providers): add Pi coding agent - #7211

Open
StiensWout wants to merge 30 commits into
pingdotgg:t3code/codex-turn-mappingfrom
StiensWout:t3code/pi-provider
Open

feat(providers): add Pi coding agent#7211
StiensWout wants to merge 30 commits into
pingdotgg:t3code/codex-turn-mappingfrom
StiensWout:t3code/pi-provider

Conversation

@StiensWout

@StiensWout StiensWout commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

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:

  • T3 speaks Pi's stdio JSONL RPC protocol through the user's own installation and discovers models, thinking levels, commands, skills, and auth from Pi itself.
  • The thinking picker asks Pi for its effective configured level, badges that real choice as Default, and keeps inheritance internally so T3 does not override Pi.
  • T3 threads use Pi's native session files for resume, current-branch fork, rollback, steering, and session naming.
  • Extension dialogs, notifications, status rows, MCP tools, and resumable subagents project into the existing T3 lifecycle surfaces.
  • The launcher replaces only Pi's conflicting official subagent extension, deduplicates explicit extension arguments, and restores trusted user and project extensions.
  • Pi's session statistics feed the shared composer context meter. The ring tracks the active thread's context window while the popover exposes cumulative tokens processed across that Pi session.
  • Turn settlement, retry and compaction recovery, child cancellation, process teardown, and fork rollback paths fail closed without leaking or corrupting the active native session.

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

  • 35 focused Pi tests, reduced from 48 by removing duplicate helper and branch matrices while strengthening observable lifecycle coverage.
  • Targeted contracts, server, and web typechecks.
  • Targeted changed-file lint and formatting.
  • Both generated Pi extensions parsed independently as TypeScript.
  • Live validation against Pi 0.84.2, including provider discovery, multi-turn context usage, MCP, extension UI, native subagents, steering, rollback, and provider handoff.
  • Mobile parity audited for the shared Codex and Claude surfaces; Pi-specific mobile behavior is intentionally limited to provider identity and model selection.

Proof

Provider discovery

Pi provider discovery

End-to-end Pi turn

Pi end-to-end 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.

Pi context meter persistence

Configured thinking default

Pi's effective setting appears as the labeled default while the internal selection continues to inherit from Pi.

Pi configured thinking default

Native resumable subagent

Pi resumable subagent

Native steering

Pi steering

Checkpoint and Pi session-tree rollback

Pi rollback

Stacked on #2829 (t3code/codex-turn-mapping) until that branch lands.

Built by GPT-5.6 Sol in T3 Code through Codex.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 561e4289-385d-455d-bdde-05ea7f17a38d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 16, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts
Comment thread apps/server/src/provider/Drivers/PiDriver.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/web/src/components/chat/providerIconUtils.ts
Comment thread apps/server/src/provider/Layers/PiProvider.ts Outdated
Comment thread apps/server/src/textGeneration/PiTextGeneration.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/provider/Layers/PiProvider.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/web/src/components/Icons.tsx Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiRpc.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts Outdated
Comment thread apps/server/src/orchestration-v2/UserFacingErrors.ts
Comment thread apps/server/src/orchestration-v2/Adapters/piT3SubagentExtensionSource.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/piT3McpInjection.ts Outdated
Comment thread apps/server/src/orchestration-v2/Adapters/PiAdapterV2.ts
StiensWout and others added 26 commits August 17, 2026 17:14
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>
@StiensWout
StiensWout force-pushed the t3code/pi-provider branch 2 times, most recently from a91b8e2 to be48afa Compare August 17, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants