You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[NEEDS GROOMING] Build execution profiles UI (Read Only / Ask Every Time / Auto Approve) since pi-coding-agent has no built-in permission-mode system #133
Follow-up to #132 (re-enabling pi-coding-agent's full built-in tools). #132 accepts "no approval gate" as an interim tradeoff for a 1-day config flip. This issue tracks building a real, user-facing permission/execution-profile system on top of that.
Confirmed (exhaustive research, see #132) that pi-coding-agent deliberately does not ship any named permission-mode concept. Straight from its README:
"No permission popups. Run in a container, or build your own confirmation flow with extensions..." (node_modules/@earendil-works/pi-coding-agent/README.md:499)
"No plan mode. Write plans to files, or build it with extensions, or install a package." (README.md:501)
So there is nothing like Claude Code's default/acceptEdits/bypassPermissions/plan modes to adopt as-is — pi-desktop needs to build its own "execution profiles" concept on top of pi-coding-agent's lower-level primitives, and expose it in the UI.
Available primitives (all confirmed to exist)
Tool allow/deny-lists on CreateAgentSessionOptions (dist/core/sdk.d.ts:10-46): tools?: string[], excludeTools?: string[], noTools?: "all" | "builtin", plus a ready-made createReadOnlyTools() factory. Runtime-live equivalent: ExtensionAPI.getActiveTools() / setActiveTools(toolNames) (dist/core/extensions/types.d.ts:933,937).
ctx.ui.confirm / ctx.ui.select (dist/core/extensions/types.d.ts:71-72,399) — interactive confirmation primitives used by pi's own CLI/TUI extensions. These are TUI-specific and not usable directly in pi-desktop per architecture rule fix: distinct artifact names for Windows nsis/portable targets #4 (renderer never talks to Node/Pi APIs directly) — pi-desktop needs its own IPC-driven equivalent instead.
Reference implementations already exist in pi-coding-agent's own examples/extensions/, worth reading directly as a starting template:
examples/extensions/permission-gate.ts — regex-detects dangerous bash commands (rm -rf, sudo, chmod/chown 777) and confirms via ctx.ui.select before allowing.
examples/extensions/plan-mode/index.ts — full read-only-exploration-mode pattern: toggles getActiveTools()/setActiveTools() and blocks non-allowlisted bash commands via tool_call.
Proposed execution profiles
Profile
Implementation
Read Only
excludeTools: ["bash","edit","write"] (or createReadOnlyTools()); no tool_call handler needed.
Ask Every Time
Full built-ins + an inline tool_call handler that blocks execution until an IPC round-trip to a renderer confirmation dialog resolves (approve/deny), modeled on permission-gate.ts.
Auto Approve (a.k.a. "skip permissions")
Full built-ins, no tool_call handler at all — current end state of #132.
Plan Mode (optional/bonus)
Reuse the plan-mode example's pattern: start read-only for exploration, toggle to full tools only once the user explicitly promotes the session to "execute".
What needs to change
src/main/agent/runtime.ts
Add an execution-profile parameter to AgentRuntime/its run() method (or read from settings), mapping each profile to the right tools/excludeTools/noTools combination on createAgentSession(...).
For "Ask Every Time", register an inline tool_call extension handler via DefaultResourceLoaderOptions.extensionFactories (the same seam already used for additionalExtensionPaths in buildAdditionalPathsResourceLoader()), that pauses execution and awaits an IPC-driven approval from the renderer before resolving { block }.
New IPC channel(s) (following the <domain>:<verb> convention, docs/INITIAL.md/AGENTS.md "Adding an IPC channel" recipe):
src/shared/events.ts — add method signature(s), e.g. tool:confirm (main → renderer request) and its response shape, plus a settings-facing settings:getExecutionProfile / settings:setExecutionProfile pair.
src/preload/index.ts — expose the new methods on api.
src/main/ipc.ts — wire the handler(s); the tool:confirm flow will need to be a webContents.send + one-shot response channel (main-initiated, not renderer-invoked), since it's the main process asking the renderer for a decision mid-tool-call.
src/renderer/lib/fake-desktop-api.ts — update the in-memory fake so the browser dev harness keeps working (per repo convention).
Renderer UI
A profile selector (e.g. in Settings or a per-conversation control) persisted via the settings store, following the existing SettingsStore/Sidebar.tsx component conventions (flat file under src/renderer/components/, styled via the single shared styles.css).
A confirmation dialog component for "Ask Every Time" mode, showing the tool name + event.input (command/path/content) with Approve/Deny actions, wired to resolve the pending tool:confirm IPC request.
Persistence
Store the selected execution profile per-session (or globally, TBD) alongside existing session state (SessionService/session-projection.ts), so resumed conversations restore the profile the same way they already restore selectedModel (see AGENTS.md lesson fix: distinct artifact names for Windows nsis/portable targets #4).
AGENTS.md — update "Adding an IPC channel" section if the confirm-dialog pattern introduces a new recipe worth documenting (main-initiated request/response over IPC is a new pattern not covered by the existing invoke-based recipe).
Testing
Unit test the profile → createAgentSession option mapping (no real network/tool execution).
Unit/integration test the tool_call handler's block/allow logic independent of the UI (given a fake confirm resolver).
E2E: verify via the fake-desktop-api.ts browser harness that switching profiles changes available tools, and that "Ask Every Time" actually surfaces a confirmation dialog before a tool call event is shown in the timeline.
Acceptance criteria
Three profiles implemented: Read Only, Ask Every Time, Auto Approve.
Profile selectable in the renderer UI and persisted (settings and/or per-session).
"Ask Every Time" blocks tool execution on a real tool_call event until the renderer responds via IPC; rejecting the dialog actually prevents the tool from running (verified against the real packaged app, not just dev mode — see AGENTS.md "packaged app" testing rules).
Background
Follow-up to #132 (re-enabling pi-coding-agent's full built-in tools). #132 accepts "no approval gate" as an interim tradeoff for a 1-day config flip. This issue tracks building a real, user-facing permission/execution-profile system on top of that.
Confirmed (exhaustive research, see #132) that
pi-coding-agentdeliberately does not ship any named permission-mode concept. Straight from its README:So there is nothing like Claude Code's
default/acceptEdits/bypassPermissions/planmodes to adopt as-is — pi-desktop needs to build its own "execution profiles" concept on top of pi-coding-agent's lower-level primitives, and expose it in the UI.Available primitives (all confirmed to exist)
CreateAgentSessionOptions(dist/core/sdk.d.ts:10-46):tools?: string[],excludeTools?: string[],noTools?: "all" | "builtin", plus a ready-madecreateReadOnlyTools()factory. Runtime-live equivalent:ExtensionAPI.getActiveTools()/setActiveTools(toolNames)(dist/core/extensions/types.d.ts:933,937).tool_callextension event (dist/core/extensions/types.d.ts:648-778) — fires before everybash/read/edit/write/grep/find/ls/custom tool call. Handler can return{ block: true, reason }to veto, or mutateevent.inputin place before execution. Wired internally byAgentSession(core/agent-session.js:214-259) regardless of trust status — a separate mechanism from the per-package install-time trust gate (fix(packages): share PackageService with real ~/.pi/agent + close trust-gate bypass #107-refactor(packages): replace per-package trust gate with install-time consent #110).ctx.ui.confirm/ctx.ui.select(dist/core/extensions/types.d.ts:71-72,399) — interactive confirmation primitives used by pi's own CLI/TUI extensions. These are TUI-specific and not usable directly in pi-desktop per architecture rule fix: distinct artifact names for Windows nsis/portable targets #4 (renderer never talks to Node/Pi APIs directly) — pi-desktop needs its own IPC-driven equivalent instead.examples/extensions/, worth reading directly as a starting template:examples/extensions/permission-gate.ts— regex-detects dangerous bash commands (rm -rf,sudo,chmod/chown 777) and confirms viactx.ui.selectbefore allowing.examples/extensions/plan-mode/index.ts— full read-only-exploration-mode pattern: togglesgetActiveTools()/setActiveTools()and blocks non-allowlisted bash commands viatool_call.Proposed execution profiles
excludeTools: ["bash","edit","write"](orcreateReadOnlyTools()); notool_callhandler needed.tool_callhandler that blocks execution until an IPC round-trip to a renderer confirmation dialog resolves (approve/deny), modeled onpermission-gate.ts.tool_callhandler at all — current end state of #132.plan-modeexample's pattern: start read-only for exploration, toggle to full tools only once the user explicitly promotes the session to "execute".What needs to change
src/main/agent/runtime.tsAgentRuntime/itsrun()method (or read from settings), mapping each profile to the righttools/excludeTools/noToolscombination oncreateAgentSession(...).tool_callextension handler viaDefaultResourceLoaderOptions.extensionFactories(the same seam already used foradditionalExtensionPathsinbuildAdditionalPathsResourceLoader()), that pauses execution and awaits an IPC-driven approval from the renderer before resolving{ block }.New IPC channel(s) (following the
<domain>:<verb>convention,docs/INITIAL.md/AGENTS.md "Adding an IPC channel" recipe):src/shared/events.ts— add method signature(s), e.g.tool:confirm(main → renderer request) and its response shape, plus a settings-facingsettings:getExecutionProfile/settings:setExecutionProfilepair.src/preload/index.ts— expose the new methods onapi.src/main/ipc.ts— wire the handler(s); thetool:confirmflow will need to be awebContents.send+ one-shot response channel (main-initiated, not renderer-invoked), since it's the main process asking the renderer for a decision mid-tool-call.src/renderer/lib/fake-desktop-api.ts— update the in-memory fake so the browser dev harness keeps working (per repo convention).Renderer UI
SettingsStore/Sidebar.tsxcomponent conventions (flat file undersrc/renderer/components/, styled via the single sharedstyles.css).event.input(command/path/content) with Approve/Deny actions, wired to resolve the pendingtool:confirmIPC request.Persistence
SessionService/session-projection.ts), so resumed conversations restore the profile the same way they already restoreselectedModel(see AGENTS.md lesson fix: distinct artifact names for Windows nsis/portable targets #4).Docs
docs/adr/0001-reuse-pi-extension-mechanism.md— document the new tool-permission architecture (profiles, thetool_callhandler mechanism, IPC confirmation flow) once implemented, superseding the "no approval gate" interim note added by Re-enable pi-coding-agent's full built-in tools (bash/edit/write/read/list), remove read-only-only scope #132.STATUS.md— new milestone entry.AGENTS.md— update "Adding an IPC channel" section if the confirm-dialog pattern introduces a new recipe worth documenting (main-initiated request/response over IPC is a new pattern not covered by the existing invoke-based recipe).Testing
createAgentSessionoption mapping (no real network/tool execution).tool_callhandler's block/allow logic independent of the UI (given a fake confirm resolver).fake-desktop-api.tsbrowser harness that switching profiles changes available tools, and that "Ask Every Time" actually surfaces a confirmation dialog before a tool call event is shown in the timeline.Acceptance criteria
tool_callevent until the renderer responds via IPC; rejecting the dialog actually prevents the tool from running (verified against the real packaged app, not just dev mode — see AGENTS.md "packaged app" testing rules).