feat: add Pi runtime provider via external CLI RPC - #2131
Conversation
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); |
There was a problem hiding this comment.
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.
|
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. |
Summary
pias a first-class runtime provider across Shared, Client, Server, Web, CLI, and the committed QA case library.read,bash,edit,write,grep,find, andlstools with Context Tree attribution, managed Skills refresh, model assertion, steering, finite retry policy, and fail-closed protocol/custody boundaries.PI_OFFLINEoverride.V1 boundaries
Validation
pnpm checkpnpm typecheck— 11/11 taskspnpm --filter @first-tree/qa test— 4/4@first-tree/clientsuite — 2112 passed / 7 skipped7f2ef83fa2e1d899d6fb7d964510ab901e93bff4using the committedpackages/qa/cases/runtime/pi-provider.mdIntegration note
This branch intentionally preserves the exact independently accepted QA head. Since its original base,
maingained three commits and now has a content conflict inpackages/client/src/runtime/session-manager.tsaround 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.