/** * OpenCode 事件分发中心 * * 职责: * - 统一管理 OpenCode 事件监听(保持单监听器模式) * - 通过上下文注入接收 index.ts 中的闭包状态 * - 事件分发到内部处理器 * * 设计原则: * - 单一入口:每种事件类型仅注册一次监听器 * - 依赖注入:所有状态通过上下文对象传入 * - 最小修改:保持语义和行为不变 */ import type { PermissionRequestEvent } from '../opencode/client.js'; import type { StreamStateManager } from '../store/stream-state.js'; /** * Timeline 片段类型 */ export type TimelineSegment = { type: 'text'; text: string; } | { type: 'reasoning'; text: string; } | { type: 'tool'; name: string; status: ToolRuntimeState['status']; output?: string; kind?: 'tool' | 'subtask'; } | { type: 'note'; text: string; variant?: 'retry' | 'compaction' | 'question' | 'error' | 'permission'; }; /** * 流式卡片数据类型(从 cards-stream 导入) */ import type { StreamCardData } from '../feishu/cards-stream.js'; /** * 流式卡片数据类型(从 cards-stream 导入) */ export type { StreamCardData } from '../feishu/cards-stream.js'; /** * 工具运行时状态 */ export type ToolRuntimeState = { name: string; status: 'pending' | 'running' | 'completed' | 'failed'; output?: string; kind?: 'tool' | 'subtask'; }; /** * 权限解析结果 */ export type PermissionChatResolution = { chatId?: string; source: 'session' | 'parent_session' | 'related_session' | 'tool_call' | 'message' | 'unresolved'; }; /** * 相关性缓存条目 */ export type CorrelationChatRef = { chatId: string; expiresAt: number; }; /** * OpenCode 事件处理上下文 * 封装 index.ts 中的所有依赖 */ export interface OpenCodeEventContext { streamStateManager: StreamStateManager; toSessionId: (value: unknown) => string; toNonEmptyString: (value: unknown) => string | undefined; setToolCallCorrelation: (toolCallId: unknown, chatId: unknown) => void; setMessageCorrelation: (messageId: unknown, chatId: unknown) => void; getToolCallCorrelation: (toolCallId: unknown) => string | undefined; getMessageCorrelation: (messageId: unknown) => string | undefined; resolvePermissionChat: (event: PermissionRequestEvent) => PermissionChatResolution; normalizeToolStatus: (status: unknown) => 'pending' | 'running' | 'completed' | 'failed'; getToolStatusText: (status: ToolRuntimeState['status']) => string; stringifyToolOutput: (value: unknown) => string | undefined; asRecord: (value: unknown) => Record | null; pickFirstDefined: (...values: unknown[]) => unknown; buildToolTraceOutput: (part: Record, status: ToolRuntimeState['status'], withInput: boolean) => string | undefined; clipToolTrace: (text: string) => string; mergeToolOutput: (previous: string | undefined, incoming: string | undefined) => string | undefined; getOrCreateToolStateBucket: (bufferKey: string) => Map; syncToolsToBuffer: (bufferKey: string) => void; upsertToolState: (bufferKey: string, toolKey: string, state: ToolRuntimeState, kind?: 'tool' | 'subtask') => void; markActiveToolsCompleted: (bufferKey: string) => void; appendTextFromPart: (sessionID: string, part: { id?: unknown; text?: unknown; }, bufferKey: string) => void; appendReasoningFromPart: (sessionID: string, part: { id?: unknown; text?: unknown; }, bufferKey: string) => void; clearPartSnapshotsForSession: (sessionID: string) => void; formatProviderError: (raw: unknown) => string; upsertLiveCardInteraction: (chatId: string, replyMessageId: string | null, cardData: StreamCardData, bodyMessageIds: string[], thinkingMessageId: string | null, openCodeMsgId: string) => void; getTimelineSegments: (bufferKey: string) => TimelineSegment[]; getPendingPermissionForChat: (chatId: string) => unknown; getPendingQuestionForBuffer: (sessionId: string, chatId: string) => unknown; applyFailureToSession: (sessionID: string, errorText: string) => Promise; upsertTimelineNote: (bufferKey: string, noteKey: string, text: string, variant?: 'retry' | 'compaction' | 'question' | 'error' | 'permission') => void; appendTimelineText: (bufferKey: string, segmentKey: string, type: 'text' | 'reasoning', deltaText: string) => void; setTimelineText: (bufferKey: string, segmentKey: string, type: 'text' | 'reasoning', text: string) => void; upsertTimelineTool: (bufferKey: string, toolKey: string, state: ToolRuntimeState, kind?: 'tool' | 'subtask') => void; } /** * OpenCode 事件中心 */ export declare class OpenCodeEventHub { private context; private registered; private userMessageIdCache; private streamAccumulator; private resolveConversationRoute; /** * 注入事件处理上下文 */ setContext(context: OpenCodeEventContext): void; /** * 注册所有 OpenCode 事件监听器 * 确保每种事件类型仅注册一次 */ register(): void; private handlePermissionRequest; private handleSessionStatus; private handleSessionIdle; private handleMessageUpdated; private handleSessionError; private handleMessagePartUpdated; /** * Build StreamAccumulator deps from injectedDependencies() and instance state. * Called lazily by StreamAccumulator factory — only when events actually fire, * so `this.context` is guaranteed to be set. */ private buildAccumulatorDeps; private handleQuestionAsked; /** * 构建问答提示文本消息 */ private buildQuestionText; /** * 构建企业微信 Markdown 格式的问答提示文本消息 * 企业微信 Markdown 格式有限,避免使用 **粗体**、_斜体_ 等语法 */ private buildWeComQuestionText; /** * 为 Telegram 发送带 InlineKeyboard 的问答卡片 */ private sendTelegramQuestionCard; /** * 转义 Telegram Markdown 特殊字符 */ private escapeTelegramMarkdown; /** * 获取注入的外部依赖 * 这些依赖不通过 context 传递,因为它们是模块级别的单例 */ private injectedDependencies; } export declare const openCodeEventHub: OpenCodeEventHub; //# sourceMappingURL=opencode-event-hub.d.ts.map