/** * Plugin-side bridge to the host's OFFICIAL permission dialog. * * Verified against the live desktop binary (1.18.30 asar probe): the tool * context handed to plugin tools carries `ask(req)` — the host wraps it as * `permission.ask({ ...req, sessionID, tool, ruleset: merge(agent.permission, * session.permission) })`. PermissionV2.ask evaluates each pattern against * that ruleset plus the saved rules (`findLast` wins, so a rule registered * for the exact tool name beats the `tm_*` wildcard): an allow rule matching * the pattern resolves SILENTLY, a deny rule throws, anything else — the * default — publishes `permission.asked`, i.e. the OFFICIAL dialog, and the * promise resolves on approval / rejects on rejection. The unified approval * gate observes the same `permission.asked` event, so its * `TM_ASK_TIMEOUT_MIN` auto-reject timer covers these asks too. * * The plugin never self-allows: a silent resolution can only come from a * rule the user (or config) created; a dialog approval is the user's own * verdict. */ export interface TmAskRequest { /** Permission/action name — use the tool name ("tm_webfetch", ...). */ permission: string; /** Resource patterns the dialog evaluates against the ruleset. */ patterns: string[]; /** Rendered by the dialog; keep it human-informative, no secrets. */ metadata?: Record; } export type TmAskFn = (req: TmAskRequest) => Promise; export declare function setAskBridgeObserver(fn: ((present: boolean) => void) | null): void; /** Extract ctx.ask defensively — host versions before the ctx bridge lack * it, and test stubs may not provide it. */ export declare function askFnOf(ctx: unknown): TmAskFn | null; export type AskOutcome = "approved" | "rejected" | "timed-out" | "unavailable"; /** A remembered allow rule resolves the ask inside the host; a human has to * read the dialog, decide and click. 1.5 s separates the two by an order of * magnitude on the observed desktop, and this is a HEURISTIC, not a host * contract — which is exactly why the sentence it produces says "没有人点这个 * 窗" only alongside the mechanism, never instead of it. */ export declare const ASK_AUTO_GRANT_MS = 1500; export interface AskResult { outcome: AskOutcome; /** How long the host took to answer. Undefined when there was no bridge. */ answeredInMs?: number; /** approved AND answered fast enough that nobody can have clicked it — i.e. * a saved rule (an "always" the user granted earlier, possibly in another * agent's session) answered on their behalf. */ autoGranted: boolean; } /** The same drive as `askUserForTarget`, but it reports HOW the approval * arrived. Callers that show the user a result need this: an agent that * cannot tell "the host remembered an always-allow" from "the user just * approved this" will report 免弹窗 as evidence about the allowlist, and that * is how a correct observation becomes a wrong conclusion. */ export declare function askUserForTargetDetailed(ctx: unknown, req: TmAskRequest, waitMs?: number, autoGrantMs?: number): Promise; /** The line a caller appends when a governed web fetch/navigation proceeded on * an approval rather than on the static allowlist. One function so no tool * can half-report it. */ export declare function askGrantNote(res: AskResult): string; /** The unified approval gate auto-rejects an unanswered dialog after * `TM_ASK_TIMEOUT_MIN`, but it is only ARMED when R6 is on and the client can * reply. Where it is not armed, `await ask()` hung until the user interrupted * the whole turn — live evidence: an out-of-allowlist tm_webfetch that showed * as a spinning card and then `Tool execution aborted`. A tool-side deadline * is the backstop, so no caller has to remember it. */ export declare const ASK_GRACE_MS = 15000; /** The v1 boot wiring (`resolveAskTimeoutMs() + ASK_GRACE_MS`) is gone with * the approval gate; the default below is the backstop for the un-armed * case. Kept as a setter so a caller can still tighten it. */ export declare function setAskWaitMs(ms: number): void; export declare function getAskWaitMs(): number; /** Drive the official dialog for an out-of-allowlist target. Never throws: a * missing bridge is "unavailable", a refusal is "rejected", and a dialog * nobody answered is "timed-out" — three different answers that need three * different next moves. */ export declare function askUserForTarget(ctx: unknown, req: TmAskRequest, waitMs?: number): Promise; export declare function setAskUnavailableNote(note: string): void; export declare function askRefusalNote(outcome: AskOutcome, waitMs?: number): string; //# sourceMappingURL=perm-ask.d.ts.map