Repository navigation
Releases
v0.25.0
Added
- Tern-native UI: when omp runs inside Tern with the Tern Surface Protocol (TSP) live (
TUI.nativeRendering, detected atsession_startthrough a probe widget factory;PI_TUI_NATIVE=0opts out, there is no config key), every pi-verdict surface is also described as a native node tree and the ANSI rows stay byte-identical everywhere else (pi, other terminals, tests). Ask and grant dialogs become a floating mediumpi-verdict.askdialog titled with the question: an origin badge (Classifier,Rule,Reason, or the failure label) next to the reason, aProtected pathbadge, the code view, ameterpair for the jev probabilities and confidence (text line withoutmetersupport), the explanation card, a rule separating the context from the question, and radio cards for the choices (the host theme'sradioglyphs,◉/○by default, filled and accent-colored when chosen, with a one-line explanation under each label; click selects, double-click confirms). AReason for Nofield (Clarificationin the grant dialog) appears under the card only while No is selected; the button bar holdsDetailsandExplain…on the left and Decline / Confirm on the right, and Decline forwards the typed reason while No is selected (a plain decline otherwise). Pointer events take the same paths as keys, and Explain is still never offered for protected-path asks (PP-6, PP-9)./automode,/verdictand/automode-setupmenus become a Ternprefssheet (scope as a segmented row, pages as navigation, number steppers for percent rows, an Apply action for confirm rows, two-step removals); the/ruleprogress panel, the classifier status row, the omp footer (badges and styled spans, same facts as the ANSI footer) and the transcript verdict label are native nodes; and the project trust prompt, theverdict_settingsconfirmation, the/grantslist and revoke confirmations, the/rulereview and the root-rule review each use the presentation that suits them, with the hostui.select/ui.confirmas the unchanged fallback: confirmations (settings change, grant revoke,/verdict-debugsteps) are small dialogs whose choices are buttons, the two rule reviews are medium dialogs with buttons, and the project trust prompt, the/grantslist and the/verdict-debugpicker are omp'spickersheet (a radio list in a medium dialog on hosts withoutpicker); the trust prompt uses thecardslayout with Trust this project / Not now / Never rows that each carry a one-line detail and the config path in the subtitle, so its text is drawn in Tern's sans face. Free-formtext,md,card,kvandcodenodes are monospace in every dialog unless they carry a Tern role, so the prose of the other dialogs (reasons, notes, radio labels and details, rule rationales, the explanation card) is tagged with Tern's ownomp.advisor.noterole (omp.plan.bodyfor the explanationmd), which draws it in the sans face; a Tern release that renames a role only turns that text back to monospace. Native nodes carry the same text as the ANSI surfaces and no more (PP-6). Theui.customcalls for the ask dialog, the menus and the choice screens pass{ overlay: true }only while TSP is live; medium dialogs addoverlayOptions.width: "60%", which omp maps to an undeclaredmdthat floats at every pane width, where a declaredmdorlgfills panes narrower than 100 columns. /verdict-debug [all|<screen>](always registered, but refuses with a notification unless debug is on —--auto-mode-debugorPI_AUTO_MODE_DEBUG=1— checked when the command runs): previews every pi-verdict screen with fixed sample data through the same code path the real call sites use (native in a live Tern surface, ANSI or the hostselect/confirmelsewhere). Screens:trust,ask(jev meter, "don't ask again" row, Explain with a canned answer),ask-protected(no Explain, PP-6),ask-rule,grant,settings-change,grants,grant-revoke,rule-review,root-rule-review,prefs(one in-memoryMenuControllercovering every row kind),progress(the/rulepanel stepped by hand),footer(7 variants),status(3 rows) andlabel(4 variants, shown in a temporary widget, never as a transcript message). A bare call opens a picker (allor one screen, returning to it after each); footer, status and label variants advance through a Next/Done step screen and the real footer is restored afterwards. Nothing is written (no config, trust store, grants, session overrides or transcript entries) and no model is called; results are reported withctx.ui.notify.openRuleProgressgained an optionalstephook (Next button / Enter) that only this command passes;/ruleand/automode-setupare unchanged. Internal extractions with no behavior change:grantLine,publishFooter,showStatusRow.
Changed
verdict_settingsconfirmation dialog (ST-2): the Tern-native dialog is nowmdwide (wassm), and the before/after values are parsed content instead of pretty-printed JSON, in the native body, the ANSI rows and the hostconfirmfallback alike: a list shows one• <entry>line per entry, an object one<key>: <value>line per key,(empty)and(not set)mark empty and absent values. New exportssettingValueLines;settingsChangeScreentakesbefore/aftervalues instead ofbeforeText/afterText.- Native Tern surfaces: the remaining monospace text of the dialogs and widgets is now sans (
omp.advisor.noteviatnProse; newtnHeadinguses Tern'somp.sheet.headrole for short headings). Ask/grant dialog: the code header, theverdict/confidencemeter labels (a shared10chminimum width so both meters start at the same x) and the meter-less fallback line; the explanation card's head moves into its body as a sans heading. Settings-change dialog:Scope/Key/Actionare prose rows with muted labels instead of akvgrid, andBefore/Afterare sans headings above the monospace JSON./rulereview: theMatches …andStopped by an earlier layer …section heads are sans headings, the command lines stay monospace. Classifier status row, omp footer and transcript verdict label: model, counts and tool name are sans next to the badges. Commands, code, paths and JSON stay monospace; the ANSIrender()output and the text carried by the native nodes are unchanged (PP-6). - Permission grant dialog (GR-2): the body is now structured instead of three equal-weight prose lines. The ANSI rows keep their text, but the lifetime sentence no longer ends in
Mode: <mode>.(the mode never affected a grant's lifetime). The Tern-native body drops the genericReasonbadge and shows aRequestheading with the request text, aProtected path/Protected pathsheading with a muted access label (read/write/full) and the monospace path per entry, aDanger rule/Danger rulesheading with the monospace rule id and its reason, and the lifetime sentence as a muted footnote; ask dialogs keep their origin badge.ApproveDialogSpec.extraLinesis replaced by the structuredscope(GrantScope), withgrantScopeLinesproducing the plain lines for the ANSI body and theconfirm()fallback. /grantsrevoke confirmation (GR-6): the Tern-native dialog is nowmdwide (wassm) and reuses the grant dialog's structured sections (Request,Protected path(s)with access label and monospace path,Danger rule(s)with id and reason; new sharedgrantScopeNodes). "Revoke all" shows one card per grant headed by its id with the same sections; the buttons readRevoke <id>/Revoke all <n>andKeep(wasRevoke/Cancel; the cancel choice id is nowkeep). The hostconfirmfallback text and the ANSIlinesare unchanged.grantRevokeScreennow takes(grants, all, ruleReason)instead of(title, detailLines).- Native Tern dialogs: the content of the ask/grant dialog and of every button or list choice screen (trust prompt, settings change,
/grantsrevoke, rule reviews, debug steps) is inset from the overlay frame bytnFramed: aspacerabove and below and an empty minimum-widthspaceron each side of the content (TSP has no padding prop, an overlay role does not pad, and a head-lesscarddraws a ring, so none of those is used). Pickers and theprefssheet are drawn by Tern and are unchanged; the ANSI rows are unchanged. /rulereview dialog (Tern-native): the buttons fit one row (Save…,Edit,Amend…,Cancel);Save…opens a native scope picker (new rolepi-verdict.rule-save,ruleSaveScreen, exportedRULE_SAVE_CHOICE) listing Session only / Project / Global with the target path as detail, and Escape orBackreturns to the review. Each match and shadowed row shows the tool name as a muted badge followed by the command as inline code cut to one line with…(Tern shows the full text on hover), so a 1000-character call no longer grows the dialog. The root-rule review (/automode-setup) gets the same short buttons (Save,Edit,Regenerate…,Discard) and moves the save target out of the button into a mutedSaves to <scope> · <file>body line.ruleReviewScreen/rootRuleReviewScreentakeoptions: { id, short? }[](andsaveTofor the root rule); the hostselectfallback and the ANSI lines keep the long option strings./verdict-debuggains therule-savescreen and itsrule-reviewsample includes a long call.- Native Tern verdict label (PR-7): the
pi-verdict.labelrow no longer lets the jev probability meter grow to the full row width (Tern's bar meter isflex: 1 1 60px); both meters carrymax: { w: "22ch" }(LABEL_METER_MAX_W), and a second compactaccentmeterconf <n>%now follows the probability meter in the same row, showing the jev confidence already stored in the label...
v0.24.0
Plan-mode grant planning (GR-8): in omp plan mode the gate briefing tells the agent to put a Permission grants section in the plan and request those grants with verdict_grant as its first execution step.
v0.22.1
Fixed
- omp
judgerole assigned to a jev model never loaded (model role "judge" = "…" unavailable, gate fell back to the session model): the role selector was resolved throughctx.models.resolve, which searches chat models only, so no judge-kind model ever resolved (and the "judge model pi-verdict cannot call" skip never ran). Role selectors now fall back to aprovider/idregistry lookup with a configured-auth check, and omp's OpenRouter-hosted jev judge models (openrouter/typesafe/jev-*, apiopenrouter-decisions) are callable:completeForClassifierposts to the openrouter decisions endpoint with the model's own id as wire model (streamDecisionsgained an optionalwireargument). Other judge-kind models (native judgment runner,typesafe/jev-*direct) are still skipped with one warning, and on theverdict-fallback/explain-gateroles count as unavailable (CL-2).
v0.22.0
Added
- Gate briefing (GR-7): every agent turn the
before_agent_starthook appends a## Permission gate (pi-verdict)section to the system prompt (pi string and omp section list both supported) that names the approval mode, lists the effectivedenyandaskdanger rules, the non-allowpath tiers, thedenyPathscount (never the paths, PP-6) and the.ompgate, and tells the agent not to retry or rephrase a blocked call but to request averdict_grant(subagents report the need instead). Omitted when the mode isoffor for a subagent whilesubagentGateisoff; no config switch, and it holds no per-session data besides mode and rules so the prompt stays cacheable.
Changed
- BREAKING: the approval modes are renamed —
default→auto,yolo→unattended,noAutoDeny→supervised(offis unchanged;/automodeand--verdict-modetakeauto|unattended|supervised|off, and the cycle shortcut order isauto → unattended → supervised → off → auto) — and so are the derived config and session keys:defaultDenyThreshold→autoDenyThreshold,defaultAllowThreshold→autoAllowThreshold,yoloDenyThreshold→unattendedDenyThreshold,noAutoDenyAllowThreshold→supervisedAllowThreshold,yoloDenyPaths→unattendedDenyPaths,yoloOmpDir→unattendedOmpDir(MD-1, MD-6). A former mode value or key in a config or session override file is reported at start-up (mode: "yolo" renamed to "unattended" — ignored,yoloOmpDir: renamed to unattendedOmpDir — key ignored) and ignored, never mapped, so the mode falls back toauto, which is stricter thanunattendedandsupervised; a former mode name given to--verdict-modeor/automodewarnsrenamed to <new>and changes nothing. Migration: editpi-verdict.jsonand any per-session override files by hand. The block tagyolo-retryis nowunattended-retry, the reason suffixes read(unattended: no human can confirm this — …)and(supervised: this would have been denied — your call), the auditmodefield and the cascade triggeryolo-askcarry the new names (unattended-ask), and the glossary term "yolo-ask slip" is now "unattended-ask slip" (PR-2). - Mode chips: the footer shows
🛡 AUTO/auto,🌙 UNATTENDED,👁 SUPERVISEDand⚠ AUTO OFF · ungated(compact style🛡 auto,🌙 unattended,👁 supervised,⚠ auto off · ungated, replacing the●/○bullets; Nerd Font style uses\uF186and\uF06Efor the moon and eye), and the prompt status-bar chip shows🛡 AUTO,🌙 UNATT,👁 SUPVand⚠ OFF · ungated.
v0.21.1
[0.21.1] - 2026-10-17
Changed
- Agent guidance for built-in rules: the
verdict_settingsandverdict_granttool descriptions and theirpromptGuidelinesnow tell the agent to check a built-in rule's current action withverdict_settingsget(keydangerRulesorpathRules) before saying it blocks an operation, and to request averdict_grantlisting the rule ids inrules(or protected paths inpaths) instead of refusing or asking the user to run the step. Text only; gate behavior,GRANT_BLOCK_HINTand block reasons are unchanged.
v0.18.0
v0.16.0
Added
- Footer status redesign and
footerconfig key ("full"default |"compact"|"off"; editable from/verdict, project-overridable, invalid values warn and fall back to"full"): the footer used to show onlyauto mode on/auto mode off."full"now renders Nerd Font powerline blocks — gate state, risky settings (floor off,.omp gate off), the classifier model (↺inherited session model,⚠ ↺configured model unavailable so the session model is used,↳ <id>·shadow|enforcefallback model), per-session verdict counters (final pipeline verdicts of root-session calls; reset bySessionState.reset(), kept across/verdictsaves; subagent calls and calls while the master switch is off are not counted), and info badges (≥N%,autoDeny off,subagent normal|auto);"compact"is one plain text line without counters, also used when the host theme lacksbg/getBgAnsi(e.g. omp). The footer refreshes after every adjudicated tool call, on/verdictsaves and onmodel_select, and never contains command or path text (ADR-0002). Pure renderer exported asrenderFooter. - Live classifier status: while a gray-zone model call runs (the primary classifier or the
classifierFallbackModelcascade), thetool_callhandler shows one widget row above the editor (ui.setWidget("verdict", …), e.g.🛡️ verdict: classifying bash via <model>…) and clears it before the verdict is presented, so it never sits next to the confirm dialog; the text is phase + tool name + model id only (never command or path text, ADR-0002), rule-layer verdicts show nothing, root sessions with a UI only (subagent and headless calls never set it), always on with no config key; the pipeline exposes it as the UI-freeAdjudicateEnv.onPhasehook. - Ask dialog mouse clicks: the rich approve dialog now handles left-clicks when the host forwards SGR mouse input (
ESC [ < b ; x ; y M) to the focused component — a click highlights an option row, a second click on the same row confirms it, but only if that row was highlighted by the immediately preceding mouse click (the initialYeshighlight and keyboard moves never arm a confirm, so allowing always takes two clicks onYes). Releases, motion, wheel, non-left buttons, legacy X10 mouse sequences, and clicks outside the option rows are ignored; the row is mapped from the host'schildren/terminal.rows/viewportTopand clicks are ignored when the host exposes none. The dialog never enables terminal mouse tracking itself, and the key-hint line is unchanged: pi 0.84.3 consumes mouse events before components, so this takes effect only on hosts that forward them.
Fixed
- Windows path matching: the path sensitivity floor (S0–S3) and the in-cwd write allowance now match against a POSIX-form spelling of every canonical path form (backslashes →
/, drive letter dropped), soC:\proj\.ssh\id_rsa,C:\proj\.git\hooks\x, and~\.npmrchit their S-rules instead of silently passing the floor; the in-cwd base is nowpath.resolved before dual-form expansion. Userallow/denyregexes see the same POSIX form for file-tool path targets on win32 (a^/proj/rule matchesC:\proj\a.ts), and bash command strings have backslashes unified to/before denyPaths token extraction socat C:\dir\fileis extracted. POSIX hosts are unchanged. Theread via project-local symlink to /etctest is skipped on win32 (no/etcto link to).
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.
v0.13.0
What's Changed
- refactor(security)!: remove self-protection layer (ADR-0001) by @frapetti-dev in #2
Full Changelog: v0.12.1...v0.13.0
v0.12.1
What's Changed
- Migrate to GitHub Package Registry and update documentation by @frapetti-dev in #1
New Contributors
- @frapetti-dev made their first contribution in #1
Full Changelog: v0.12.0...v0.12.1