/** * Pre-call confirmation for confirmation-required tool actions. * * The server annotates confirmation-required tools/actions in the /tools * schema (`requires_confirmation`, plus `interactive_only` for actions whose * skip parameters are schema-locked, e.g. slack send) and advertises * `features.interactive_confirm` when the caller's caps AND the server-side * gatekeeper agree. When both hold, the CLI resolves the confirmation * BEFORE the single server call — `[y/N]` on stdin/stderr or `--yes` — and * injects `cli_confirmed: true`, which the server translates into direct * execution. A decline means ZERO server calls. No replay, no second call: * the old pending/double-call contract only remains as the server-side * fail-safe for a stale schema cache (see isPendingConfirmationEnvelope). * * Actions that dispatch on one argument (genteam members `op`, teams api * `method`) additionally carry `confirmation_exempt` — the server's list of * read-only values of that argument, plus, when the server substitutes a * value for an OMITTED argument, that `default`. Only an exact match * against the final merged args (an omitted argument counting as the * declared default) skips the prompt (see isConfirmationExempt); everything * else stays gated as before, and the server's own gate still backs every * write. * * `--yes` is the only advertised unattended path: a new-contract server * omits the legacy `skip_confirmation` / `auto_skip_confirmation` * parameters from the schema it serves to this CLI. They are still * accepted (hidden flags, --args-file keys — see LEGACY_SKIP_PARAMS) and * passed through verbatim, because the server honors them for CLI callers * regardless of the feature bit, so pre-contract cron scripts keep working * on any server. When such a param is what skips the prompt, the CLI prints * LEGACY_SKIP_DEPRECATION_HINT once per process (stderr). * * Like pendingApproval.ts, every decision is a pure, unit-testable * function; the readline shell at the bottom is the only impure part. The * prompt reads stdin (NOT /dev/tty) so `echo y | gsk ...` and agents * feeding a background process work; EOF defaults to decline. */ import type { ApiResponse, ToolSchema } from './types.js'; export declare const STALE_SCHEMA_CACHE_MESSAGE = "schema cache stale, run 'gsk --refresh ls' and retry"; /** * The pre-contract server-side skip parameters. A new-contract server hides * them from the schema served to this CLI; the CLI keeps accepting them * (hidden flags + --args-file keys on confirmation-annotated tools) and * forwards them unchanged — the server honors them for CLI callers with or * without the feature bit, so an unattended script written against the old * contract works on every server version. */ export declare const LEGACY_SKIP_PARAMS: readonly ["skip_confirmation", "auto_skip_confirmation"]; export declare const LEGACY_SKIP_DEPRECATION_HINT: string; /** Test hook: forget that the deprecation hint was already printed. */ export declare function resetLegacySkipDeprecationHint(): void; export interface ConfirmationFlags { requiresConfirmation: boolean; interactiveOnly: boolean; } /** * Per-value exemption declared by the server (e.g. genteam members op=list): * true only when the annotation is well-formed AND the FINAL merged arg is a * string exactly equal to one of the server's canonical values. An arg that * is ABSENT (`undefined`, never `null` or `''`) counts as the annotation's * `default` when the server declares one — that is the value its run() * substitutes, so `teams api --path ` with no --method is the GET it * really is (#60102). Non-string, unknown or differently-cased values, a * default the server did not declare (or declared outside `values`) and any * malformed annotation stay gated — fail-closed; the server's own gate still * backs every write. No trim, no case-fold: the adaptors normalize * differently (lower / none / upper) and the client cannot know which. */ export declare function isConfirmationExempt(exempt: unknown, args: Record): boolean; /** * Resolve the static confirmation annotation for one invocation: service * tools carry it per action (`tool.actions[args.action]`), single-action * tools on `tool.cli`. Reads the merged args (not the positional) because * the action may arrive via --args-file — and, for the same reason, the * discriminator of a per-value exemption (`op` via flag, alias or file) * is read from the final merged args too. */ export declare function confirmationFlagsFor(tool: ToolSchema, args: Record): ConfirmationFlags; export interface ConfirmationContext { /** The cached /tools payload advertised features.interactive_confirm. */ featureOn: boolean; yes: boolean; noInput: boolean; /** stdin was already consumed by `--args-file -` — nothing to prompt from. */ stdinConsumed: boolean; /** * The final merged args already carry a truthy skip_confirmation / * auto_skip_confirmation — a pre-contract unattended script (e.g. * `gsk email send --skip_confirmation true` on a scheduled run) that * explicitly opted out of server-side confirmation. Computed from the * args by resolvePreCallConfirmation; set directly only in tests. */ skipRequested?: boolean; } export type ConfirmationDecision = { kind: 'proceed'; confirmed: boolean; /** * A legacy skip param — not --yes — is what bypassed the prompt on a * gated, non-locked action with the feature on: the caller prints the * deprecation hint. Absent when the feature is off or the action is * not gated (the param changed nothing there). */ deprecatedSkip?: boolean; } | { kind: 'prompt'; warning?: string; } | { kind: 'refuse'; exitCode: number; message: string; }; /** * The confirmation state machine, pre-prompt half: * * - feature off (old server / GK off) or action not annotated → call as * today, no cli_confirmed; * - explicit skip_confirmation/auto_skip_confirmation arg on a normal * action → call as today, no prompt, no cli_confirmed (the server * honors the skip param directly for CLI callers — existing unattended * scripts keep working), flagged deprecatedSkip so the caller prints * the one-line [DEPRECATED] hint; * - --yes on a normal action → call with cli_confirmed, no prompt; * - --yes or a skip param on an interactive-only action → warn, fall into * the prompt (the server strips locked skip params anyway); * - no usable stdin (--no-input, or --args-file -) → exit 2, zero calls; * - otherwise → prompt. */ export declare function decideConfirmation(flags: ConfirmationFlags, ctx: ConfirmationContext): ConfirmationDecision; /** * stderr preview of exactly what the user typed (the server has not * normalized anything yet — you confirm what you sent). The action is part * of the prompt question, so it is skipped here. */ export declare function previewLines(args: Record): string[]; /** Prompt question naming the exact tool + action being confirmed. */ export declare function promptQuestion(tool: ToolSchema, args: Record): string; /** y/yes (case-insensitive) confirms; anything else — including EOF — declines. */ export declare function isConfirmedAnswer(raw: string): boolean; export type PreCallConfirmation = { action: 'call'; injectConfirmed: boolean; } | { action: 'abort'; exitCode: number; message: string; }; /** The side-effect surface of the flow — faked in unit tests. */ export interface ConfirmIo { /** Ask on stderr, resolve with the raw answer line ('' on EOF). */ ask(question: string): Promise; /** One human-facing preview/warning line on stderr. */ notify(line: string): void; } /** * Full pre-call flow. Returns either "call (optionally with cli_confirmed * injected)" or "abort with this exit code" — the caller must not touch the * server on abort. A legacy skip param that bypassed the prompt yields a * plain call (no injection) plus the deprecation hint on stderr, at most * once per process. */ export declare function resolvePreCallConfirmation(tool: ToolSchema, args: Record, ctx: ConfirmationContext, io: ConfirmIo): Promise; /** * Fail-safe detector: with the interactive contract active the server never * pends — a pending envelope means the cached schema predates this action's * confirmation flag, so the CLI neither prompted nor sent cli_confirmed. * The caller reports STALE_SCHEMA_CACHE_MESSAGE; there is no replay. */ export declare function isPendingConfirmationEnvelope(envelope: ApiResponse): boolean; /** * Thin readline shell around resolvePreCallConfirmation: stdin in, * stderr out (NEVER stdout — it must stay one machine-parseable JSON * document). EOF (stdin closed / Ctrl+D) resolves the question with '', * which declines — same close-race pattern as pendingApproval.ts. */ export declare function runPreCallConfirmation(tool: ToolSchema, args: Record, ctx: ConfirmationContext): Promise; //# sourceMappingURL=confirm.d.ts.map