import { ToolApprovalStore } from "./approval-store.js"; import { type ToolApprovalsManifest, type ToolApprovalsMode } from "./manifest.js"; import { type GateTelemetry } from "./telemetry.js"; /** * Tool approval gate: requires an interactive user approval before * state-changing senpi MCP tools execute. * * OpenClaw v2026.5.x has no declarative "ask" policy for MCP tools (only the * exec tool has `tools.exec.ask`); the supported mechanism is a plugin * `before_tool_call` hook returning `requireApproval`, which the gateway * drives via `plugin.approval.request` / `plugin.approval.resolve` * (decisions: allow-once / allow-always / deny; timeout → deny). Approval * prompts are broadcast to gateway clients holding the `operator.approvals` * scope — the senpi agent-bridge connection — and surface in senpi-web. * * The gated tool set and the defaults (mode, prompt timeout) come from the * checked-in manifest (`tool-approvals-manifest.json`, see manifest.ts) — the * reviewed, fleet-wide control surface. Mode precedence: env * {@link TOOL_APPROVALS_ENV_VAR} > plugin config `toolApprovals` > manifest * `defaults.mode`. * * `allow-always` decisions are persisted per tool via {@link ToolApprovalStore} * (OpenClaw treats them as one-shot for plugin approvals). * * Design doc: docs/mcp-tool-approvals-design.md */ interface LoggerLike { info(...args: unknown[]): void; warn(...args: unknown[]): void; } /** Minimal shape of the OpenClaw plugin API surface the gate needs. */ export interface ToolApprovalGatePluginApi { on(hookName: string, handler: (...args: unknown[]) => unknown, opts?: { priority?: number; }): void; } export type { ToolApprovalsMode }; /** Env var that overrides the `toolApprovals` plugin config and the manifest default. */ export declare const TOOL_APPROVALS_ENV_VAR = "SENPI_TOOL_APPROVALS"; /** * Match a tool-registry name against the senpi MCP server and return the bare * tool name. Covers the embedded-MCP shape (`senpi__create_position`), the * CLI-runner backend shape (`mcp__senpi__create_position`), and openclaw's * server-name dedupe suffix (`senpi-2__…`). */ export declare function matchSenpiTool(toolName: unknown): string | null; export declare function isToolApprovalsMode(value: unknown): value is ToolApprovalsMode; /** * Resolve the gate mode: env {@link TOOL_APPROVALS_ENV_VAR} > plugin config * `toolApprovals` > manifest `defaults.mode`. Unrecognized values warn and * fall through to the manifest default rather than silently disabling the gate. */ export declare function resolveToolApprovalsMode(params: { pluginConfig?: Record; env?: NodeJS.ProcessEnv; logger?: LoggerLike; manifest?: ToolApprovalsManifest; }): ToolApprovalsMode; /** * Human sentence for a senpi trade tool call, mirroring senpi-web's own * per-tool phrasing (its `getDescription` registry). * * Why the plugin builds this rather than leaving it to the UI: senpi-web keys * that registry by the **bare** tool name and feeds it the call `args`, but for * plugin approvals openclaw sends the prefixed registry name * (`senpi__create_position`) and has **no `args` field at all` in * `PluginApprovalRequestParamsSchema` (additionalProperties: false). So the UI * takes its fallback branch and renders `description` verbatim, with no args * block — meaning this string is the *only* place the user sees what they are * approving. It must therefore carry the numbers, not just the intent. */ export declare function describeSenpiToolCall(tool: string, params: unknown): string | undefined; /** * Prompt body: the human sentence when we know the tool, plus the compact raw * args so the exact numbers are always visible (senpi-web renders no args block * for plugin approvals — see {@link describeSenpiToolCall}). Capped to the * gateway's description limit, which rejects (not truncates) anything longer. */ export declare function buildApprovalDescription(tool: string, params: unknown): string; export interface RegisterToolApprovalGateOptions { api: ToolApprovalGatePluginApi; mode: ToolApprovalsMode; /** Base plugin state dir; the store file lives directly under it. */ stateDir: string; logger?: LoggerLike; /** Injectable for tests; defaults to the checked-in manifest. */ manifest?: ToolApprovalsManifest; /** Injectable for tests; defaults to the real event/metric planes. */ telemetry?: GateTelemetry; } /** * Register the `before_tool_call` approval gate. No-op in `off` mode. * Returns the store (for tests/introspection) when registered. */ export declare function registerToolApprovalGate(options: RegisterToolApprovalGateOptions): ToolApprovalStore | undefined; //# sourceMappingURL=tool-approval-gate.d.ts.map