import type { ChoiceCardPrompt } from './types.js'; import type { RunContext } from './approval-bus.js'; /** Hard caps on user-facing prompt + choice count + per-choice label. */ export declare const MAX_QUESTION_CHARS = 1000; export declare const MAX_CHOICE_LABEL_CHARS = 80; export declare const MIN_CHOICES = 2; export declare const MAX_CHOICES = 9; export interface AskPayload { question?: unknown; choices?: unknown; timeoutSec?: unknown; /** Selection mode. single=pick one; multi=pick N items at once. */ selectionMode?: unknown; /** Multi-select lower bound (only used when selectionMode=multi). */ minSelections?: unknown; /** Multi-select upper bound (only used when selectionMode=multi). */ maxSelections?: unknown; /** Allow free-form text as an alternative answer path. */ allowCustomInput?: unknown; /** Label for the custom-input pseudo option (TUI only). */ customInputLabel?: unknown; /** When true (default), text replies like "1" / "a" / the choice * label substring match the corresponding option. Disable for * strict button-only flows (the user must click a button or wait * for the timeout). */ allowTextReply?: unknown; /** Optional http(s) URL surfaced as a "查看详情" entry on the card, for * context the decision depends on (e.g. a viewer/paste link, a PR). */ detailsUrl?: unknown; /** Optional label for the details link (default "📖 查看详情"). */ detailsLabel?: unknown; } export interface AskChoice { /** 1-indexed for human readability (1 / 2 / 3 …) */ index: number; /** Display label as shown to the user. */ label: string; } export type AskSelectionMode = 'single' | 'multi'; interface AskSuccessResult { /** 1-indexed primary choice. For multi-select this is the first pick. */ index: number; /** Primary label. For multi-select this is the first pick label. */ label: string; /** How the answer reached agim. */ via: 'button' | 'text'; /** Raw user-typed text or callback payload. */ raw: string; /** Chosen selection mode. */ selectionMode: AskSelectionMode; /** Present for multi-select answers. */ indexes?: number[]; /** Present for multi-select answers. */ labels?: string[]; /** Present when the user gave free-form custom input. */ customText?: string; } export type AskResult = { ok: true; result: AskSuccessResult; } | { ok: false; error: string; reason?: 'timeout' | 'cancelled' | 'invalid' | 'no-messenger'; }; /** * Handle one `ask_user` RPC. Returns the AskResult to be wired back to * the sidecar. ALL exceptions caught + converted; the bus never sees * a thrown error from here. */ export declare function handleAskOp(payload: AskPayload, ctx: RunContext): Promise; /** * Router-side: try to resolve a pending ask for this thread with the * inbound text. Returns true iff the text was consumed (router should * skip normal routing). */ export declare function tryResolvePendingAsk(platform: string, channelId: string, threadId: string, rawText: string): boolean; /** * Messenger-side: resolve a pending ask from a terminal button payload. * Returns true iff resolved. Kept for web/tui callers (and tests) that send * fully-formed `single` / `multi:` / `custom:` data. */ export declare function tryResolvePendingAskByButton(data: string): boolean; /** Outcome of a button tap, for the router to act on (ack + re-render). */ export type AskButtonOutcome = { kind: 'resolved'; finalText: string; } | { kind: 'rerender'; card: ChoiceCardPrompt; } | { kind: 'await-custom'; prompt: string; } | { kind: 'invalid'; hint: string; } | { kind: 'unmatched'; }; /** * Unified ask button dispatch. Handles BOTH the terminal payloads * (single / multi: / custom:, via resolveByButton) AND the stateful * Telegram kinds (toggle / submit / custom-trigger) that accumulate * server-side. The router maps the returned outcome to an ack + an * optional in-place card edit. */ export declare function handleAskButton(data: string): AskButtonOutcome; /** Diagnostic: pending count per thread. Used by /ask status / future * /tasks#ask web tile. */ export declare function listPendingAsks(): Array<{ reqId: string; platform: string; threadId: string; question: string; choices: number; ageMs: number; }>; /** * Cancel a single pending ask by reqId. Returns true iff something * was cancelled. Used by the admin panel's per-row Cancel button — * the agent waiting on it gets { ok: false, reason: 'cancelled' } * back exactly as if the user had let it time out, which the agent * already knows how to handle. */ export declare function cancelPendingAsk(reqId: string, reason?: string): boolean; /** Test hook: cancel everything. Useful between bun-test cases. */ export declare function _resetAskBusForTests(): void; export {}; //# sourceMappingURL=ask-user-rpc.d.ts.map