feat(desktop): add Pi extension support - #76416
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
Compatibility note for extension authorsPostHog Desktop runs Pi through RPC mode rather than Pi's terminal UI. Standard Pi extension behavior, including tools, hooks, commands, and resource loading, works normally. The RPC UI methods bridged by this PR also work:
TUI-specific rendering cannot be transferred directly to Electron. export default function (pi: ExtensionAPI) {
pi.registerCommand("my-command", {
description: "Run my extension",
handler: async (_args, ctx) => {
if (ctx.mode === "tui") {
// Rich terminal-only implementation.
return runCustomTui(ctx);
}
if (!ctx.hasUI) {
return;
}
const choice = await ctx.ui.select("Choose an action", [
"Analyze",
"Configure",
]);
if (choice === "Configure") {
const value = await ctx.ui.input("Configuration", "Enter a value");
ctx.ui.notify(`Saved ${value}`, "info");
}
},
});
// Useful in Pi's TUI. Desktop users can invoke /my-command instead.
pi.registerShortcut("ctrl+shift+x", {
description: "Run my extension",
handler: /* ... */,
});
}The main fallback mappings are:
Use if (ctx.mode === "tui") {
// Custom components, component factories, and terminal input.
}
if (ctx.hasUI) {
// Dialogs, notifications, statuses, and text widgets.
}For widgets, use text lines in RPC/Desktop mode because component factories are ignored: ctx.ui.setWidget("results", [
"3 files changed",
"Tests passing",
]);A future rich custom UI system would need a declarative, sandboxed protocol that Electron can render. Arbitrary terminal components cannot be supported transparently by the current RPC wire format. |
Prompt To Fix All With AI### Issue 1
products/desktop/packages/workspace-server/src/services/pi-session/pi-session.ts:490-495
**Failed stop leaves stale session**
When `session.client.stop()` rejects for the current session, this branch rethrows before removing the session or unregistering its process. A later resume with the same cwd and trust path then takes the existing-session fast path, leaving the task connected to a dead or partially stopped Pi runtime.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(desktop): add Pi extension support" | Re-trigger Greptile |
jonathanlab
left a comment
There was a problem hiding this comment.
Thanks a lot for this, really appreciate you digging in to this. Neat to see extension RPC slot in fairly easily like this.
Because we our RPC streams are not stored anywhere, it seems like this turned out to be trickier than expected, we have to do a lot of state tracking with regards to extensionReplay and extension_state_snapshot and state management in PiSessionStore. I'm hesitant about introducing more state because it makes things complex and hard to reason about.
I personally think we should try to keep UI extension state ephemeral. As far as I know, pi-cli extension UI state is also ephemeral and does not survive reconnects, so lets not try do to that here. Any state that's meaningful we should serialize in the session file. We can then construct fresh UI from that on restart.
There's also some things that need refactoring/changing here, left some comments.
|
Addressed the overall review direction in ee6c62c. Extension UI state is now ephemeral: the backend replay/snapshot protocol and custom lifecycle events were removed, extension concerns were split into a dedicated controller and reducer, and runtime-scoped streams clear state rather than reconstructing it across reconnects or replacements. Pending dialogs are cancelled when possible so removing replay does not leave an active extension waiting indefinitely. |
Problem
Pi sessions in the desktop app could run built-in harness extensions, but they could not expose the standard Pi extension UI protocol to the renderer. Project-local
.piresources also had no explicit repository trust flow, so safely loading project extensions, skills, prompts, and settings was not possible.Changes
Before:
flowchart LR A{{Pi runtime}} --> B[Conversation RPC] B --> C[Desktop chat] D[Project .pi resources] --> E[Disabled] classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff; classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000; class A phBlue; class B,C phYellow; class D,E phGray;After:
flowchart LR A{{Pi runtime}} --> B[Conversation RPC] A --> C[Extension UI RPC] B --> D[Desktop chat] C --> E[Dialogs and extension surfaces] F[Project .pi resources] --> G[Repository trust gate] G --> A classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff; classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000; class A phBlue; class B,C,G phRed; class D,E phYellow; class F phGray;Warning
Trusted project extensions run arbitrary code with the user's permissions. The trust dialog calls this out before enabling repository resources.
How did you test this code?
pnpm build:depsand the full desktoppnpm typecheckacross 20 packages.packages/core, host-boundary validation,git diff --check, andhogli ci:preflight --strict.autoreviewskill after implementation and after porting into the monorepo. Final result was blocker-free.The added tests guard realistic regressions in RPC request/response mapping, replay deduplication and reconnect behavior, trust reuse across unrelated repositories, Pi session continuity during trust changes, and accessible dialog submission behavior.
👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Automatic notifications
Docs update
Added
products/desktop/docs/PI-EXTENSIONS.mdcovering installation, project trust, supported APIs, and security boundaries.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Implemented with the Pi coding agent in a local session. Skills used:
pr,autoreview,/writing-tests,/writing-user-facing-copy, and/writing-code-comments.The implementation uses Pi's native package, extension, RPC UI, and trust semantics rather than introducing a separate desktop plugin system. Project trust is attached to the registered repository and validated managed worktrees; cloud sessions remain isolated from local resources.