ADR 0214: Trusted extensions run in the Agent sidecar
- Status: Accepted (v1 implemented 2026-09-10)
- Date: 2026-09-10
- Decision: D387
- Related: ADR 0002, ADR 0008, D007,
07-plugins/16-trusted-extensions.md,07-plugins/01-plugin-system.md§14
Context
PI-Desktop's plugin system (ADR 0008) runs plugins in their own process behind a permission gateway. That is the right shape for distributed, untrusted code, but it leaves no surface for code that must sit on the agent loop itself: tools that execute in-process with the session's working directory, hooks on every turn, tool call, and provider request, and slash commands that carry session context. The desktop runtime in packages/agent-runtime/src/runtime.ts already exposes these hook points internally; nothing lets a user attach code to them.
The kernel packages pi-ai and pi-agent-core (ADR 0002) have a sibling, pi-coding-agent, that defines an ExtensionAPI contract and a loader for TypeScript extension modules. Adopting that contract gives PI-Desktop a second extension surface with an existing, documented API and an existing body of extensions written against it.
Three routes were evaluated: implement ExtensionAPI over the desktop runtime; express the same capabilities as a new plugin contribution type; or replace the desktop runtime with pi-coding-agent's AgentSession.
Decision
- Trusted extensions are the second extension surface. The sidecar pins
pi-coding-agentat the same version as the other two kernel packages as a types-only dependency, mirrors its discovery rules, loads modules withjiti/staticand virtual modules, and implementsExtensionAPIon top of the desktop runtime's hook points in a desktop-owned Runner. One Runner per session. (The upstreamExtensionRunnerbinds the terminal theme and is not reused.) - Trusted, opt-in, no auto-import. Extensions are labelled "Trusted extension", run with sidecar trust, and are disabled until the user enables each one. D007 is unchanged:
~/.piis scanned for candidates, never imported. Project-scoped extensions are enabled per project. - Explicit support classes. Every
ExtensionAPImember is Supported, Deferred, or Unsupported. Unsupported members are inert and produce diagnostics; they never throw. Terminal-UI surfaces stay Unsupported. - Plugins are untouched. No plugin manifest, permission, or process boundary changes. Extension tools and commands enter the same catalogs plugins use, after plugins, and lose name collisions.
- No host-core change in v1. Enablement is app settings; new traffic is sidecar-to-main proxy methods and Electron IPC only.
Consequences
- Users can attach in-process tools, hooks, and commands to the agent loop without a new app release.
- Two extension surfaces coexist and are explained in user documentation as "plugins" (sandboxed) and "trusted extensions" (in-sidecar).
- The three kernel packages move in lockstep; a contract test with sample extensions guards every upgrade.
- The sidecar bundle must keep jiti loadable; this is verified first.
- Module-level extension state is shared across sessions in one sidecar.
Alternatives considered
- A new plugin contribution type for in-process hooks. Rejected: it would either punch through the plugin sandbox or duplicate
ExtensionAPIunder a second name with no existing extensions to run. - Replace the desktop runtime with
AgentSession. Rejected for now: it discards the desktop's modes, plans, deferred tools, and subagent logic, and terminal-UI surfaces remain unusable regardless. Kept as a long-term option. - Run extensions inside the plugin host process. Rejected: the permission gateway would deny most of what extensions do, and they would fail at load.