import type { ActionGuardConfig, SecurityConfig, SlopGuardConfig } from "./config.js"; import type { PreviousAction, TaskMessage, Verdict } from "./guard.js"; import type { Judge } from "pi-typesafe"; /** One tool call as the agent proposed it. `id` is Pi's tool call id, stable across hooks and retries. */ export interface ToolCallRef { id: string; tool: string; input: Record; } /** What the session says about the call: the latest user prompt, recent messages for scope, and the sibling calls of the same assistant message. */ export interface Conversation { task: string | undefined; /** Scope context only: approval still comes from `task`, never from this history. */ context?: readonly TaskMessage[] | undefined; /** Tool calls in the same assistant message, this one included; they are judged together. */ siblings?: readonly ToolCallRef[] | undefined; /** The agent's own words in that message (or its latest text under this prompt); shared by the siblings. Explains, never authorizes. */ plan?: string | undefined; } export interface InspectOptions { config: ActionGuardConfig; cwd: string; /** Omit to run offline pattern checks only (no consent, no network). */ judge?: Judge | undefined; signal?: AbortSignal | undefined; slop?: SlopGuardConfig | undefined; security?: SecurityConfig | undefined; /** Calls allowed in the previous turn; the regret question about them rides this call's request, never a sibling's. */ previousActions?: readonly PreviousAction[] | undefined; } /** * The Action guard for one session. `evaluateAction` judges a single call; this module owns what spans calls: * * - **Hold and approval.** After a hold in steer mode, the next guarded call that runs under a *new* user prompt asks Jev * whether that prompt approves it. The retry rarely repeats the held string byte for byte (a `command -v` dropped, a * different timeout), so approval is judged against the action itself, never matched against the earlier command text. * A re-hold under the reply keeps the original reference prompt; otherwise the reply could never approve anything. * Without a judge, a reply that reads as approval stands in for the question. * - **Sibling prejudging.** Calls of one assistant message are judged as soon as the first of them is inspected, so their * requests go out together. A judgment is used once and only for the input it was made for; an earlier hook may have * changed the call's input, and a stale judgment is discarded, not reused. Prejudgments do not outlive their turn. */ export declare class ActionGuard { private readonly prejudged; private lastHoldPrompt; private holdPending; /** Judges one call. The verdict's `approvedByUser` means a pending hold was released by the user's reply. */ inspect(call: ToolCallRef, conversation: Conversation, options: InspectOptions): Promise; /** The call inspected under `task` was held: the next inspection under a different prompt asks whether that prompt approves it. */ hold(task: string | undefined): void; /** Siblings that were never inspected (an earlier one terminated the batch, or Esc) do not outlive their turn. */ turnEnd(): void; reset(): void; }