/** * Interactive approval for hosted destructive actions (`gsk hosted *`). * * Destructive hosted submissions return a transport-ok envelope whose domain * payload is `code: "pending_approval"` — the action sits server-side until * the user approves/rejects it (normally via the web project banner) or it * expires. When gsk itself runs in an interactive terminal, round-tripping * the user through the web UI is pointless: prompt y/n on stderr, then drive * `hosted_action_approve` / `hosted_action_reject` / the `hosted_action_wait` * loop directly. * * This is deliberately the CLI's first per-tool special case, so it is kept * isolated here: every decision lives in a pure, unit-testable function; the * readline shell at the bottom is the only impure part, and index.ts only * gates on shouldPromptForApproval(). stdout stays machine-parseable — all * prompt/progress lines go to stderr, and the caller prints exactly one * transport envelope: the LAST one received (terminal wait result, reject * ack, or the untouched original pending stub). * * Domain codes mirror backend/hosted_deploy_skill/responses.py (the * approve/reject codes land in the same PR as this file). */ import type { ApiResponse } from './types.js'; export interface PendingApprovalInfo { id: string; summary: string; diffPreview?: unknown; expiresAt?: string; } /** * Recognize a pending-approval submission envelope. Requires the * `pending_action_id` — without it the flow could not approve anything, * so such an envelope just prints as-is. */ export declare function extractPendingApproval(envelope: ApiResponse): PendingApprovalInfo | null; /** * Gate for the interactive flow: a pending-approval envelope, an * interactive terminal on BOTH stdin and stdout (stdout piped to a file * or another process must keep today's print-and-exit behavior even if * stdin is a TTY), and no --no-input. */ export declare function shouldPromptForApproval(envelope: ApiResponse, isInteractiveTty: boolean, noInput: boolean): boolean; export type ApprovalAnswer = 'approve' | 'reject' | 'leave'; /** y/yes → approve, n/no → reject, anything else (incl. Enter) → leave. */ export declare function classifyAnswer(raw: string): ApprovalAnswer; /** * After hosted_action_approve: `wait` when the action is (already) approved * and will execute; `present` for everything else — terminal translations * (action_expired / action_rejected), cli_approve_disabled, not-found, and * transport errors are all final answers the user just needs to see. */ export declare function decideAfterApprove(envelope: ApiResponse): 'wait' | 'present'; export type WaitStep = { kind: 'present'; } | { kind: 'continue'; progress: string; } | { kind: 'exhausted'; notice: string; }; /** * Reduce one hosted_action_wait envelope to the next loop step. * * - terminal codes (completed / execution_failed / action_rejected / * action_expired), unknown codes and transport errors → present as-is; * - still_pending with server budget left (retryable=true) → continue; * - still_pending with the budget spent → exhausted: stop looping, tell the * user on stderr what to run next, and still present the envelope. */ export declare function nextWaitStep(envelope: ApiResponse, actionId: string): WaitStep; /** * stderr banner shown before the y/n prompt. Pure so the expiry math is * testable; `nowMs` defaults to the real clock at the call site. */ export declare function pendingPromptLines(info: PendingApprovalInfo, nowMs: number): string[]; /** The side-effect surface of the flow — faked in unit tests. */ export interface ApprovalIo { executeTool(toolName: string, args: Record): Promise; /** Ask on stderr, resolve with the raw answer line. */ ask(question: string): Promise; /** One human-facing progress/banner line on stderr. */ notify(line: string): void; now(): number; } /** * Full decision flow for one pending-approval envelope. Returns the ONE * transport envelope the caller must print to stdout — always the last * one received (or the untouched original when the user leaves it pending). */ export declare function runPendingApprovalFlow(pendingEnvelope: ApiResponse, io: ApprovalIo): Promise; /** * Thin TTY shell around runPendingApprovalFlow: readline over * stdin/stderr (NEVER stdout — it must stay one machine-parseable JSON * document). Only called when shouldPromptForApproval() returned true. */ export declare function runInteractiveApprovalFlow(pendingEnvelope: ApiResponse, executeTool: (toolName: string, args: Record) => Promise): Promise; //# sourceMappingURL=pendingApproval.d.ts.map