Skip to content

feat: add pi as a direct-session provider - #233

Draft
Kunde21 wants to merge 6 commits into
happier-dev:devfrom
Kunde21:pi-direct-sessions
Draft

feat: add pi as a direct-session provider#233
Kunde21 wants to merge 6 commits into
happier-dev:devfrom
Kunde21:pi-direct-sessions

Conversation

@Kunde21

@Kunde21 Kunde21 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds pi (the @earendil-works/pi-coding-agent harness) as a fourth direct-session provider alongside Claude, Codex, and OpenCode. Pi sessions are tree-structured JSONL on disk, so this mirrors the Claude filesystem model while porting pi's own active-branch resolution so imported transcripts match what a resumed pi session actually sees.

Discovery, transcript import/follow, linking, and takeover all resolve through the existing shared seam (getDirectSessionProviderOps('pi') via the backend catalog), keeping shared orchestration provider-agnostic.

Changes

  • Protocol (packages/protocol): add 'pi' to AgentProviderIdV1 and a piAgentDir source variant to DirectSessionsSource.
  • Provider (apps/cli/src/backends/pi/directSessions/): full DirectSessionProviderOps implementation — discovery (cwd-encoded directory scan), paging (whole-file tree-walk → active-branch item-list paging), readAfter/follow, activity, working-directory (from authoritative header cwd), and takeover spawn options (pi --session <uuid> from the session's cwd, PI_CODING_AGENT_DIR env).
  • Catalog (backends/pi/index.ts): wire getDirectSessionProviderOps.
  • Linking (ensureDirectSessionLink): pi arms for source-keyed identity (piAgentDir:<agentDir>), piSessionId metadata, and pi marker recognition.
  • Security gate (validateDirectMachineSource): pi arm mirroring Claude's model (daemon-controlled agent dir, client override must match — path-traversal guard).

Key implementation notes

  • Active-branch resolution: ports pi's own buildContextEntries / buildSessionPath / _buildIndex (last-in-file leaf derivation, firstKeptEntryId compaction fold) so imports never include abandoned sibling branches or duplicate history. Faithful to the installed binary.
  • Paging cursor: backward paging uses an endExclusive cursor (collect newest-first, byte-limit truncates the older end) so pages stay gap-free and overlap-free even when maxBytes truncates below maxItems — reconstructable into full chronological order.
  • Takeover: resumes in place via pi --session <uuid> (not --fork), launched from the header cwd (authoritative; the --<cwd>-- directory name is not decoded since its encoding collapses separators and drive colons).

Two bugs found and fixed via testing

  1. Paging overlap: the original backward pager's consumed cursor caused overlapping windows when maxBytes truncated pages → duplicated/gapped, out-of-order imports of any substantial session. Caught by live validation against a real 700-item pi session.
  2. Security gate: validateDirectMachineSource had a closed switch over the provider enum with no pi arm → every pi RPC request rejected at runtime with unsupported direct session provider, despite TypeScript compiling. Caught by an RPC-handler integration test.

Testing

  • Unit: pi provider module suite (entry-context, mapping, paging, title, discovery) + linking + validation.
  • Integration: RPC-handler tests exercising list/page/readAfter/link.ensure/takeover through the real catalog + real pi providerOps with fixture pi sessions (including an abandoned sibling branch so active-branch selection comes through the RPC stack).
  • Live validation (throwaway, not committed): ran the provider against real ~/.pi/agent — discovered 119 sessions, paged a 700-item session across 7 pages (chronological, gap-free, duplicate-free).
  • No regressions across the direct-session corridor (Claude/Codex/OpenCode unaffected).

Out of scope / residual

  • The literal pi --session <uuid> process spawn was not run against a live daemon+server (needs a running daemon build + server auth). The spawn options are proven correct and match pi's verified resume semantics.
  • Protocol dist is a gitignored build artifact; CI will regenerate it from source.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Note

Add pi as a direct-session provider with transcript paging, candidate listing, and takeover support

  • Registers pi as a valid AgentProviderIdV1 and extends DirectSessionsSourceSchema with a new piAgentDir source kind.
  • Adds piDirectSessionProviderOps implementing the full DirectSessionProviderOps interface: listing session candidates, paging transcripts (backward with byte/item limits), tail-reading via polling forward cursor, retrieving activity from file mtime, and resolving spawn options to resume pi in-place.
  • Session files are discovered by scanning <agentDir>/sessions subdirectories, extracting UUIDs from JSONL filenames, and sorting by mtime. Candidate listing supports pagination via base64url index cursors and optional title/metadata search.
  • Transcript mapping walks the active branch via parentId links, handles compaction (firstKeptEntryId), and normalizes entries to DirectTranscriptRawMessageV1 with stable pi:<relPath>:<id> identifiers.
  • validateDirectMachineSource enforces that client-supplied agentDir matches the daemon-configured path, mirroring the existing claude policy.

Macroscope summarized db3805b. (Automatic summaries will resume when PR exits draft mode or review begins).

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 88b8bb60-a5e5-4eeb-b981-024d87b015d6

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.

Register `pi` in `AgentProviderIdV1` and add a `piAgentDir` source variant
to `DirectSessionsSource`, then implement the full direct-session provider
under `apps/cli/src/backends/pi/directSessions/` and wire it through the
backend catalog (`getDirectSessionProviderOps`).

Pi sessions are tree-structured JSONL keyed by `id`/`parentId`, so the
active branch is resolved by porting pi's own `buildContextEntries`/
`buildSessionPath`/`_buildIndex` tree walk into `piEntryContext.ts`
(faithful to the installed binary's `firstKeptEntryId` compaction fold).
Paging slices the projected active-branch item list (index cursors) rather
than raw bytes, since the tree walk needs the whole file; `older` pages
return chronological intra-page order so the shared import orchestrator's
page-reversal reconstructs full chronological order.

Takeover resumes in place via `pi --session <uuid>` launched from the
session's header `cwd` (authoritative; the `--<cwd>--` directory name is
not decoded because its encoding collapses both separators and drive
colons), with `PI_CODING_AGENT_DIR` pointing at the scanned agent dir.

Layer 4 linking (`ensureDirectSessionLink` pi arms) is not yet included;
linking works generically today but pi-specific source-key discrimination
and the `piSessionId` metadata field remain to be added.
…tity

Add pi arms to the four provider-discrimination sites in
ensureDirectSessionLink so pi direct sessions link with correct identity:
resolveSourceKey produces `piAgentDir:<agentDir>` (was falling to
'unknown', which collided across different PI_CODING_AGENT_DIR scopes),
buildDirectSessionMetadata writes `piSessionId`, resolveMetadataRemoteSessionId
reads it back, and resolveMarkerProviderId recognizes pi-flavored daemon
markers (flavor + backendTarget agentId).

The identity-merge path intentionally mirrors Claude (no provider-specific
re-sync block); the generic directSessionV1 update handles refresh.

With this, pi reaches full direct-session parity with Claude/Codex/OpenCode
across discovery, import, follow, takeover spawn, and linking.
…es truncates

The backward pager's cursor tracked a `consumed` item count and derived the
next page window as `total - consumed - maxItems`. When maxBytes truncated a
page below maxItems, the next window overlapped the just-delivered region,
producing duplicated/gapped items and out-of-order reconstruction —
corrupting imports of any substantial pi session. Fixtures used a large
maxBytes so never triggered it.

Switch to an `endExclusive` cursor: each page collects newest-first within
[endExclusive-maxItems, endExclusive), byte-limit truncates the older end,
and the next page window begins exactly where this one stopped. Pages stay
gap-free, overlap-free, and reconstruct into full chronological order
regardless of truncation. Adds a regression test (maxBytes: 512) that
failed before the fix.
validateDirectMachineSource had a closed switch over the provider enum
with only codex/claude/opencode arms and a default rejection. Once 'pi'
joined AgentProviderIdV1, TypeScript compiled but every pi direct-session
RPC request failed at runtime with 'unsupported direct session provider'
— a silent daemon->pi wiring break the integration test was written to
surface (and did).

Add the pi arm mirroring the claude security model: the configured agent
dir is daemon-controlled (env PI_CODING_AGENT_DIR or default ~/.pi/agent,
resolved via resolvePiAgentDir), a client may omit agentDir, and a
supplied agentDir must match the configured dir as a path-traversal
guard. Adds owner-level unit coverage for the arm and a new RPC-handler
integration test exercising list/page/readAfter through the real catalog
+ real pi providerOps against a fixture pi session (with an abandoned
sibling branch so active-branch selection comes through the RPC stack too).
…daemon RPC

Extend the existing auth-gated RPC integration coverage to the pi provider:

- link.ensure integration: add a pi case to the mock-server harness,
  asserting created=true and that the persisted (encrypted) metadata
  carries providerId='pi', piSessionId, and a piAgentDir source. Adds
  PI_CODING_AGENT_DIR to the test env scope.

- takeover: add a pi case (real catalog + fixture pi session + spawn
  capture) asserting the spawn options carry the header cwd as
  directory, resume=<pi session uuid>, builtInAgent pi,
  transcriptStorage='direct', and PI_CODING_AGENT_DIR env.

No production code changed; closes the last unverified RPC wiring paths
(link.ensure, takeover) for pi with mocked auth.
Register pi so the UI's direct-session browse picker offers it as a
discoverable provider, completing the UI side of the pi direct-session
support.

- packages/agents manifest: flip pi sessionStorage.direct to true. This
  is the gate that listDirectBrowseProviderIds (and the new-session /
  handoff direct-storage flows) check. It is an accurate capability
  declaration: the daemon already supports pi direct (in-place) session
  storage via the provider added in the CLI work.

- apps/ui: add a pi directSessions.browse capability (order 40) with a
  resolvePiBrowseSourceOptions resolver returning the piAgentDir source,
  mirroring the claude/codex/opencode providers. Add the
  browseSourcePiDefault translation across all locales.

- Tests: update resolveDirectBrowseSourceOptions to expect pi in the
  provider list, and vendorHandoffPolicy to reflect pi's now-true direct
  storage (swapping the "unsupported direct storage" example to gemini).

Broader (intended) effect: pi is now also selectable for direct
transcript storage when starting a new session and for direct-storage
handoff, matching claude/codex/opencode.
@Kunde21
Kunde21 force-pushed the pi-direct-sessions branch from 3ed24b1 to c6b1f8a Compare August 11, 2026 01:36
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