Skip to content

fix(server): restore user Pi extension and plugin loading - #427

Open
iamdin wants to merge 5 commits into
mainfrom
fix/local-pi-extensions
Open

iamdin wants to merge 5 commits into
mainfrom
fix/local-pi-extensions

Conversation

@iamdin

@iamdin iamdin commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Requirement

Restore loading of the user's local Pi extensions and configured plugin packages. A provider such as CLIProxyAPI can have endpoint/auth configuration in models.json while its extension supplies the model list; the blanket noExtensions policy introduced by #104 removed those models from Pie's picker.

Expected behavior

  • User extensions and configured packages contribute providers/models and slash commands in discovery and live sessions, including resume.
  • Extension commands/input hooks that handle input without a model turn succeed immediately rather than failing or waiting for a nonexistent turn.
  • Startup and discovery dialogs without an active turn receive an explicit decline/cancellation; ordinary RPC waits for startup while UI replies remain readable. Extensions that require interactive startup confirmation will receive a decline, not automatic approval.
  • Only an explicitly supplied registered Project is approved for Project resources. Global discovery does not approve incidental Project-local code, even with defaultProjectTrust: always.
  • Explicit --no-extensions, --extension, and Project trust overrides remain effective in the bundled child. No edits or migration of user settings, credentials, or model configuration.

Changes and risks

  • Remove the blanket automatic-extension opt-out from Pie's normal discovery/session policy; retain cold discovery's theme/context exclusions.
  • Preserve the SDK's prompt disposition alongside the existing started boolean. A handled input returns an empty, non-started receipt without relaxing the queued-without-an-active-turn invariant.
  • Pass Project trust and explicit extension flags through the bundled runtime; keep global discovery unapproved for Project resources.
  • Add isolated tests for user extension directories, local plugin packages, provider defaults, command discovery, handled input, create/resume, and opt-out/trust denial. Update the domain documentation.

Extensions are trusted executable code with the user's OS permissions, just as in Pi; this is not a sandbox or an extension whitelist. Terminal-only custom extension UI remains unsupported. No new Pie-owned storage format or location is introduced.

Latest verification — 67aa9ee5

Startup-dialog follow-up: child stdin/UI routing starts before binding finishes; parent UI routing starts before readiness; discovery explicitly declines dialogs. EOF cancels pending dialogs and gracefully drains admitted commands. Five real bundled-child startup/trust regressions added. Process-heavy verifier files run serially to avoid CI startup starvation without relaxing assertions or deadlines.

  • pnpm build and pnpm check passed.
  • Full local Node suite: 1352 passed, 1 skipped, no type errors.
  • New-head GitHub Check passed: 1352 Node + 100 browser tests; React Doctor and preview checks passed.
  • Fresh author Web/Desktop startup before/after evidence is in the follow-up comment. Web screenshots and both 60 fps videos are complete. Desktop screenshots and head video are available, but baseline recording failed twice (encoder backpressure); Desktop video acceptance remains incomplete.
  • Local macOS browser suite: 99 passed / 1 failed in the unchanged Ctrl+Enter case, also reproduced on the clean pre-fix baseline. Linux CI passed all 100.

Independent re-review/acceptance is still required. No merge or installation performed.

Original model-loading verification — 268a1f3e

Tested head: 268a1f3e59e48cf9ea6abf33306c3a532c61070a; baseline: 01f864fe30b4376714547bd6bd6576b5540e3d03.

  • pnpm check — passed (lint, formatting, workspace typechecks).
  • pnpm exec vitest run --project server — 643 passed, 1 skipped, no type errors. The added integration tests run the real Bun-built pie-pi-process with an isolated agent directory and an in-process fixture provider; they do not substitute a fake Pi executable.
  • pnpm exec turbo run build --filter=@getpie/server --force — passed for the tested head.
  • Isolated Desktop launch/doctor/drive/cleanup on both the clean baseline worktree and the fixed head, using the same synthetic user-extension fixture. Baseline picker has no fixture models; head exposes/selects E2E Fake, completes a Pi turn, exposes /local-ping, and handles it without hanging or browser errors. Screenshots and both recordings attached.
  • Anonymous daemon ticket request remains 401; authenticated request is 200. All verification processes were cleaned up; the installed Desktop/user daemon was not replaced or stopped.

Limits: Desktop proof uses a deterministic fixture provider, not a paid/remote model service. The user's real CLIProxyAPI endpoint/key was not exercised. Web browser drive and packaged .app installation were not verified. This PR is not merged or installed locally.

local-extension-models-before

local-extension-models-after

local-extension-response-after

local-extension-command-after

recording-001.webm
recording-001.webm

@pkg-pr-new

pkg-pr-new Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
npx https://pkg.pr.new/oxwen11/pie/@getpie/cli@427

commit: 67aa9ee

@oxwen11

oxwen11 commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Independent review — P1 startup-dialog deadlock, not accepted

Head 579d972d83415a94968b2819b14afe1bd2ccc09d, base/trusted rules 4a338cb0f5e1b22d24ca1dea46ce1ee6a50c2cd3. All required CI passed before review. Reviewed the full 13-file diff, discovery callers/Project lookup, resource policy, child construction, prompt admission, and extension-UI transport/lifecycle.

P1: enabling user extensions exposes an unanswerable startup dialog

The removal of automatic extension opt-out (project-resource-policy.ts, normal discovery/session paths) enables legitimate user extensions that await a supported RPC UI dialog in session_start:

export default function (pi) {
  pi.on("session_start", async (_event, ctx) => {
    if (ctx.hasUI) await ctx.ui.confirm("Startup verification", "Continue?");
  });
}

This is not terminal-only custom UI: confirm is part of the documented RPC request/response protocol, and ctx.hasUI is true.

Two initialization barriers prevent progress:

  1. rpc/rpc-mode.ts:353–355,427,862–872: await rebindSession() waits for session.bindExtensions() / the startup handler before attaching the stdin JSONL reader. The child emits extension_ui_request, but cannot consume the matching response while waiting for that handler.
  2. pi/process.ts:425–439,458–464: the parent waits for the get_state readiness handshake before starting its UI-request consumer. Thus moving only the child reader is not sufficient for the actual parent lifecycle. listAvailablePiCommands also starts this child without a consumer for interactive startup requests.

The normal live session consequently cannot finish acquisition with such an extension (the parent has a handshake timeout); command discovery cannot complete through the same startup barrier. Restoring extension loading makes this previously-disabled user-extension path reachable.

Independent executable reproduction

Fresh pinned reviewer worktree, locked install, Turbo-built actual Bun pie-pi-process; isolated agent directories and sample working directories, offline mode, no credentials copied, no model call, no product/source modification.

Drove real JSONL stdin/stdout in two otherwise equivalent cases:

Case Confirm emitted Matching confirm reply sent get_state succeeded
No startup-dialog extension no n/a yes
Extension above yes yes, immediately no

The diagnostic sent {"id":"state","type":"get_state"} on startup, then answered the emitted request with {"type":"extension_ui_response","id":"<emitted id>","confirmed":true}. No state response arrived during the subsequent 3-second observation window; source inspection establishes the dependency cycle, rather than interpreting a timeout alone as proof. Both owned child processes were reaped and temporary fixtures removed.

The normal extension/discovery/agent suite independently passed 4 files / 36 tests, no type errors. Its existing happy-path/handled-input/trust tests do not exercise startup dialogs.

Required follow-up and scope

Coordinate startup input handling, parent readiness/UI routing and noninteractive command discovery so a startup extension cannot wait on a response that the host cannot deliver. Do not silently approve requests or weaken Project trust. Add deterministic startup-dialog and discovery regressions, then restart new-head CI, independent review and affected Web/Desktop acceptance.

Review blocked; not merged. No source repair was attempted. This is actual bundled-child protocol verification, not paid-model or Web/Desktop UI acceptance. The author screenshots and previous happy-path proofs do not resolve this finding. Further acceptance stopped at this reproduced defect.

@oxwen11

oxwen11 commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Queue update after verified #423 merge — waiting at CI

Head 425d71a8c3e019cb803906f70740f40f7f658ebd now contains main/trusted-rules 0920277492d381b108b520b341c449bcc3e3dbd8 (behind_by=0 verified). GitHub performed a conflict-free merge update guarded by the previous exact head; no conflict resolution or source repair.

Required CI for this new head is queued/in progress at this observation, so no old-head review or acceptance is carried forward. The startup-dialog blocker remains unresolved: #427 (comment)

No merge or deferred auto-merge. Continue only after the applicable blocker is resolved and the new version passes required CI → review → independent acceptance.

@iamdin

iamdin commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Consolidated duplicate PR #428 into this PR. Production behavior was identical (only an explanatory comment differed). Preserved its additional --no-extensions command-discovery assertion in commit 3e21e19; its handled-input empty-stream assertion is already covered by this PR’s real bundled-process integration tests. After syncing this branch with its remote main merges, all 36 focused server tests passed with no type errors, and pnpm check passed. Previous Desktop screenshots/video remain evidence for the original fix, not a new drive of this head. Retain #427 as the single PR; no merge performed.

Read startup UI responses while gating ordinary RPC commands on binding. Start parent UI routing before readiness and explicitly decline discovery dialogs. Cancel dialogs on EOF, drain admitted commands, and propagate graceful exit through the RPC entry point.

Add real bundled-child startup and trust regressions. Serialize process-heavy verifier files to avoid full-suite startup starvation without changing security assertions or timeouts.
@iamdin

iamdin commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Startup-dialog fix — new head 67aa9ee5, CI green; please re-review

Addresses the startup-dialog blocker in #427 (comment):

  • The bundled child attaches stdin while extensions bind. UI responses bypass that initialization gate; ordinary RPC commands still wait for binding.
  • The parent starts its UI consumer before the readiness handshake and publishes the session only after startup completes. With no active turn/question consumer, startup dialogs receive the existing explicit decline/cancellation, never an approval. Noninteractive command discovery also consumes and declines all supported blocking requests.
  • EOF cancels pending/future dialogs, drains admitted commands, and propagates graceful exit through entry.ts instead of an unhandled callback rejection. Active-turn question handling and Project trust remain unchanged.
  • Five real bundled-child regressions cover all four supported dialog kinds, positive protocol replies, safe create/resume/discovery declines, EOF/exit, and global discovery's Project trust denial.

Verification: observed the original three startup cases fail before the fix; new regressions pass. pnpm build and pnpm check pass. Full local Node suite: 1352 passed, 1 skipped, no type errors. This head's GitHub Check passed: 1352 Node tests + 100 browser tests; React Doctor and preview checks also passed. The verifier CI starvation was addressed by serializing its process-heavy files, not by changing assertions or increasing deadlines. Its concurrent-live-run isolation case is retained.

Fresh author runtime evidence: isolated actual Web server and Desktop/token daemon plus the real Bun Pi child, using the same offline extension fixture on pre-fix 3e21e19d and this head. The ordinary draft-send path fails before the fix; here Bun exits with code 0 while waiting for startup UI, rather than surviving until the handshake timeout. After the fix, startup's confirm records false, acquisition completes, and the fixture provider replies. Desktop anonymous/authenticated tickets remain 401/200. Owned verification processes were cleaned up. No installed app, actual Pi configuration, real credential, or paid model service was changed/used.

Evidence limits: Web before/after screenshots and both 60 fps recordings attached. Desktop before/after screenshots and the validated head recording attached; the Desktop baseline recorder failed with encoder-backpressure errors on two attempts, so its video proof remains incomplete and no corrupt clip is attached. Local macOS browser tests are 99 passed/1 failed in the unchanged Ctrl+Enter case; the same failure was reproduced in the clean pre-fix worktree. Linux CI passed all 100 browser tests.

This is author integration evidence, not independent acceptance. Please restart independent review/affected acceptance on this exact head; no merge or auto-merge has been requested.

startup-dialog-before

startup-dialog-after

recording-001.webm
recording-001.webm

startup-dialog-before

startup-dialog-after

recording-001.webm

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