import type { MessengerAdapter } from './types.js'; import { type ApprovalNotification, type Decision } from './approval-bus.js'; /** * Classify an inbound IM reply as an approval decision. Returns null when the * reply doesn't match any known pattern — the caller should treat that as * "user changed topic" and route normally. */ export declare function parseApprovalReply(text: string): Decision | null; /** * Render an approval request as a plain text message. We keep this format * stable across IM platforms; richer flows (Feishu cards with buttons) can * layer on later by checking platform/sendCard. * * Two flavors: * - Normal: y/n/all action box with timeout footer * - Auto-allow grace mode (n.autoAllow set): "⏱ 自动放行中…" with deny option */ export declare function formatApprovalPrompt(n: ApprovalNotification): string; /** * Map an IM platform name to its messenger registration. WeChat is special- * cased because the messenger is registered as "wechat-ilink" but messages * carry platform="wechat". */ export declare function platformToMessengerName(platform: string): string; export interface InstallOptions { resolveMessenger: (platform: string) => MessengerAdapter | undefined; /** Optional. Platform names whose messengers should have button-callback * support bound at install time. Adapters that don't implement * onButtonCallback are silently skipped. Defaults to all known IM * platforms; existing tests / callers don't need to pass this. */ buttonCallbackPlatforms?: string[]; } /** * Wire approval-bus.notifier to the messenger layer. Idempotent: calling * install() twice replaces the previous wiring. * * Notifier prefers the rich-card path (sendApprovalCard) when the resolved * adapter implements it; otherwise it falls back to the plain-text path * (sendMessage with formatApprovalPrompt). The text path is the canonical * approval channel for adapters without native interactive buttons (Feishu * cards aren't wired for buttons yet, WeChat iLink can't render them). * * Button callbacks: for each platform listed in buttonCallbackPlatforms * whose adapter implements onButtonCallback, we subscribe a handler that * parses callback_data of the form `apv::` and resolves the * matching pending. Subscribing happens once at install — adapters started * later won't receive button wiring until install() is called again. */ export declare function install(opts: InstallOptions): void; export declare function uninstall(): void; /** * Bind the inline-button handler for a single platform's messenger. The * install() loop calls this for every default platform at boot; callers * (e.g. web/server.ts) that register their messenger AFTER install have * to call this themselves to get button-click → resolvePending wiring. * * Idempotent at the messenger level — onButtonCallback's contract is * "replace previous handler", so re-calling this on the same platform * just rewires. * * Silent no-op when the messenger isn't registered yet, has no * onButtonCallback, or the router hasn't been install()'d. */ export declare function bindButtonHandlerForPlatform(platform: string): void; /** * Called by cli's onMessage handler before it routes to the agent. * Returns true when this message was an approval reply (so the caller should * stop and not invoke the normal router). * * Behavior: * - If thread has no pending approval → return false, do nothing * - If thread has pending AND text parses as decision → resolve, return true * - If thread has pending AND text does NOT parse → auto-deny pending * (user is moving on), return false so the message routes normally * * Receipt: when the resolution adds or revokes an auto-allow rule, also * sends a one-line confirmation back to the IM thread so the user knows * the rule landed (or got cleared). Best-effort: a missing messenger or * sendMessage failure is logged and ignored — the approval itself already * resolved successfully. */ /** * Called by cli's onMessage handler before it routes to the agent. * `key` must be a composite key from `threadKey(platform, channelId, threadId)` * — same value the bus stored under at registerRun/registerSyntheticPending * time. A bare threadId will miss in multi-platform deployments. */ export declare function tryHandleApprovalReply(key: string, text: string, userId?: string): boolean; /** * Parse callback_data of the form `apv::`. reqId may itself * contain characters but never `:` in practice (UUID / opaque sidecar id); * we still split on the LAST colon to be safe, leaving the choice as the * single trailing letter. */ export declare function parseCallbackData(data: string): { reqId: string; choice: 'y' | 'n' | 'a'; } | null; /** Test helper — not exported from index. */ export declare function _isInstalled(): boolean; //# sourceMappingURL=approval-router.d.ts.map