Skip to content

feat: pi (pi.dev) as an officially supported session backend - #132

Merged
saucam merged 1 commit into
mainfrom
feat/pi-provider
Jul 9, 2026
Merged

feat: pi (pi.dev) as an officially supported session backend#132
saucam merged 1 commit into
mainfrom
feat/pi-provider

Conversation

@saucam

@saucam saucam commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

What

pi becomes codeoid's second first-class backend, riding entirely on the provider extension surface from #131. One codeoid session = one warm pi --mode rpc subprocess (strict-LF JSONL); pi keeps its own durable session tree and codeoid resumes it across daemon restarts via switch_session. User docs: docs/providers-pi.md.

The provider (src/daemon/providers/pi/)

  • rpc.ts — subprocess + LF-only framing + request correlation (pi explicitly warns against Unicode-separator line splitting), stderr-tail diagnostics, ENOENT and crash surfaced through one failure channel
  • translate.ts — pure pi-event → ProviderEvent mapping (unit-tested): streaming text/thinking deltas, assistant commits (tool-only rounds skipped), tool completion, retries, extension errors, compaction notices
  • index.ts — the SessionProvider: lazy spawn per workdir, model switching (provider/model-id values reported through the per-provider catalog), per-turn usage as a delta of pi's cumulative session stats, now/next → pi steering and later → follow-up, rotation via new_session, listCommands/listModels feeding session.commands and models.list

The approval bridge (bridge.ts)

pi ships no built-in permission system — gating is delegated to extensions by design. codeoid injects its own pi extension (-e, regenerated per session) that hooks pi's tool_call and routes every tool through canUseTool, so modes, autonomous budgets, session.approve, audit logging, and patchableKeys input patches all apply to pi sessions identically to Claude ones. Safety posture:

  • bridge announces readiness on session start; if it never does, turns fail closed ("tools would run ungated") instead of silently running unreviewed
  • defense-in-depth: any tool execution that somehow bypassed the gate is flagged loudly in the transcript

pi extensions work end-to-end

Hooks run inside pi unchanged. Extension dialogs (ctx.ui.select/confirm/input/editor) route through TurnOpts.requestUserInputsession.ui_request (answered from any attached client); ctx.ui.notify becomes an info/system row; extension/prompt/skill slash commands surface in the / palette via session.commands.

User exposure

  • session.create gains providerIdfail-closed: an unknown id is invalid_request, never a silent claude fallback (resume keeps the warn-and-fall-back path)
  • auth.ok advertises the daemon's registered providers (default first)
  • the web new-session modal grows a backend picker (hidden on legacy daemons)
  • config: providers.pi.{enabled, command}

Tests

All offline — a protocol-faithful fake-pi fixture (spawned through a wrapper script) exercises the full pipeline: text turns, approval allow/deny including the input-patch round-trip proven from pi's side, extension dialogs, notifications, catalogs, second-turn usage deltas, ungated-tool detection, bridge fail-closed, and missing-binary errors. Plus pure translation units, registry catalog/disable tests, manager fail-closed + providerIds() ordering, schema fidelity, and web modal picker tests. Suites: 1170 daemon/protocol/core tests + 130 web tests green; tsc, biome, eslint clean.

Follow-ups (called out in docs)

  • pi subprocess env hardening parity with the Claude subprocess
  • provider picker in the TUIs (codeoid-ui, in-repo Ink) — web-first here
  • pi ctx.ui.custom() TUI components don't cross RPC (pi degrades them itself)

🤖 Generated with Claude Code

One codeoid session = one warm `pi --mode rpc` subprocess (strict-LF
JSONL). pi's own session file is the backing id — daemon restarts resume
the same pi conversation via switch_session.

- PiProvider (SessionProvider): prompt/steer/follow_up mapping, streaming
  text+thinking translation, per-turn usage deltas from session stats,
  model switching (provider/model-id), rotation via new_session, model +
  command catalogs (get_available_models / get_commands)
- Bridge extension: pi has NO native permission system, so codeoid
  injects a pi extension (-e) that routes every pi tool_call through
  canUseTool — modes, budgets, session.approve, audit, and patchableKeys
  input patches all apply. Missing bridge = turns fail CLOSED; ungated
  executions are flagged loudly
- pi extension dialogs (select/confirm/input/editor) route through
  requestUserInput → session.ui_request; notify becomes transcript rows
- User exposure: session.create.providerId (fail-closed on unknown ids),
  auth.ok advertises registered providers (default first), web
  new-session modal grows a backend picker; config providers.pi.{enabled,command}
- Tests run offline against a protocol-faithful fake-pi fixture: text
  turns, approval allow/deny + input patching, dialogs, notifications,
  catalogs, usage deltas, ungated-tool detection, bridge fail-closed,
  missing-binary errors; plus pure translation units and manager/schema
  coverage. docs/providers-pi.md documents setup + limitations
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@saucam, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e3665ae6-19bd-49db-ad38-7b7f4a86730b

📥 Commits

Reviewing files that changed from the base of the PR and between 1e9c1fc and 67b373b.

📒 Files selected for processing (21)
  • CHANGELOG.md
  • docs/providers-pi.md
  • packages/protocol/src/schemas.test.ts
  • packages/protocol/src/schemas.ts
  • packages/protocol/src/types.ts
  • src/config.ts
  • src/daemon/providers/pi/bridge.ts
  • src/daemon/providers/pi/index.ts
  • src/daemon/providers/pi/rpc.ts
  • src/daemon/providers/pi/translate.ts
  • src/daemon/providers/registry.ts
  • src/daemon/server.ts
  • src/daemon/session-manager.ts
  • src/daemon/session.ts
  • src/tests/fixtures/fake-pi.ts
  • src/tests/pi-translate.test.ts
  • src/tests/provider-pi.test.ts
  • src/tests/provider-registry.test.ts
  • src/tests/session-manager-extension-verbs.test.ts
  • web/src/components/NewSessionModal.test.tsx
  • web/src/components/NewSessionModal.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pi-provider

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.

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.75911% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.60%. Comparing base (1e9c1fc) to head (67b373b).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/daemon/providers/pi/index.ts 90.00% 38 Missing ⚠️
src/daemon/providers/pi/translate.ts 88.42% 11 Missing ⚠️
src/daemon/providers/pi/rpc.ts 96.90% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #132      +/-   ##
==========================================
+ Coverage   80.15%   80.60%   +0.44%     
==========================================
  Files          90       94       +4     
  Lines       15587    16215     +628     
==========================================
+ Hits        12494    13070     +576     
- Misses       3093     3145      +52     
Flag Coverage Δ
daemon 80.60% <91.75%> (+0.44%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/protocol/src/schemas.ts 100.00% <100.00%> (ø)
packages/protocol/src/types.ts 100.00% <ø> (ø)
src/config.ts 90.71% <100.00%> (+0.27%) ⬆️
src/daemon/providers/pi/bridge.ts 100.00% <100.00%> (ø)
src/daemon/providers/registry.ts 100.00% <100.00%> (ø)
src/daemon/session-manager.ts 64.82% <100.00%> (+0.28%) ⬆️
src/daemon/session.ts 88.03% <100.00%> (ø)
src/daemon/providers/pi/rpc.ts 96.90% <96.90%> (ø)
src/daemon/providers/pi/translate.ts 88.42% <88.42%> (ø)
src/daemon/providers/pi/index.ts 90.00% <90.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@saucam
saucam merged commit 33effa2 into main Jul 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant