/** * report_tool_issue — automated QA tool for tracking unexpected tool behavior. * * Enabled by default; gated behind PI_AUTO_QA=1 / `dev.autoqa` so a user * who flips the setting off short-circuits injection entirely. * Always injected into every agent (including subagents) regardless of tool selection. * Records grievances to a local SQLite database; never throws. * * Before the first record lands, the user's consent is checked. If they've * never been asked (`dev.autoqa.consent === "unset"`) the process-global * consent handler — wired by `InteractiveMode` to a Yes/No popup — is * invoked exactly once and the decision is persisted. Subsequent calls * (including from subagents) read the cached decision without prompting. * * When the user grants consent, push is automatically active against the * bundled endpoint (`dev.autoqaPush.endpoint`, default `qa.omp.sh`). Each * insert schedules a background flush that POSTs pending rows and deletes * them on HTTP 2xx. `PI_AUTO_QA_PUSH=1` forces push in non-interactive * environments where the consent dialog never fires. Tool execution is * never blocked on the network and never throws. */ import { Database } from "bun:sqlite"; import type { AgentTool } from "@oh-my-pi/pi-agent-core"; import type { Settings } from ".."; import type { ToolSession } from "./index"; export declare function isAutoQaEnabled(settings?: Settings): boolean; /** * Resolver for the user's "share grievances?" consent. * * Return values: * - `true` — user agreed; record + ship for this run and persist. * - `false` — user declined; suppress for this run and persist. * - `null` — user dismissed the dialog (ESC, click-away, …) without * picking an option. The decision is NOT cached or persisted, * so the next `report_tool_issue` invocation re-prompts. * * Persistence is the tool's job (so subagent invocations can persist into * the disk-backed `Settings` instance the host registered alongside the * handler), not the handler's. Implementations live in hosts that have UI * affordances — today only `InteractiveMode`. When no handler is * registered (CLI subcommands, tests, non-interactive runs) consent * defaults to `false` — the explicit "don't collect by default" stance. */ export type AutoQaConsentHandler = () => Promise; /** * Register the consent handler and the persistent {@link Settings} instance * the decision should be written to. Passing `null` clears the handler * (e.g. on `InteractiveMode` teardown). Re-registration is authoritative. */ export declare function setAutoQaConsentHandler(handler: AutoQaConsentHandler | null, persistentSettings?: Settings | null): void; /** Test-only: clear consent cache + handler. Never call from production code. */ export declare function __resetAutoQaConsentForTests(): void; /** * Resolve the user's consent for `report_tool_issue` grievances. * * Precedence (highest first): * 1. Process-global cache (set on first successful resolution). * 2. Persistent setting (`dev.autoqa.consent` on the supplied `Settings`). * 3. Persistent setting on the registered host `Settings`. * 4. Consent handler popup (single-flight; persists the answer). * 5. Default-deny when no handler is registered. * * Never throws — handler errors degrade to "denied for this call" without * caching, so a subsequent invocation can re-prompt instead of being * permanently locked into the false branch. */ export declare function resolveAutoQaConsent(settings: Settings | undefined): Promise; export declare function getAutoQaDbPath(): string; /** * Open (or return the cached handle for) the auto-QA SQLite database at * `~/.omp/agent/autoqa.db`. Idempotently runs schema creation, the * `pushed`-column migration, and index setup so every consumer — tool * execute path, manual `omp grievances push`, future debug scripts — * sees the same prepared schema. Returns `null` only on a hard open * failure (filesystem permissions, etc.); a missing file is created. * * Exported because the `omp grievances` CLI handlers need the migrated * handle too — having a second `openDb` in the CLI led to the column * never being added on the manual-push path. */ export declare function openAutoQaDb(): Database | null; export interface FlushResult { pushed: number; ok: boolean; skipped?: boolean; } /** * Optional per-flush controls. Used by `omp grievances push` to surface * progress to a TTY and to skip the user-facing consent gate (manual * pushes are the user's explicit intent, not a side effect of a tool call). */ export interface FlushOptions { /** * Skip the `dev.autoqa.consent === "granted"` gate in * {@link resolvePushConfig}. Endpoint configuration is still required. * Reserved for explicit user-driven pushes (CLI `grievances push`, * future debug recipes); never set from the tool's auto-flush path. */ bypassConsent?: boolean; /** * Fires once at the start of the loop with the snapshot count of * unpushed rows. Subsequent inserts won't be reflected (the count is * a planning hint for progress reporters, not a live total). */ onStart?: (totalUnpushed: number) => void; /** * Fires after every successfully shipped batch with the running pushed * count. Reporters compare against the `totalUnpushed` they saw in * `onStart` to advance their bar. */ onProgress?: (pushedSoFar: number) => void; } /** Test-only: clear single-flight + cooldown state. Never call from production code. */ export declare function __resetAutoQaFlushStateForTests(): void; /** * Flush queued grievances to the configured backend. * * Single-flight: concurrent callers share the in-flight promise. After a * failed push, retries are skipped for {@link FAILURE_COOLDOWN_MS} ms. * Never throws — all errors are caught and routed to the logger. */ export declare function flushGrievances(db?: Database, settings?: Settings, options?: FlushOptions): Promise; export declare function createReportToolIssueTool(session: ToolSession, activeBuiltinNames?: readonly string[]): AgentTool;