Repository navigation
v0.14.0
Added
-
Subagent gate (omp only; pi behavior is unchanged): omp runs subagents in-process with no UI, so every
askinside a subagent used to degrade to deny. New config keyssubagentGate("off"default |"normal"|"auto") andsubagentAskTimeoutMs(positive integer ms, default60000) route subagent asks to the root session's UI."normal"shows the usual confirmation dialog on the root UI, prefixed with a[subagent <id> (<name>)]label; if nobody answers beforesubagentAskTimeoutMs(counted from enqueue, queue wait included) or the root has no UI, the ask is resolved without a human."auto"never prompts and always resolves without a human."off"(the default) makes the gate inert in subagents: no rule, floor or classifier check runs on a subagent's tool calls — opt in with"normal"or"auto"to gate them. Resolving without a human consultsclassifierFallbackModelonly for asks the classifier raised, and only an explicitallowfrom it permits the call — anything else denies, with block tagsubagent-auto; protected-path /.ompasks and asks that exist only because ofautoDeny: falsealways deny (ADR-0002: no path plaintext reaches notifications or the agent), and a demoted first-layer deny is never auto-allowed (ADR-0004). A cancelled subagent run closes its pending root dialog and blocks withsubagent-cancelledwithout consulting the second model. Root dialogs are now serialized process-wide (omp queuesconfirmbut notcustom). Withaudit: true, subagent asks recordsubagent: { id, name, resolution: "human" | "timeout" | "auto" }, and second-model resolutions recordfallback.triggeredBy: "subagent-ask". Set omp'sextensionHandlers.toolCallTimeoutMsto at leastsubagentAskTimeoutMs + 60000— a subagent waiting on the root UI does not pause its handler budget. -
The first-run config template pre-fills a starter
toolsallowlist (ask,todo,wait,task,yield,think,checkpoint,rewind,recall,reflect): omp tools with no side effect of their own, or whose effects are gated elsewhere, skip the classifier from the first session after the initial run. Tools that read paths outside the file-tool family (glob,ast_grep,lsp), send data off-machine (web_search), persist memory, execute code or mutate state are deliberately left out. A pre-filled user declaration, not a built-in allowlist; existing configs are never rewritten. -
gateOmpDir(defaulttrue): forced.ompgate. Any file-tool path or bash command that touches a.ompdirectory (path segment.omp, matched on the lexical and symlink-resolved forms; bash also matches.ompas a bare word, e.g.cd .omp) is a terminal ask — non-interactive sessions degrade to deny. Pipeline position: after the built-in floor and userdeny, beforedenyPathsand userallow(anallowregex cannot skip it). The ask reason carries no path (UI-only detail).grep/find/lsare checked on their own target only. Setfalseto disable;/verdictnow has agateOmpDiron/off switch (project files can also unset it to inherit the global value). -
/verdict [user|local]command: edit the list keys ofpi-verdict.json(allow,deny,denyPaths,tools,rules) from the TUI — add, edit, and remove entries.usertargets<agentDir>/config/pi-verdict.json,localthe project config (<project>/.pi/pi-verdict.json,.omp/…on omp); a bare call asks which. Regex entries (allow/deny) are validated before saving. Each save rewrites only the edited key (other keys, key order, and_hintare preserved), reloads the rules into the running session immediately without clearing shadow-cache or fallback session stats, and never auto-trusts a project. The first project-level edit of a key offers to start from a copy of the global list (a project list replaces, not extends, the global one);× Unsetremoves the override so the project inherits the global list again. Other scalar keys (builtinDenyFloor,classifierModel,toggleShortcut, …) are out of scope. Requires an interactive UI. -
Rich approve dialog in the interactive TUI: the code under review (bash
command, writecontent, editnewTextblocks) is rendered with the chat's Markdown renderer as a syntax-highlighted fenced block (capped at 40 lines / 4000 characters; control and bidi characters shown as\uXXXXescapes). When the ask comes straight from jev, a colored bar graphs its allow/ask/deny distribution with a confidence legend, plus aconcern:line. RPC mode and hosts withoutui.customkeep the plain-text confirm unchanged. -
jev backend: each decisions request now also carries a typed
concernquestion (deletion, write outside the project, network, package install, environment change, credentials, untrusted code execution, other, none); the answer is appended to the reason as— concern: <label>(cosmetic — a missing or malformed answer is ignored, only the verdict stays fail-closed). -
EXPLAIN-GATE role and two new options on the interactive ask dialog (rich TUI dialog only; RPC mode and hosts without
ui.customkeep the plain Yes/No confirm unchanged). Explain… takes an optional free-text question and calls the EXPLAIN-GATE role — a model call (session model by default) whose system prompt is fixed and whose default task isExplain what this action does and why the gate held it for confirmation.; a non-empty question replaces the default task. The model sees the transcript (same sanitized window as the classifier), the full code under review and the gate's stated reason; its answer is rendered in the re-opened dialog, labelled advisory, and never reaches the agent, the verdict, the audit log or the classifier. It can be asked repeatedly before deciding. No, with explanation… declines and forwards the user's text (single line, sanitized, ≤1000 characters) to the agent in the block reason (user declined, saying: "…"); plain No and Escape are unchanged, and Escape in either follow-up prompt returns to the dialog. Explain is not offered for protected-path asks (denyPathshits and the.ompgate): their path plaintext is UI-only and must not reach a model provider (ADR-0002); No, with explanation… is available there. New config keysexplainGateModel(provider/id[:thinking], default: session model; unavailable → session model with a one-time warning) andexplainGatePrompt(replaces the default task); both can be set per project.
Changed
- BREAKING:
trustedProjectsinpi-verdict.jsonis replaced by an interactive project trust prompt plus a gate-owned trust file,<agentDir>/config/pi-verdict-trust.json({ "trusted": [...], "untrusted": [...] }). A project config (<dir>/.pi/pi-verdict.json,.omp/…on omp) is applied only when its project root is trusted; an interactive session starting in an undecided project asks Trust / Not now / Never. Headless sessions and subagents never prompt and ignore undecided project configs. ExistingtrustedProjectsentries are no longer read, so affected projects prompt once.
Fixed
- jev adapter on pi 0.84: host detection no longer relies on
registerProvider.length(pi's wrapper is now(providerOrName, config), arity 2, so pi was routed into the omp registration branch and every jev classifier call failed closed withNo API provider registered for api: jev-decisions). omp is now recognized by its API object carryingloggerandtypebox; behavior on omp is unchanged.