import { type V3GateCardHandlerDeps } from './v3-gate-card-handler.js'; import { type V3BlockedCardHandlerDeps } from './v3-blocked-card-handler.js'; import { type V3LoopGrantCardHandlerDeps } from './v3-loop-grant-card-handler.js'; import { type V3RevisitGrantCardHandlerDeps } from './v3-revisit-grant-card-handler.js'; import { type V3RunSaveCardHandlerDeps } from './v3-run-save-card-handler.js'; import { type V3DistillationCardHandlerDeps } from './v3-distillation-card-handler.js'; import { type WorkerSessionReplyOptions } from '../../core/worker-pool.js'; import type { DaemonSession } from '../../core/types.js'; import type { ProjectInfo } from '../../services/project-scanner.js'; export interface CardHandlerDeps { activeSessions: Map; sessionReply: (rootId: string, content: string, msgType?: string, larkAppId?: string, turnId?: string, opts?: WorkerSessionReplyOptions) => Promise; lastRepoScan: Map; /** v3 humanGate 审批卡点击处理(driveRun 由 daemon 接的 v3 gate runner 提供). */ v3GateDeps?: V3GateCardHandlerDeps; /** v3 blocked 重试卡点击处理(同一个 runner 的 driveRun). */ v3BlockedDeps?: V3BlockedCardHandlerDeps; /** v3 loop 追加一轮卡点击处理(同一个 runner 的 driveRun). */ v3LoopGrantDeps?: V3LoopGrantCardHandlerDeps; /** v3 回溯预算准许卡点击处理(同一个 runner 的 driveRun). */ v3RevisitGrantDeps?: V3RevisitGrantCardHandlerDeps; /** v3 成功终态卡的「保存复用」动作。 */ v3RunSaveDeps?: V3RunSaveCardHandlerDeps; /** v3 参数蒸馏提案的接受/拒绝动作。 */ v3DistillationDeps?: V3DistillationCardHandlerDeps; /** VC meeting invite/consumer card actions. Implemented in daemon to * keep meeting sessions, tombstones, and listener-group state single-owned. */ vcMeetingCardAction?: (data: CardActionData, larkAppId: string) => Promise; /** Codex 完成通知卡动作。事件存储、App 打开和会话接管由 daemon 单点持有。 */ codexNotifierCardAction?: (data: CardActionData, larkAppId: string) => Promise; /** 授权成功后重放之前被拦截的消息,让用户无需再 @ 一遍。 */ replayGrantedMessage?: (data: any, larkAppId: string) => void; } /** * Lark card action callback envelope. * * Exported so module-specific dashboard handlers can share the callback type * without redeclaring it. * * Trust model: * - `operator.open_id` and `operator.union_id` are Lark-verified payload * fields. Treat them as the only legitimate source of caller identity. * - `action.value` is round-tripped from the card schema and IS NOT * verified by Lark. NEVER read identity fields (`union_id`, `open_id`, * `user_id`, …) from `action.value`. */ export interface CardActionData { operator?: { open_id?: string; /** Lark-verified union_id, present on card v2 callbacks where the tenant * enables `with_union_id`. Absent when Lark doesn't carry it; callers * fall back to `resolveUserUnionId` via `resolveCardOperatorUnionId`. */ union_id?: string; }; action?: { value?: Record; option?: unknown; options?: unknown; form_value?: Record; }; context?: { open_message_id?: string; }; open_message_id?: string; } /** Resolved operator identity returned by `resolveCardOperatorUnionId`. */ export interface CardOperatorIdentity { /** Verified `on_`-prefixed union_id, or `undefined` when verification fails. */ unionId?: string; /** The verified `operator.open_id` echoed back for audit/log purposes. Never * used as an authn/authz proxy when `unionId` is absent. */ openId?: string; } /** Optional deps for `resolveCardOperatorUnionId` — production omits, tests * inject a fake `resolveUserUnionId` to avoid hitting the Lark contact API. */ export interface ResolveCardOperatorUnionIdDeps { resolveUserUnionId?: (larkAppId: string, openId: string) => Promise<{ unionId?: string; name?: string; }>; } /** * Resolve the verified `union_id` of the operator who clicked a card button. * * Three-state semantics: * 1. `operator.union_id` starts with `on_` → trust it directly. * 2. `operator.union_id` is present but does NOT start with `on_` (e.g. * `ou_xxx`, malformed) → reject; do NOT fallback. Trusting `open_id` * after a malformed verified field would be a bypass. * 3. `operator.union_id` is absent → fall back to * `resolveUserUnionId(larkAppId, openId)`, accepting only `on_`-prefixed * results. * * In every failure mode (missing open_id, fallback returns no unionId, * fallback throws) the function returns `{ openId }` with `unionId` left * undefined, so callers fail closed. * * `action.value` is NEVER read here — see the unit tests that pin that * contract. */ export declare function resolveCardOperatorUnionId(data: CardActionData, larkAppId: string, deps?: ResolveCardOperatorUnionIdDeps): Promise; /** * Commit a resolved working directory onto a repo-select session: pin it, then * either fork the pending CLI (first selection) or close + recreate the session * (mid-session switch). Shared by the dropdown flow, the worktree flow (which * funnels back in with the freshly created worktree path) and the manual * directory-entry form. Extracted to module scope so the form-submit branch can * reuse the exact same spawn/switch path instead of duplicating it. */ export declare function commitRepoSelection(ctx: { ds: DaemonSession; rootId: string; cardMessageId?: string; larkAppId?: string; operatorOpenId?: string; activeSessions: Map; sessionReply: (rid: string, content: string, msgType?: string, turnId?: string) => Promise; }, dirPath: string, dirLabel: string, opts?: { suppressConfirmReply?: boolean; confirmReplyText?: string; pinWorkingDir?: boolean; riffRepoDirs?: string[]; }): Promise; /** * 仅默认目录 + auto-worktree 的**异步**提交:`ds` 必须已注册进 activeSessions 且处于 * `pendingRepo` 挂起态(prompt 已 buffer,入站路由不会去抢 fork——见 daemon.ts pendingRepo * 分支),本函数在**关键路径之外**(调用方 `void` 掉、立即返回)跑: * 1) 在 `baseDir` 建独立 worktree(非 git / 失败 → 回退 baseDir,均经 `notify` 发提示) * 2) 用与「选仓库卡」完全相同的 {@link commitRepoSelection} 提交该目录并 fork——复用其 * prompt 重建(会 fold 进等待期间 buffer 的后续消息)、代际守卫、僵尸防护。 * * 这样避免了把 git fetch(可长达 30s)同步塞进 spawn/fork 链路的三宗罪:放大重复 spawn * 竞态、worker=null 期间被路由在**基目录**抢 fork、阻塞 dashboard/webhook 响应。 * * 永不抛出:worktree 失败已在内部回退;commitRepoSelection 异常被兜底 log(会话仍留在 * pendingRepo,用户可 /repo 自救),绝不让 unhandled rejection 掀掉 daemon。 */ export declare function runAutoWorktreeCommit(deps: { ds: DaemonSession; anchor: string; larkAppId: string; baseDir: string; title?: string; prompt?: string; operatorOpenId?: string; activeSessions: Map; notify: (message: string) => Promise | void; }): Promise; /** * Machine-wide counts for the overload alert preview. Each daemon reports its * bot-scoped stopped sessions and its own live workers, so both fields must be * summed. Best-effort: an unreachable daemon just contributes 0. */ export declare function countHostOverload(): Promise<{ stopped: number; idle: number; }>; export declare function handleCardAction(data: CardActionData, deps: CardHandlerDeps, larkAppId?: string): Promise; //# sourceMappingURL=card-handler.d.ts.map