/**
 * In-memory broker for `botmux ask` (v0.1.8).
 *
 * Holds the pending-ask registry, runs the deadline timers, and arbitrates
 * click resolution. IM-agnostic: the im/lark side wires a dispatcher via
 * `setCardDispatcher` so the broker doesn't import Lark types.
 *
 * §3 / §6 / §7 / §8 of /tmp/botmux-ask.md.
 */
import type { AskCardDispatcher, AskClickOutcome, AskResult, CreateAskInput, PendingAsk } from './ask-types.js';
/** Wire the canTalk predicate. Called once during daemon bootstrap. */
export declare function setCanTalkChecker(fn: (larkAppId: string, chatId: string, openId: string) => boolean): void;
/** Wire the IM-side dispatcher. Called once during daemon bootstrap from
 *  daemon.ts after im/lark/ask-card.ts is constructed. */
export declare function setCardDispatcher(d: AskCardDispatcher): void;
/** Register a new pending ask. Returns a Promise that settles when:
 *   - a valid click arrives (`kind:'answered'`)
 *   - the deadline elapses (`kind:'timedOut'`)
 *   - the broker invalidates the ask (`kind:'invalidated'`)
 *
 *  Side effects:
 *   - generates askId + nonce
 *   - starts the deadline timer
 *   - dispatches the card; if the card send fails, the ask is immediately
 *     invalidated and the Promise settles with `kind:'invalidated'`.
 *
 *  Throws synchronously only if no dispatcher has been wired — that's a
 *  daemon-misconfiguration bug, not a runtime ask failure.
 */
export declare function registerAsk(input: CreateAskInput): Promise<AskResult>;
/**
 * 勾选/取消勾选某问题的某个选项（累积模式，不 settle）。
 *
 * 校验同 `tryResolveAsk`：askId 存在 / nonce 匹配 / 未 settle / 已授权 /
 * questionIndex 合法 / key 在该问题的 options 中。
 *
 * 对于单选问题（multiSelect:false），翻转时 Set 内只保留该 key（相当于"换选"）。
 * 对于多选问题（multiSelect:true），翻转规则：已在 Set 中则移除，否则添加。
 *
 * 成功返回 `'toggled'`；非法返回对应 AskClickOutcome。
 */
export declare function toggleAsk(args: {
    askId: string;
    nonce: string;
    questionIndex: number;
    key: string;
    by: string;
}): AskClickOutcome;
/**
 * 提交答案并 settle。
 *
 * `selections` 显式传入时直接使用（按钮单选 / 一次性表单提交场景）；
 * 否则使用 `toggleAsk` 累积的勾选状态。
 *
 * 对于 `multiSelect:false` 的问题，要求恰好 1 个选中，否则返回 `'stale'`。
 * 校验通过则 settle 并返回 `'accepted'`；非法返回对应 AskClickOutcome。
 */
export declare function submitAsk(args: {
    askId: string;
    nonce: string;
    by: string;
    selections?: ReadonlyArray<ReadonlyArray<string>>;
}): AskClickOutcome;
/**
 * 提交一段自定义回复（用户在话题里直接打字作答，替代点按钮）并 settle。
 *
 * 校验：askId 存在 / 未 settle / `by` 可 canTalk / text trim 后非空。
 * settle 为 `kind:'answered'`，各问 `answers` 为空数组、`comment` 携带 trim 后原文
 * （替代语义：没有任何选项被选中，CLI 侧 formatAnswer 用 comment 回落作答）。
 *
 * 不需要 nonce：调用方（daemon 消息路由）用 `findPendingAskByAnchor` 从在线
 * pending 表按话题 anchor 查到 askId，本身就排除了「重启后的陈旧卡片」场景。
 *
 * 成功返回 `'accepted'`；非法返回对应 AskClickOutcome。
 */
export declare function submitCustomReply(args: {
    askId: string;
    by: string;
    text: string;
}): AskClickOutcome;
/**
 * 按话题 anchor 查找一个**未 settle**的 pending ask，供 daemon 判断「这条文字回复
 * 是不是在回答某个 ask」。匹配条件：
 *   - larkAppId 相同（不跨 bot 命中）
 *   - chatId 相同
 *   - thread-scope：ask.rootMessageId === anchor（话题根 message_id）
 *   - chat-scope：ask.rootMessageId === null（anchor 实为 chatId，已由 chatId 命中）
 *
 * 命中多个时返回最先注册的（实践中同一 anchor 同时最多一个 pending ask，因为发起
 * ask 的 CLI 此刻正阻塞等待结果）。返回 snapshot，改它不影响 broker 状态。
 */
export declare function findPendingAskByAnchor(args: {
    larkAppId: string;
    chatId: string;
    anchor: string;
}): PendingAsk | undefined;
/** Resolve attempt from a card-button click. Returns one of the §10 outcomes;
 *  caller (card click handler) maps to user-facing toast.
 *
 *  v0.1.8 起退化为单问单选的便捷封装：等价于
 *  `submitAsk({..., selections:[[selected]]})`.
 *  使 `botmux ask buttons` 与其已有测试零回归。
 *
 *  All four "no-op" outcomes (`unauthorized`/`stale`/`already_settled`) leave
 *  the broker state unchanged so the original CLI Promise keeps waiting for
 *  the real winner or the deadline. */
export declare function tryResolveAsk(args: {
    askId: string;
    nonce: string;
    selected: string;
    by: string;
}): AskClickOutcome;
/** Invalidate every pending ask. Intended for daemon shutdown / restart paths
 *  so CLI subprocesses unblock with `kind:'invalidated'` instead of waiting
 *  forever on a dead daemon. Returns the number of asks actually settled
 *  (settled-but-retained entries from the race window are skipped). */
export declare function invalidateAll(reason: string): number;
/** Count of asks still awaiting a click / timeout — excludes settled entries
 *  retained within the race-loser feedback window. For tests and metrics only. */
export declare function _pendingCount(): number;
/** Read a pending ask by id. Returns a snapshot; mutating it has no effect on
 *  broker state. Used by the card handler to PATCH toggle state. */
export declare function getAskSnapshot(askId: string): PendingAsk | undefined;
/** Read a pending ask by id — for tests only. Returns a snapshot; mutating it
 *  has no effect on broker state. */
export declare function _getPending(askId: string): PendingAsk | undefined;
/** 返回当前 pending map 中所有 askId 列表（含 settled 但仍在 retention 内的条目）。
 *  仅供测试使用。 */
export declare function _allAskIds(): string[];
/** Reset broker state — for tests only. Does NOT resolve outstanding promises,
 *  so tests must not call this while real CLI processes might be waiting. */
export declare function _resetForTest(): void;
//# sourceMappingURL=ask-broker.d.ts.map