omp packages

@ian-pascoe/pi-advisor0.8.4

Background review and attributed corrective advice for Pi agent sessions.

GitHub Actions Published 19 hours ago MIT ~1.7K downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

Pi Advisor

@ian-pascoe/pi-advisor reviews a Pi agent's completed work and surfaces concise, attributed findings for material problems and worthwhile low-risk cleanup.

Requires Node >=22.19.0 and Pi >=1.1.0.

Install

pi install npm:@ian-pascoe/pi-advisor
# or from this checkout
pi -e ./packages/pi-advisor/src/index.ts

Advisor is disabled by default. Configuration precedence is session, trusted project, global, then package defaults. Settings use Pi's native settings files and session entries; no separate database or transcript store is created.

Commands

/advisor
/advisor status
/advisor on|off [--global|--project]
/advisor inherit [key] [--global|--project]
/advisor set <key> <JSON> [--global|--project]
/advisor prompt [--global|--project]
/advisor set includeSubagents true
/advisor set model "provider/model-id"
/advisor set maxFindingsPerReview 4
/advisor set maxNitsPerRequest 3
/advisor set seedBudgetTokens 40000
/advisor set reviewEvery "request"
/advisor set maxSessionTokens 150000
/advisor set maxToolResultChars 2000
/advisor set anthropicLongCache true

Argument autocomplete suggests command names, settings keys, and valid trailing scope flags.

In the interactive TUI, /advisor alone opens a settings menu built from Pi's native settings list, like /settings. A Scope row chooses where edits are written (session, trusted project, or global), and each setting shows its effective value with any non-default source. enabled, includeSubagents, anthropicLongCache, and thinkingLevel cycle the selected scope's own value in place (inherit → on → off, or through each thinking level); while that scope has none, the row shows what it inherits, such as inherit (on · project), and a value overridden by a higher-precedence scope is marked. Model, tools, numbers, and the prompt open submenus, and the prompt uses Pi's own editor component. Every edit is validated and applied immediately; a paused Advisor shows its reason and a Resume row. Closing the menu records one status entry listing the changes it applied, or nothing if none were. Without the TUI, /advisor records the status entry instead.

/advisor status and the other subcommands each record a status entry in the transcript. Collapsed, it shows state, model, backlog, usage and cost, the number of Reviews with their running cost and the last Review's cost, unavailable tools, and the last error; expanded, it lists every setting with its source and each watched Child Agent. A mutating command's entry starts with the changes it applied. While enabled, the footer shows ● advisor on when idle, ● advisor reviewing/consulting with any backlog N, or ! advisor paused, plus Child Agents that are reviewing or paused. Interventions and status entries use Pi's custom-message box with a bold Advisor label; an Intervention's severity is a coloured word (nit, concern, blocker), and Child Agent findings add the agent ID. advisor_ask renders as Pi's built-in tools do: a lowercase advisor_ask header with the question, a Markdown answer collapsed to ten lines with Pi's expand hint, and Pi's Elapsed/Took footer. Warnings and errors read Advisor: ....

Interventions render with their severity and Advisor attribution. Long Nits collapse until expanded; Concerns and Blockers always show in full.

On-demand consultations

When Advisor is enabled, the main agent can call advisor_ask({ message }) for analysis or a second opinion. The call waits for the current Review, then returns plain Markdown before any further Review Backlog is processed. Follow-up calls continue the same private Advisor Session.

A Consultation receives only observed context that has not already been supplied to that Advisor. It does not reduce the Review Backlog, create an Intervention, or trigger a Corrective Turn. Its ordinary tool call and result remain in the observed transcript for later Reviews.

The tool is absent while Advisor is disabled. A paused Advisor keeps it visible so the call can report the pause reason and recovery commands. User cancellation stops only that Consultation; an inference, authentication, deadline, or investigative-tool failure pauses Advisor. With Pi's built-in codemode active, scripts can call it too; under codemode.mode: "only" its declaration is hidden and it is reachable only through codemode scripts.

Consultation authorizes analysis and investigation, not implementation or other side effects. The configured Advisor Prompt remains authoritative. Tool Grants still expose each granted tool's full native interface, so exclude mutating tools when a prompt-level boundary is insufficient.

prompt opens Pi's native editor and replaces the whole Advisor Prompt. inherit removes an override at the selected scope. Invalid keys and values are rejected. Lists, including allowedTools, replace the inherited list rather than merge. catchUpThreshold accepts any positive safe integer or "off"; reviewTimeoutMs accepts 1–2,147,483,647 milliseconds (the native timer range); maxFindingsPerReview accepts an integer from 1 through 32; maxNitsPerRequest accepts any non-negative safe integer, where 0 delivers no Nits; seedBudgetTokens and maxSessionTokens accept any positive safe integer or "auto"; maxToolResultChars accepts any positive safe integer; reviewEvery accepts "turn", "request", or any positive safe integer number of turns; anthropicLongCache accepts true or false.

advisor_ask declares MCP-style annotations (read-only, non-destructive, idempotent, closed-world) that Pi reports through pi.getAllTools() for permission extensions; Pi does not send them to model providers. They describe the consultation itself, not the tools granted to the Advisor. The Advisor Session's internal advisor_report tool, which extensions inherited by that session can see, declares non-destructive, closed-world annotations and is not read-only, because it records the Review's findings.

Defaults and access

Option Default
Enabled false
Watched sessions Main only
Thinking level high, independent of the observed agent
Allowed tools read, grep, find, ls
Catch-up threshold 3
Review deadline 120 seconds
Investigative calls per Review 8
Findings per Review 4
Nits per request 3
Context Seed budget auto (¼ of the Advisor model's context window, at most 50k)
Review Cadence (reviewEvery) turn
Advisor Session size auto (½ of the Advisor model's context window, at most 100k)
Tool-result text cap 4,000 characters (maxToolResultChars)
OpenAI prompt cache retention 24 hours on Advisor requests (openai provider)
Anthropic 1h prompt cache false (anthropicLongCache)
Automatic Corrective Turns 1 per request/task

A Tool Grant names tools; it does not sandbox their full native interfaces. Explicitly granting bash, for example, permits any command, including mutations. Unavailable names are ignored and reported; status reflects the Advisor Session at that moment, so a granted tool that registers later, such as an MCP tool after its server connects, is reported until it does. Advisor never autogrants missing tools. Granted tools with codemode or deferred exposure (such as tools from Pi's built-in MCP) are not declared to the Advisor model; they stay callable from granted codemode scripts, and a granted tool_search can declare them, as in the observed agent. Granting codemode lets the Advisor script its other granted tools; it inherits the observed codemode.mode, so under "only" the Advisor calls advisor_report from a script. It also inherits whether scripts get the models API: Minimal Subagents Child Agents have none, so neither do their Advisors.

The Advisor inherits each observed agent's model unless configured otherwise. An observed model change rebuilds the Advisor Session only when the model is inherited; an Advisor with its own model keeps its session. The design notes explain why the inherited case reseeds. Its thinking level is not inherited: unless thinkingLevel is set in some layer (session, project, or global), it is high whatever the observed agent uses. Review work gains finding quality from high reasoning, while reasoning tokens bill at the output price, so following an observed agent at xhigh or max adds cost for little benefit; a fixed default also means an observed thinking change never discards the Advisor Session or an in-flight Review. Set thinkingLevel (for example low or medium to cut output cost) to override it; explicit values and layer precedence work as for every other setting, and /advisor inherit thinkingLevel returns to the default. The high default was chosen on this rationale, not from a measured finding-quality evaluation. One root policy can cover current and future Minimal Subagents descendants when enabled.

Sessions and Context Management

Each watched agent has a private, persisted native Advisor Session with fresh, session-bound extension resources. Reviews reuse that session until context or configuration changes require rebuilding. Pi's enabled builtin:<name> extensions are recreated in their observed order: codemode, tool-search, and mcp from Pi's exported factories, and llama.cpp from the installed Pi package's shipped extension file, retaining their metadata and fresh provider state. Built-ins disabled with -builtin:<name> stay absent. This is a built-in recreation recipe, not general inline-factory support; a host factory registered under a built-in name is rejected. Other opaque inline or custom extension resources that cannot be safely recreated are reported as unsupported and pause the Advisor; they are not silently reused or omitted. The Advisor's session state remains separate from the observed agent's state. The Advisor's own system prompt carries no skills catalogue, so skill changes in the Observed Agent's session no longer churn it.

Reviews and Consultations receive Review Evidence: what the observed model saw, not Pi's stored records. Messages keep their role, text, reasoning text, tool calls with arguments, tool-result text with error status, and image attachments; replay signatures, display-only tool details, provider metadata, and native IDs are omitted. Each tool call and its result share a short reference. Redacted reasoning and responses that errored, aborted, or hit the output limit are marked. The first Review or Consultation in an Advisor Session carries a Context Seed: the observed system prompt and each observed tool's name and one-line summary (not its full description or schema), plus the current conversation. Later Reviews and Consultations add only new messages.

Each tool result's text is capped at maxToolResultChars (default 4,000 characters), in the Context Seed and in every incremental update: a longer result keeps its first and last half-limit around a marker, [… N characters omitted from this tool result; the full result is in the observed session file <path>], so granted tools such as read or grep can recover it. The result keeps its short reference, tool name, and error status, so a finding can still cite it. Local sessions showed tool results as about 45% of Review Evidence characters (median 657, 95th percentile about 8k, 99th about 24k), and the Advisor pays for each as a cache write when supplied and as a cache read on every later Review until compaction; a 4k cap saves about 16% of evidence characters (2k, 24%; 8k, 8%). The cap is applied before the seed is fitted to its budget and before the calibrated estimate measures new evidence, so the budget and the Token calibration both describe the capped evidence the Advisor receives. User messages, assistant text, and reasoning are never capped. Capping is a pure function of the message and the setting, so projecting the same messages again gives byte-identical evidence. Changing maxToolResultChars, like any configuration change, rebuilds the Advisor Session from a new Context Seed capped at the new value, and findings still pending are retracted. Guardian shares the projection but does not cap tool results.

The current conversation is already bounded by the observed session's native compaction, but can still be very large, so the Context Seed is fitted to seedBudgetTokens, counted in the Advisor model's reported tokens. Pi's chars/4 estimate of the seed JSON the Advisor receives, plus Pi's per-image estimate, undercounts Review Evidence by about 1.7–1.9× on Claude models, so Advisor scales the estimate by a per-model factor (Token calibration). The first Review or Consultation always sees the Observed Setup; the original request; and the newest turn with the request that prompted it. The original request is the first message the user typed into the current context; after compaction it is the compaction summary, which records the earlier goal, plus the first message the user typed after it. Compaction summaries, delivered Advisor findings, other extension messages, and ! command output are never taken for a request. The rest of the budget holds the newest turns that fit, oldest first. A turn is one message, or an assistant message with all of its tool results, so a tool call is never separated from its result. Text that alone exceeds the remaining budget is shortened with a marker. When messages are omitted or shortened, the seed lists the positions, in the observed context, of the messages it keeps and names the observed session file, so granted tools such as read or grep can inspect the full messages. The Advisor's own context_history covers only its private session. Later Reviews still add only messages newer than the seed; omitted messages are not sent later. auto, the default, uses a quarter of the Advisor model's context window (Pi's 128k fallback when a model declares none), at most 50k tokens: the seed is resent with every inference of the first Review and stays in the Advisor Session, so the rest is left for the Advisor Prompt, investigation, and later Reviews, and an auto seed always fits within half the auto maxSessionTokens in reported tokens. Models with windows above 200k tokens get the 50k ceiling rather than a quarter of their window. The ceiling is half the auto Advisor Session cap, and gives up verbatim recall of an older conversation for a cheaper first Review: omitted messages stay reachable through the granted tools. An explicit budget is capped at the Advisor model's context window.

Context Management is optional. If loaded and its tools are granted, context_notes, context_history, and context_rollover operate on the Advisor's private context. If any of those three tools is excluded, the Advisor pauses before reviewing. There is no autogrant or hook bypass.

Review Cadence and cost

Each Review is a new prompt in the same private Advisor Session, so its input is the whole Advisor Session so far: mostly prompt-cache reads, but billed on every Review. Two settings bound that cost.

reviewEvery sets the Review Cadence: when Reviews run. Whatever the cadence, a Review covers every turn not yet reviewed, and only messages the Advisor has not yet seen are added. When those messages would exceed seedBudgetTokens (in reported tokens, scaled by the same factor), as a coarse cadence can gather or, under any cadence, a single very large turn, Advisor instead rebuilds the Advisor Session from a Context Seed within that budget. Like any rebuild, this withdraws earlier Concerns and Blockers that were queued to steer the observed agent but not yet consumed; findings already in its context stay.

  • "turn", the default, reviews after every turn (one model response and its tool batch) that is not read-only. Findings arrive soonest, and Concerns and Blockers can steer the agent while it still runs; cost grows with the number of turns.
  • N reviews after every N turns and again when the request completes, for any turns left over.
  • "request" reviews once when the request completes: the agent's run has ended, including its steering and follow-up messages, and Pi will not retry it automatically. This is the cheapest cadence, but findings arrive only after the agent has finished, so a Concern waits for the next continuation and a Blocker can only prompt a Corrective Turn.

A read-only turn is one whose tool calls are all read-only: Pi's built-in read, grep, find, and ls, or a tool whose definition carries readOnlyHint: true, such as LSP queries, web search, subagent_wait, and terminal_wait. bash is never read-only, even for a command that only reads, and neither is a turn that also calls any other tool, a tool that is not registered, or that makes no tool calls. Under "turn", a read-only turn joins the Review Backlog without starting a Review. The next turn that is not read-only, a turn with a failed tool call, or request completion reviews the backlog, so the Review still sees the exploration. This saves the Reviews of pure exploration, which rarely yield a finding alone. The trade-off is latency: a Concern about a misdirected search or a wrong file read arrives with the Review after the exploration ends, not one turn after it. N and "request" already batch turns and count read-only turns like any other.

Under N or "request", a turn whose tool call failed still starts a Review at once, as it does under "turn". A Catch-up Wait waits only for a running Review: turns waiting for their Review, whether for the next cadence point or because they only read, count toward the backlog but never start a Catch-up Wait themselves, and the wait ends when that Review finishes. Headless final drains and Minimal Subagents task completion review whatever turns remain, waiting up to one Review deadline (reviewTimeoutMs, 120 seconds by default) rather than the 30-second Catch-up Wait ceiling, so a Review covering a whole request can finish. A Review already running when the observed session compacts still finishes and delivers its findings; the next Review rebuilds the Advisor Session from the compacted context.

maxSessionTokens bounds the Advisor Session itself. After a Review, when the Advisor Session's context exceeds the cap, Advisor compacts it with Pi's native compaction (compact(), as /compact does), which summarizes older Advisor history with the Advisor model and keeps Pi's compaction.keepRecentTokens of recent history; a cap below keepRecentTokens acts as keepRecentTokens. The summary is asked to keep the observed request, findings already reported, open concerns, and what the Advisor verified. Observed evidence continues incrementally afterwards, and delivered and deferred findings are kept by Advisor itself. Compaction is one extra Advisor model call that re-reads the history it summarizes without cache hits, counted in that Review's cost. It runs after the Review's findings are delivered, under its own deadline of reviewTimeoutMs. If it fails or times out, for example because an inherited extension cancels it, Advisor does not pause: it discards the Advisor Session, and the next Review starts a new one from a Context Seed. Pi declining because the whole Advisor Session is recent history it keeps is not a failure. auto takes half the Advisor model's context window (Pi's 128k fallback when a model declares none), at most 100k tokens: room for an auto Context Seed plus as much again for later Reviews, so a full seed alone never forces compaction (seed budgets count reported tokens, so this holds on models whose tokens outnumber Pi's estimate), and far below Pi's own threshold (the window less compaction.reserveTokens), where each Review would re-read almost a full window. The 100k ceiling exists because the per-Review cost follows the Advisor Session's size, not the model's window: with a 1M-token window, half the window is about 500k tokens, where an Advisor Session was measured costing at least $0.12 for each Review that found nothing, and $0.20–0.34 for typical ones, from cache reads alone, more than the observed agent cost over the same period; cache reads were the largest Advisor cost (44% of $33.72 across 338 measured responses). Ceilings count reported tokens, which the Token calibration below makes possible: against Pi's estimate alone, 100k would have meant about 175k real tokens for a seed. The ceiling trades cost against recall. Compaction is a separate, uncached call that summarizes each Advisor token about once, so its cost per Review barely depends on the cap, while the read cost of every Review grows with the session. With Pi's default compaction.keepRecentTokens of 20k, about 5k of new evidence per Review, cache reads at 0.1× the input price, and a summary costing about 15k input tokens, the modeled cost per Review is about 11k input-token equivalents at 200k, 6–7k at 80–100k, and lowest near 60k. The cost of 100k is more frequent compaction, and older evidence reaching the Advisor only as a summary instead of verbatim; the granted tools can still read the full observed session. A higher cap, up to the window, compacts less often and recalls more, at a higher cost for every Review. When Context Management is loaded in the Advisor Session, its Rollover manages that context instead, and maxSessionTokens is not applied.

/advisor status shows the cost of the last Review, including any compaction it ran, and the running total of Reviews since the session started or Advisor was reloaded, from the native usage the Advisor Session records. Reviews that failed or were cancelled after prompting the Advisor count, since they were billed; Consultations do not. Its tokens and cost instead cover the current Advisor Session, including Consultations, and restart when that session is rebuilt. Unknown cost, such as for a model without prices, is shown as unknown; a Review that reported no usage on a priced model costs $0. Expanded, status also shows each Child Agent's Review cost.

Prompt cache retention

A provider's prompt cache lasts about five minutes by default, so a Review after a longer idle gap re-reads the whole Advisor Session uncached. Advisor asks for longer retention on its own requests only, as a per-request stream option on the Advisor Session's agent; it never sets the process-wide PI_CACHE_RETENTION, and the observed agent's requests are byte-for-byte unchanged.

  • OpenAI (the openai provider): Advisor requests send prompt_cache_retention: "24h" (prompt_cache_options.ttl on models with explicit cache modes). It costs nothing extra, so it is always on.
  • Anthropic (the anthropic provider): anthropicLongCache (default false) asks for the 1h cache TTL on Advisor requests. A 1h cache write costs 2× the input price instead of 1.25×, so enable it only when Reviews are routinely more than five minutes apart. With false, Advisor requests use the provider default, so a process-wide PI_CACHE_RETENTION=long still applies to them.
  • OpenAI Codex (openai-codex) and other providers keep their default: Pi's Codex adapter sends no retention field, and ChatGPT sign-in rejects the OpenAI one.

Pi's own cache warming (cacheWarming) reads the retention of the request it replays, so it follows these lifetimes.

Token calibration

Pi's chars/4 estimate undercounts Review Evidence, which is JSON-heavy: on Claude models real tokens run about 1.7–1.9× the estimate for a large seed, so a seed fitted to an estimated 100k reached about 175k real tokens and pushed the Advisor Session past maxSessionTokens at once. Advisor therefore measures seedBudgetTokens, and whether new evidence fits it, in the Advisor model's reported tokens. It fits the seed by the estimate scaled by a per-model factor. The factor starts at 2 and follows the Advisor's own Reviews and Consultations: each prompt that added at least 2,000 estimated tokens compares the input tokens its first response reported, less the Advisor Session's size before the prompt, with Pi's estimate of the prompt. Before a session's first response Pi counts only its messages, so Advisor adds Pi's estimate of the system prompt and active tool definitions to that size; afterwards the previous response's reported usage already includes them. The factor is that ratio rounded up to a quarter, changing only when the ratio moves clearly past it, so a steady model keeps its evidence window, and with it the prompt cache, stable. The same helper, @ian-pascoe/pi-utils/token-calibration, calibrates Guardian's evidence budget. Advisor keeps its factors in memory for the life of the observer, not in the session, so after a reload the first seed uses the fallback of 2 again; a smaller fallback would overshoot dense models, and a larger one only shrinks a seed until the first Review reports usage.

Scheduling and safety

Reviews combine completed observed turns (one model response plus its tool batches), as often as reviewEvery allows, and use bounded catch-up waits of at most 30 seconds. Each Review has its own deadline, investigative-call budget, and configurable finding limit. One terminating report returns findings in severity order. Formatting-equivalent findings retain their highest severity within a Review; later escalation from Nit to Concern to Blocker remains deliverable. Distinct Concerns are delivered together when eligible or retained together for re-evaluation during the three-turn cooldown.

Finding quality

Each finding must name a concrete defect in work the observed agent has already done and cite its evidence: the short ref that Review Evidence gives each tool call and its result, or a short verbatim quote. Advice about what to do, test, or say next belongs in a consultation (advisor_ask), not in an Intervention. The default Advisor Prompt asks for this, and advisor_report requires an evidence object with refs, a quote, or both on every finding, so a replaced Advisor Prompt keeps the requirement. Advisor drops a finding that cites no evidence, or cites a ref it never supplied to that Advisor Session, and tells the Advisor which findings it dropped; quotes are not checked against the evidence. A report in the older single-finding form cites no evidence, so its finding is dropped too. An advisor_report call that does not match the report schema, such as a finding without evidence, is rejected with the reason so the Advisor can correct it; after two rejected calls the Review ends without findings rather than retrying until its deadline, and Advisor is not paused. Delivered Interventions keep their evidence in the journaled message details; the observed agent sees only the severity and message.

A Review judges the turns completed when it started. If the observed agent completes more turns before the Review's findings are delivered, those findings are superseded: they may already be fixed or explained, so none of them is delivered. Instead, the next Review receives them alongside the newer turns, re-validates them, and reports again only those that still apply; this adds no model call. Under reviewEvery: "turn" that Review is due at once, even if the turns since were read-only: withheld findings make the backlog due, so a finding about an edit is not held until the next edit or the request's end. Under N or "request" it waits for the next cadence point, at the latest the Review when the request completes, which normally has no newer turns to supersede it (unless a new request has already started). Findings are withheld this way at most once. If the agent also outruns a Review that was given withheld findings, all of that Review's Concerns and Blockers are delivered anyway, including ones it reports for the first time, since it judged them against newer turns, and all its Nits are dropped. This is a deliberate starvation guard: it is applied per Review, so it holds even when the re-validating Review rewords a finding or cites newer references. A withheld Concern that the next Review defers for the Concern cooldown keeps that mark. So findings are never held back for more than one extra Review, however fast turns arrive. Superseded findings still waiting when Advisor is disabled, reconfigured, resumed after a pause, or moved to another branch or session are dropped with the rest of its invalidated review state, and so are those whose re-validating Review does not finish within a headless or Child Agent final drain.

maxNitsPerRequest (default 3) limits the Nits delivered per request; further Nits are dropped. Concerns and Blockers are never capped. A request runs from a user prompt (or a Minimal Subagents task) through its completion, including the Review at its completion and any Corrective Turn; the count restarts with the next request. Superseded Nits are kept for re-validation only while the request has room for them. /advisor status shows how many findings await re-validation and how many were dropped since the session started or Advisor was reloaded: Nits over the cap, findings with missing or unknown evidence, Nits from a re-validating Review that was superseded again, and Reviews ended by invalid reports. The Nit count follows the selected branch, so it survives a configuration change within a request.

Nits are non-interrupting and enter context at the next natural step boundary without starting a Corrective Turn. Running work receives native steering for eligible Concerns and Blockers. A Blocker after normal interactive completion may receive a tracked corrective continuation within the configured budget; aborted, uncertain, deliberately interrupted, and headless-completed work is preserved without a hidden restart. Child corrections stay inside Minimal's owned operation. Headless root shutdown allows only a bounded final drain and never starts hidden corrective work.

Status reports effective settings, sources, review or consultation state, backlog, usage/cost of the current Advisor Session, Review cost, and the last error. Unknown cost is shown as unknown, never zero. Review failure pauses Advisor while leaving the observed agent running; changing configuration, branch, or session identity invalidates stale in-flight work.

Advisor is privileged extension code. Review inherited extensions and granted tools before installing it in a session with access to local files, credentials, or mutating APIs.