Skip to content

feat: add Pi runtime provider via external CLI RPC - #2131

Draft
yuezengwu wants to merge 17 commits into
mainfrom
feat/pi-runtime-provider
Draft

feat: add Pi runtime provider via external CLI RPC#2131
yuezengwu wants to merge 17 commits into
mainfrom
feat/pi-runtime-provider

Conversation

@yuezengwu

Copy link
Copy Markdown
Contributor

Summary

  • Add pi as a first-class runtime provider across Shared, Client, Server, Web, CLI, and the committed QA case library.
  • Drive the official external Pi CLI through its RPC mode with a long-lived, supervised session; preserve provider-owned authentication and configuration rather than copying credentials or mutating global provider state.
  • Support the native read, bash, edit, write, grep, find, and ls tools with Context Tree attribution, managed Skills refresh, model assertion, steering, finite retry policy, and fail-closed protocol/custody boundaries.
  • Settle provider-entered work with durable notice-before-ACK semantics across retry, operator suspend, daemon shutdown, reconnect, and notice-delivery recovery, preventing unsafe replay.
  • Let Pi own helper/catalog acquisition by default while suppressing provider update checks and telemetry; preserve an explicit operator PI_OFFLINE override.

V1 boundaries

  • macOS and Linux are supported. Windows fails closed before provider spawn until the accepted Job Object supervisor exists.
  • Non-empty First Tree-managed MCP configuration is rejected before prompt entry.
  • Authentication remains provider-owned; First Tree provides recovery guidance but no in-product Pi login or token custody.
  • An empty model selection and reasoning effort inherit Pi's persisted/local defaults.

Validation

  • pnpm check
  • pnpm typecheck — 11/11 tasks
  • pnpm --filter @first-tree/qa test — 4/4
  • Full @first-tree/client suite — 2112 passed / 7 skipped
  • Real Pi 0.83 host smoke — 4/4
  • Independent formal QA on exact HEAD 7f2ef83fa2e1d899d6fb7d964510ab901e93bff4 using the committed packages/qa/cases/runtime/pi-provider.md
    • Packaged Darwin daemon and authenticated Pi product path
    • Clean helper acquisition and all seven native tools
    • Context Tree read/write attribution
    • Session continuity, accepted steering, and Skill hot refresh
    • MCP/auth terminal notices
    • Manual suspend and full shutdown notice-before-ACK/no-replay races
    • Repeated notice-failure recovery, protocol/privacy checks, and Windows fail-closed behavior

Integration note

This branch intentionally preserves the exact independently accepted QA head. Since its original base, main gained three commits and now has a content conflict in packages/client/src/runtime/session-manager.ts around the new chat-session reset/replay-fence work. The conflict is not resolved in this PR head because doing so would change the validated candidate and requires a fresh integration review and QA run.

yuezengwu and others added 17 commits August 1, 2026 22:05
Introduce a macOS/Linux Pi provider driven by official `pi --mode rpc`
JSONL, with resolve-only capability probing, provider-owned auth, and
deterministic V1 MCP rejection before prompt launch.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the rejected request envelope with official command objects, parse
assistantMessageEvent/usage shapes, and keep abort/steer/settlement waiters
from hanging. Also refresh active config/Skills at idle turns, restart the
RPC process for spawn-scoped changes, treat prompt-response timeouts as
unknown custody, fix lowercase CT tool refs + git deltas, raise the minimum
published version to 0.80.5, and expand the QA case.

Co-authored-by: Cursor <cursoragent@cursor.com>
… auto-retry

Keep Pi-owned auto-retry as the only in-turn retry path, advance one-shot
context at accept/unknown boundaries, refresh briefing/Skills digests with
idle RPC restarts, and harden auth/capability classification plus usage and
model assertion contracts for the next root-reviewable head.

Co-authored-by: Cursor <cursoragent@cursor.com>
Emit durable exhausted-retry and config notices before ACK, follow shared
preflight retry, advance briefing fingerprints on the hot path, parse
colon-bearing model IDs like Pi 0.83, attach Context Tree HEAD/deltas, and
replace raw provider diagnostics with allow-listed detail.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rivacy

Route active model/MCP config failures through the shared classifier so
hot-path injects emit a durable terminal notice before consumed ACK;
validate get_state with Pi 0.83 exact-first model matching; and keep RPC
diagnostics on allow-listed tokens instead of raw response ids/commands.

Co-authored-by: Cursor <cursoragent@cursor.com>
Route hot-path drain failures (refresh, format, RPC restart, get_state,
version gate) through ProviderAttempt with a finite batch-local retry loop
instead of unbounded inbox token.retry; classify get_state/session protocol
errors as terminal Pi capability.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track the active drain batch/promise and use a shared abortable retry sleep
so suspend/shutdown can cancel backoff, await in-flight drain/turn work, and
recover each delivery exactly once with no late post-lifecycle token mutation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Formal QA rejected 521f17a for missing grep/find/ls on Pi 0.83 spawns and
for replaying an accepted bash turn after graceful client shutdown. Pass the
centralized V1 --tools allowlist on every RPC spawn, keep delivery leases valid
while handlers drain on manager shutdown, and terminally ACK provider-entered
custody exactly once before reconnect.

Co-authored-by: Cursor <cursoragent@cursor.com>
Root rejected reason-string inference for provider-entered shutdown custody
and the conflation of route adoption with delivery settlement. SessionManager
now passes settleProviderEntered only on full graceful drain, restores the
shuttingDown adoption fence, and keeps a separate settlement lease for
already-issued tokens so durable notice + ACK can complete before invalidate.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep SessionContext mutations behind the shuttingDown fence while delivery
tokens still settle terminal notice+ACK. Abort and consume on prompt write
or accept gaps without requiring a first stream event, and regress deferred
ctx probes plus before-write/write-pending/accepted-no-event shutdowns.

Co-authored-by: Cursor <cursoragent@cursor.com>
Manual session:suspend now settles write/tool-entered Pi turns before
prepareOperatorSuspend ACK, keeping route adoption fenced without
invalidating the settlement lease mid-drain.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep suspended+suspending only on the settlement lease so SessionContext
mutations stay fenced during operator suspend, and classify post-writeLine
onCommandWritten failures as after_write so FT cannot re-prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>
Clearing the dynamic token provider before SessionManager.shutdown made
notice HTTP fall back to a stale bind-time proof, so graceful drain could
settle Pi without posting notice/ACK and reconnect replayed the entered turn.
Keep the provider resolvable until drain joins; cleanup in finally after.

Co-authored-by: Cursor <cursoragent@cursor.com>
Active and deferred inject DeliveryTokens reused the adoption-only route
lease for notice+ACK, so manual session:suspend flipped the entry to
suspended and blocked token.complete; prepareOperatorSuspend then ACKed
the processing prefix without a durable runtime notice. Mirror
start/resume: mutation stays isRouteAdoptionValid, settlement uses
isDeliverySettlementLeaseValid, and attempt cancel revokes both.

Co-authored-by: Cursor <cursoragent@cursor.com>
Notice persistence failure left owned/processing rows that prepareOperatorSuspend
promoted to ordinary terminal. After recoverChat cleared debt, redelivery hit
redelivery_terminal_retry and ACKed without ever posting the human-visible
notice. Mark notice-required custody on the inbox ledger, retain it across
recovery, and settle notice-then-ACK on redelivery without re-entering the
provider.

Co-authored-by: Cursor <cursoragent@cursor.com>
A recovered notice-only redelivery that fails again must close the
activation window so the next dispatch can request recovery #2 instead
of looping on recoveryWindowOpen with no recoverChat call.

Co-authored-by: Cursor <cursoragent@cursor.com>
Unconditional --offline set PI_OFFLINE and blocked Pi's ensureTool("fd")
on clean supported hosts. Suppress version checks and install telemetry
via forced child env instead; preserve explicit operator PI_OFFLINE.

Co-authored-by: Cursor <cursoragent@cursor.com>

export function isPiBinaryMissingError(input: unknown): boolean {
const text = errorText(input);
return /pi cli is missing|pi.*not (?:found|installed)/i.test(text);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed as a valid finding. The unanchored pi.*not ... alternative is quadratic on repeated pi input; a local scaling probe reproduced ~0.16 ms at 250 repetitions and ~14.94 ms at 4,000 repetitions. The next integration head will replace it with an equivalent linear string search and add an adversarial regression. The current PR head remains frozen because it is the exact independently QA-accepted candidate; the fix will receive root review and a fresh formal QA run together with the pending main integration.

@yuezengwu

Copy link
Copy Markdown
Contributor Author

Context Tree draft: https://github.com/agent-team-foundation/first-tree-context/pull/875

It records the durable Pi provider, authentication, platform-support, configuration, and onboarding boundaries from this source PR. It will remain draft until this source change is merged and the Tree diff is reconciled against merged source truth.

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.

2 participants