Skip to content

Releases

v0.25.0

Choose a tag to compare

@fuji97 fuji97 released this 31 Oct 03:10
v0.25.0
df68e27

Added

  • Tern-native UI: when omp runs inside Tern with the Tern Surface Protocol (TSP) live (TUI.nativeRendering, detected at session_start through a probe widget factory; PI_TUI_NATIVE=0 opts 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 medium pi-verdict.ask dialog titled with the question: an origin badge (Classifier, Rule, Reason, or the failure label) next to the reason, a Protected path badge, the code view, a meter pair for the jev probabilities and confidence (text line without meter support), the explanation card, a rule separating the context from the question, and radio cards for the choices (the host theme's radio glyphs, ◉ / ○ by default, filled and accent-colored when chosen, with a one-line explanation under each label; click selects, double-click confirms). A Reason for No field (Clarification in the grant dialog) appears under the card only while No is selected; the button bar holds Details and Explain… 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, /verdict and /automode-setup menus become a Tern prefs sheet (scope as a segmented row, pages as navigation, number steppers for percent rows, an Apply action for confirm rows, two-step removals); the /rule progress 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, the verdict_settings confirmation, the /grants list and revoke confirmations, the /rule review and the root-rule review each use the presentation that suits them, with the host ui.select / ui.confirm as the unchanged fallback: confirmations (settings change, grant revoke, /verdict-debug steps) are small dialogs whose choices are buttons, the two rule reviews are medium dialogs with buttons, and the project trust prompt, the /grants list and the /verdict-debug picker are omp's picker sheet (a radio list in a medium dialog on hosts without picker); the trust prompt uses the cards layout 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-form text, md, card, kv and code nodes 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 own omp.advisor.note role (omp.plan.body for the explanation md), 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). The ui.custom calls for the ask dialog, the menus and the choice screens pass { overlay: true } only while TSP is live; medium dialogs add overlayOptions.width: "60%", which omp maps to an undeclared md that floats at every pane width, where a declared md or lg fills panes narrower than 100 columns.
  • /verdict-debug [all|<screen>] (always registered, but refuses with a notification unless debug is on — --auto-mode-debug or PI_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 host select/confirm elsewhere). 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-memory MenuController covering every row kind), progress (the /rule panel stepped by hand), footer (7 variants), status (3 rows) and label (4 variants, shown in a temporary widget, never as a transcript message). A bare call opens a picker (all or 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 with ctx.ui.notify. openRuleProgress gained an optional step hook (Next button / Enter) that only this command passes; /rule and /automode-setup are unchanged. Internal extractions with no behavior change: grantLine, publishFooter, showStatusRow.

Changed

  • verdict_settings confirmation dialog (ST-2): the Tern-native dialog is now md wide (was sm), and the before/after values are parsed content instead of pretty-printed JSON, in the native body, the ANSI rows and the host confirm fallback 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 exports settingValueLines; settingsChangeScreen takes before/after values instead of beforeText/afterText.
  • Native Tern surfaces: the remaining monospace text of the dialogs and widgets is now sans (omp.advisor.note via tnProse; new tnHeading uses Tern's omp.sheet.head role for short headings). Ask/grant dialog: the code header, the verdict/confidence meter labels (a shared 10ch minimum 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/Action are prose rows with muted labels instead of a kv grid, and Before/After are sans headings above the monospace JSON. /rule review: the Matches … and Stopped 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 ANSI render() 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 generic Reason badge and shows a Request heading with the request text, a Protected path/Protected paths heading with a muted access label (read/write/full) and the monospace path per entry, a Danger rule/Danger rules heading with the monospace rule id and its reason, and the lifetime sentence as a muted footnote; ask dialogs keep their origin badge. ApproveDialogSpec.extraLines is replaced by the structured scope (GrantScope), with grantScopeLines producing the plain lines for the ANSI body and the confirm() fallback.
  • /grants revoke confirmation (GR-6): the Tern-native dialog is now md wide (was sm) 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 shared grantScopeNodes). "Revoke all" shows one card per grant headed by its id with the same sections; the buttons read Revoke <id> / Revoke all <n> and Keep (was Revoke / Cancel; the cancel choice id is now keep). The host confirm fallback text and the ANSI lines are unchanged. grantRevokeScreen now 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, /grants revoke, rule reviews, debug steps) is inset from the overlay frame by tnFramed: a spacer above and below and an empty minimum-width spacer on each side of the content (TSP has no padding prop, an overlay role does not pad, and a head-less card draws a ring, so none of those is used). Pickers and the prefs sheet are drawn by Tern and are unchanged; the ANSI rows are unchanged.
  • /rule review dialog (Tern-native): the buttons fit one row (Save…, Edit, Amend…, Cancel); Save… opens a native scope picker (new role pi-verdict.rule-save, ruleSaveScreen, exported RULE_SAVE_CHOICE) listing Session only / Project / Global with the target path as detail, and Escape or Back returns 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 muted Saves to <scope> · <file> body line. ruleReviewScreen/rootRuleReviewScreen take options: { id, short? }[] (and saveTo for the root rule); the host select fallback and the ANSI lines keep the long option strings. /verdict-debug gains the rule-save screen and its rule-review sample includes a long call.
  • Native Tern verdict label (PR-7): the pi-verdict.label row no longer lets the jev probability meter grow to the full row width (Tern's bar meter is flex: 1 1 60px); both meters carry max: { w: "22ch" } (LABEL_METER_MAX_W), and a second compact accent meter conf <n>% now follows the probability meter in the same row, showing the jev confidence already stored in the label...
Read more

v0.24.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 29 Oct 17:16

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

Choose a tag to compare

@fuji97 fuji97 released this 22 Oct 22:59
v0.22.1
13c27c4

Fixed

  • omp judge role assigned to a jev model never loaded (model role "judge" = "…" unavailable, gate fell back to the session model): the role selector was resolved through ctx.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 a provider/id registry lookup with a configured-auth check, and omp's OpenRouter-hosted jev judge models (openrouter/typesafe/jev-*, api openrouter-decisions) are callable: completeForClassifier posts to the openrouter decisions endpoint with the model's own id as wire model (streamDecisions gained an optional wire argument). Other judge-kind models (native judgment runner, typesafe/jev-* direct) are still skipped with one warning, and on the verdict-fallback / explain-gate roles count as unavailable (CL-2).

v0.22.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 19 Oct 15:49

Added

  • Gate briefing (GR-7): every agent turn the before_agent_start hook 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 effective deny and ask danger rules, the non-allow path tiers, the denyPaths count (never the paths, PP-6) and the .omp gate, and tells the agent not to retry or rephrase a blocked call but to request a verdict_grant (subagents report the need instead). Omitted when the mode is off or for a subagent while subagentGate is off; 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 (off is unchanged; /automode and --verdict-mode take auto|unattended|supervised|off, and the cycle shortcut order is auto → 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 to auto, which is stricter than unattended and supervised; a former mode name given to --verdict-mode or /automode warns renamed to <new> and changes nothing. Migration: edit pi-verdict.json and any per-session override files by hand. The block tag yolo-retry is now unattended-retry, the reason suffixes read (unattended: no human can confirm this — …) and (supervised: this would have been denied — your call), the audit mode field and the cascade trigger yolo-ask carry 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, 👁 SUPERVISED and ⚠ AUTO OFF · ungated (compact style 🛡 auto, 🌙 unattended, 👁 supervised, ⚠ auto off · ungated, replacing the ●/○ bullets; Nerd Font style uses \uF186 and \uF06E for the moon and eye), and the prompt status-bar chip shows 🛡 AUTO, 🌙 UNATT, 👁 SUPV and ⚠ OFF · ungated.

v0.21.1

Choose a tag to compare

@frapetti-dev frapetti-dev released this 17 Oct 17:09

[0.21.1] - 2026-10-17

Changed

  • Agent guidance for built-in rules: the verdict_settings and verdict_grant tool descriptions and their promptGuidelines now tell the agent to check a built-in rule's current action with verdict_settings get (key dangerRules or pathRules) before saying it blocks an operation, and to request a verdict_grant listing the rule ids in rules (or protected paths in paths) instead of refusing or asking the user to run the step. Text only; gate behavior, GRANT_BLOCK_HINT and block reasons are unchanged.

v0.18.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 08 Oct 16:35

v0.18.0

v0.16.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 02 Oct 13:04

Added

  • Footer status redesign and footer config key ("full" default | "compact" | "off"; editable from /verdict, project-overridable, invalid values warn and fall back to "full"): the footer used to show only auto 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|enforce fallback model), per-session verdict counters (final pipeline verdicts of root-session calls; reset by SessionState.reset(), kept across /verdict saves; 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 lacks bg/getBgAnsi (e.g. omp). The footer refreshes after every adjudicated tool call, on /verdict saves and on model_select, and never contains command or path text (ADR-0002). Pure renderer exported as renderFooter.
  • Live classifier status: while a gray-zone model call runs (the primary classifier or the classifierFallbackModel cascade), the tool_call handler 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-free AdjudicateEnv.onPhase hook.
  • 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 initial Yes highlight and keyboard moves never arm a confirm, so allowing always takes two clicks on Yes). 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's children/terminal.rows/viewportTop and 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), so C:\proj\.ssh\id_rsa, C:\proj\.git\hooks\x, and ~\.npmrc hit their S-rules instead of silently passing the floor; the in-cwd base is now path.resolved before dual-form expansion. User allow/deny regexes see the same POSIX form for file-tool path targets on win32 (a ^/proj/ rule matches C:\proj\a.ts), and bash command strings have backslashes unified to / before denyPaths token extraction so cat C:\dir\file is extracted. POSIX hosts are unchanged. The read via project-local symlink to /etc test is skipped on win32 (no /etc to link to).

v0.14.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 02 Oct 08:50
9a8319f

Added

  • Subagent gate (omp only; pi behavior is unchanged): omp runs subagents in-process with no UI, so every ask inside a subagent used to degrade to deny. New config keys subagentGate ("off" default | "normal" | "auto") and subagentAskTimeoutMs (positive integer ms, default 60000) 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 before subagentAskTimeoutMs (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 consults classifierFallbackModel only for asks the classifier raised, and only an explicit allow from it permits the call — anything else denies, with block tag subagent-auto; protected-path / .omp asks and asks that exist only because of autoDeny: false always 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 with subagent-cancelled without consulting the second model. Root dialogs are now serialized process-wide (omp queues confirm but not custom). With audit: true, subagent asks record subagent: { id, name, resolution: "human" | "timeout" | "auto" }, and second-model resolutions record fallback.triggeredBy: "subagent-ask". Set omp's extensionHandlers.toolCallTimeoutMs to at least subagentAskTimeoutMs + 60000 — a subagent waiting on the root UI does not pause its handler budget.

  • The first-run config template pre-fills a starter tools allowlist (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 (default true): forced .omp gate. Any file-tool path or bash command that touches a .omp directory (path segment .omp, matched on the lexical and symlink-resolved forms; bash also matches .omp as 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 user deny, before denyPaths and user allow (an allow regex cannot skip it). The ask reason carries no path (UI-only detail). grep/find/ls are checked on their own target only. Set false to disable; /verdict now has a gateOmpDir on/off switch (project files can also unset it to inherit the global value).

  • /verdict [user|local] command: edit the list keys of pi-verdict.json (allow, deny, denyPaths, tools, rules) from the TUI — add, edit, and remove entries. user targets <agentDir>/config/pi-verdict.json, local the 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 _hint are 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); × Unset removes 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, write content, edit newText blocks) 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 \uXXXX escapes). When the ask comes straight from jev, a colored bar graphs its allow/ask/deny distribution with a confidence legend, plus a concern: line. RPC mode and hosts without ui.custom keep the plain-text confirm unchanged.

  • jev backend: each decisions request now also carries a typed concern question (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.custom keep 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 is Explain 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 (denyPaths hits and the .omp gate): their path plaintext is UI-only and must not reach a model provider (ADR-0002); No, with explanation… is available there. New config keys explainGateModel (provider/id[:thinking], default: session model; unavailable → session model with a one-time warning) and explainGatePrompt (replaces the default task); both can be set per project.

Changed

  • BREAKING: trustedProjects in pi-verdict.json is 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. Existing trustedProjects entries 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 with No API provider registered for api: jev-decisions). omp is now recognized by its API object carrying logger and typebox; behavior on omp is unchanged.

v0.13.0

Choose a tag to compare

@frapetti-dev frapetti-dev released this 25 Sep 13:33
d28bd50

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

Choose a tag to compare

@frapetti-dev frapetti-dev released this 25 Sep 13:00
981f1b4

What's Changed

  • Migrate to GitHub Package Registry and update documentation by @frapetti-dev in #1

New Contributors

Full Changelog: v0.12.0...v0.12.1