@agentdevjs/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BasicAgent-RPYJHYCM.js +12 -0
- package/dist/BasicAgent-RPYJHYCM.js.map +1 -0
- package/dist/ExplorerAgent-ITTSJRJI.js +12 -0
- package/dist/ExplorerAgent-ITTSJRJI.js.map +1 -0
- package/dist/chunk-574NG4UB.js +1259 -0
- package/dist/chunk-574NG4UB.js.map +1 -0
- package/dist/chunk-BVF7RUXV.js +136 -0
- package/dist/chunk-BVF7RUXV.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-KO4DJKM6.js +99 -0
- package/dist/chunk-KO4DJKM6.js.map +1 -0
- package/dist/chunk-TIJV6GJW.js +51 -0
- package/dist/chunk-TIJV6GJW.js.map +1 -0
- package/dist/chunk-UCEIJEM5.js +61 -0
- package/dist/chunk-UCEIJEM5.js.map +1 -0
- package/dist/chunk-YFSM6IYU.js +6572 -0
- package/dist/chunk-YFSM6IYU.js.map +1 -0
- package/dist/features/example-feature/templates/example-tool.render.d.ts +3 -0
- package/dist/features/example-feature/templates/example-tool.render.js +20 -0
- package/dist/features/example-feature/templates/example-tool.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/edit.render.d.ts +12 -0
- package/dist/features/opencode-basic/templates/edit.render.js +44 -0
- package/dist/features/opencode-basic/templates/edit.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/glob.render.d.ts +13 -0
- package/dist/features/opencode-basic/templates/glob.render.js +44 -0
- package/dist/features/opencode-basic/templates/glob.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/grep.render.d.ts +13 -0
- package/dist/features/opencode-basic/templates/grep.render.js +67 -0
- package/dist/features/opencode-basic/templates/grep.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/ls.render.d.ts +12 -0
- package/dist/features/opencode-basic/templates/ls.render.js +35 -0
- package/dist/features/opencode-basic/templates/ls.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/read.render.d.ts +13 -0
- package/dist/features/opencode-basic/templates/read.render.js +83 -0
- package/dist/features/opencode-basic/templates/read.render.js.map +1 -0
- package/dist/features/opencode-basic/templates/write.render.d.ts +13 -0
- package/dist/features/opencode-basic/templates/write.render.js +44 -0
- package/dist/features/opencode-basic/templates/write.render.js.map +1 -0
- package/dist/features/skill/templates/skill.render.d.ts +13 -0
- package/dist/features/skill/templates/skill.render.js +33 -0
- package/dist/features/skill/templates/skill.render.js.map +1 -0
- package/dist/features/subagent/templates/agent-close.render.d.ts +9 -0
- package/dist/features/subagent/templates/agent-close.render.js +29 -0
- package/dist/features/subagent/templates/agent-close.render.js.map +1 -0
- package/dist/features/subagent/templates/agent-list.render.d.ts +9 -0
- package/dist/features/subagent/templates/agent-list.render.js +23 -0
- package/dist/features/subagent/templates/agent-list.render.js.map +1 -0
- package/dist/features/subagent/templates/agent-send.render.d.ts +9 -0
- package/dist/features/subagent/templates/agent-send.render.js +37 -0
- package/dist/features/subagent/templates/agent-send.render.js.map +1 -0
- package/dist/features/subagent/templates/agent-spawn.render.d.ts +9 -0
- package/dist/features/subagent/templates/agent-spawn.render.js +38 -0
- package/dist/features/subagent/templates/agent-spawn.render.js.map +1 -0
- package/dist/features/subagent/templates/wait.render.d.ts +9 -0
- package/dist/features/subagent/templates/wait.render.js +29 -0
- package/dist/features/subagent/templates/wait.render.js.map +1 -0
- package/dist/features/todo/templates/task-clear.render.d.ts +12 -0
- package/dist/features/todo/templates/task-clear.render.js +32 -0
- package/dist/features/todo/templates/task-clear.render.js.map +1 -0
- package/dist/features/todo/templates/task-create.render.d.ts +12 -0
- package/dist/features/todo/templates/task-create.render.js +56 -0
- package/dist/features/todo/templates/task-create.render.js.map +1 -0
- package/dist/features/todo/templates/task-list.render.d.ts +12 -0
- package/dist/features/todo/templates/task-list.render.js +72 -0
- package/dist/features/todo/templates/task-list.render.js.map +1 -0
- package/dist/features/todo/templates/task-update.render.d.ts +12 -0
- package/dist/features/todo/templates/task-update.render.js +58 -0
- package/dist/features/todo/templates/task-update.render.js.map +1 -0
- package/dist/index.d.ts +6831 -0
- package/dist/index.js +8376 -0
- package/dist/index.js.map +1 -0
- package/dist/notification-5DFBYKKN.js +32 -0
- package/dist/notification-5DFBYKKN.js.map +1 -0
- package/dist/resolver-F5SM2RHQ.js +8 -0
- package/dist/resolver-F5SM2RHQ.js.map +1 -0
- package/dist/session-events-TETMQI45.js +18 -0
- package/dist/session-events-TETMQI45.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/notification.ts","../src/core/debug-hub.ts","../src/core/types.ts","../src/core/debug-transport.ts","../src/core/claw-debug-client.ts","../src/core/debug-capabilities.ts"],"sourcesContent":["/**\r\n * Notification System - 核心通知系统\r\n *\r\n * 职责:\r\n * - 定义通知类型和分类\r\n * - 提供通知发送接口\r\n * - 管理通知上下文(当前 Agent ID)\r\n * - 节流高频通知\r\n *\r\n * 上下文隔离机制:\r\n * 使用 AsyncLocalStorage 实现 per-call 上下文隔离,\r\n * 确保同进程内多个并发的 Agent.onCall() 调用各自的通知路由正确。\r\n * 旧的全局 set/clear API 作为向后兼容 fallback 保留。\r\n */\r\n\r\nimport { AsyncLocalStorage } from 'node:async_hooks';\r\nimport type { Notification, NotificationCategory, LLMPhase } from './types.js';\r\nimport type { CallOutcome } from './lifecycle.js';\r\nimport { DebugHub } from './debug-hub.js';\r\n\r\n// ========== AsyncLocalStorage 上下文 ==========\r\n\r\ninterface NotificationScope {\r\n agentId: string;\r\n lastNotificationTime: number;\r\n lastLLMPhase: string | null;\r\n}\r\n\r\nconst notificationAls = new AsyncLocalStorage<NotificationScope>();\r\n\r\n/**\r\n * 在通知作用域中执行函数。\r\n *\r\n * 使用 AsyncLocalStorage 实现 per-call 上下文隔离,\r\n * 确保同进程内多个并发的 Agent.onCall() 调用各自的通知路由正确。\r\n * 在此作用域内调用的 emitNotification 会自动使用作用域的 agentId。\r\n *\r\n * @param agentId 当前调用的 Agent ID\r\n * @param fn 在作用域内执行的函数\r\n * @returns fn 的返回值\r\n */\r\nexport function runWithNotificationScope<T>(agentId: string, fn: () => T): T {\r\n return notificationAls.run(\r\n { agentId, lastNotificationTime: 0, lastLLMPhase: null },\r\n fn,\r\n );\r\n}\r\n\r\n// ========== 模块级 fallback(向后兼容) ==========\r\n\r\nlet _fallbackAgentId: string | null = null;\r\nlet _fallbackLastTime: number = 0;\r\nlet _fallbackLastPhase: string | null = null;\r\n\r\n/**\r\n * 持久化的 fallback scope 对象,字段代理到模块级变量。\r\n * 仅在无 ALS scope 时使用。\r\n */\r\nconst _fallbackScope: NotificationScope = {\r\n get agentId() { return _fallbackAgentId!; },\r\n get lastNotificationTime() { return _fallbackLastTime; },\r\n set lastNotificationTime(v: number) { _fallbackLastTime = v; },\r\n get lastLLMPhase() { return _fallbackLastPhase; },\r\n set lastLLMPhase(v: string | null) { _fallbackLastPhase = v; },\r\n};\r\n\r\n/**\r\n * 获取当前活跃的通知上下文。\r\n * 优先返回 ALS scope,回退到模块级 fallback。\r\n */\r\nfunction getActiveScope(): NotificationScope | null {\r\n const als = notificationAls.getStore();\r\n if (als) return als;\r\n if (_fallbackAgentId) return _fallbackScope;\r\n return null;\r\n}\r\n\r\n// ========== 节流 ==========\r\n\r\nconst THROTTLE_INTERVAL = 100;\r\n\r\n// ========== 公开 API ==========\r\n\r\n/**\r\n * 设置通知上下文(向后兼容)\r\n *\r\n * @deprecated 使用 runWithNotificationScope 替代。\r\n * 此函数仅设置模块级 fallback,不影响 ALS scope。\r\n *\r\n * @param agentId Agent ID\r\n */\r\nexport function _setNotificationAgent(agentId: string): void {\r\n _fallbackAgentId = agentId;\r\n}\r\n\r\n/**\r\n * 清除通知上下文(向后兼容)\r\n *\r\n * @deprecated 使用 runWithNotificationScope 替代。\r\n * 此函数仅清除模块级 fallback。\r\n */\r\nexport function _clearNotificationAgent(): void {\r\n _fallbackAgentId = null;\r\n _fallbackLastTime = 0;\r\n _fallbackLastPhase = null;\r\n}\r\n\r\n/**\r\n * 获取当前通知上下文的 Agent ID。\r\n * 优先返回 ALS scope 的 agentId,回退到模块级 fallback。\r\n */\r\nexport function _getCurrentNotificationAgent(): string | null {\r\n return notificationAls.getStore()?.agentId ?? _fallbackAgentId;\r\n}\r\n\r\n/**\r\n * 发送通知到 DebugHub\r\n * @param notification 通知对象\r\n */\r\nexport function emitNotification(notification: Notification): void {\r\n const scope = getActiveScope();\r\n if (!scope) {\r\n // 没有通知上下文,静默忽略\r\n return;\r\n }\r\n\r\n const now = Date.now();\r\n const notificationType = String(notification.type || '');\r\n const bypassThrottle = notificationType === 'call.start'\r\n || notificationType === 'call.finish'\r\n || notificationType === 'llm.complete';\r\n\r\n // 阶段转换检测:llm.char_count 的 phase 变化时绕过节流,\r\n // 确保用户立即看到 thinking→content→tool_calling 的阶段切换\r\n let phaseTransition = false;\r\n if (notificationType === 'llm.char_count') {\r\n const data = notification.data as Record<string, unknown> | undefined;\r\n const phase = typeof data?.phase === 'string' ? data.phase : '';\r\n if (phase && phase !== scope.lastLLMPhase) {\r\n phaseTransition = true;\r\n scope.lastLLMPhase = phase;\r\n }\r\n }\r\n\r\n // 节流:状态类通知需要节流,事件类通知不需要\r\n // 阶段转换也绕过节流\r\n if (notification.category === 'state' && !bypassThrottle && !phaseTransition) {\r\n const timeSinceLast = now - scope.lastNotificationTime;\r\n if (timeSinceLast < THROTTLE_INTERVAL) {\r\n // 跳过此次通知\r\n return;\r\n }\r\n scope.lastNotificationTime = now;\r\n } else if (phaseTransition) {\r\n // 阶段转换绕过节流,但仍需更新 lastNotificationTime\r\n // 否则紧随其后的同 phase 通知也会因为 timeSinceLast 过大而绕过\r\n scope.lastNotificationTime = now;\r\n }\r\n\r\n // 推送到 DebugHub\r\n const debugHub = DebugHub.getInstance();\r\n debugHub.pushNotification(scope.agentId, notification);\r\n}\r\n\r\n// ========== 通知构造函数 ==========\r\n\r\n/**\r\n * 创建 LLM 字符计数通知\r\n * @param charCount 当前累积字符数\r\n * @param phase LLM 生成阶段\r\n */\r\nexport function createLLMCharCount(\r\n charCount: number,\r\n phase: LLMPhase,\r\n extras?: {\r\n thinkingChars?: number;\r\n contentChars?: number;\r\n toolCallCount?: number;\r\n streamToolNames?: string[];\r\n },\r\n): Notification {\r\n return {\r\n type: 'llm.char_count',\r\n category: 'state',\r\n timestamp: Date.now(),\r\n data: {\r\n charCount,\r\n phase,\r\n ...(typeof extras?.thinkingChars === 'number' ? { thinkingChars: extras.thinkingChars } : {}),\r\n ...(typeof extras?.contentChars === 'number' ? { contentChars: extras.contentChars } : {}),\r\n ...(typeof extras?.toolCallCount === 'number' ? { toolCallCount: extras.toolCallCount } : {}),\r\n ...(Array.isArray(extras?.streamToolNames) && extras.streamToolNames.length > 0\r\n ? { streamToolNames: extras.streamToolNames }\r\n : {}),\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * 创建 LLM 完成通知\r\n * @param totalChars 总字符数\r\n */\r\nexport function createLLMComplete(totalChars: number): Notification {\r\n return {\r\n type: 'llm.complete',\r\n category: 'state',\r\n timestamp: Date.now(),\r\n data: {\r\n totalChars,\r\n },\r\n };\r\n}\r\n\r\n/** llm.retry 通知 data:单次模型请求内部的重试过程观测。 */\r\nexport interface LLMRetryData {\r\n /** waiting:进入退避等待;requesting:等待结束、即将重发请求 */\r\n phase: 'waiting' | 'requesting';\r\n /** 即将进行的第几次重试(1-based) */\r\n attempt: number;\r\n /** 适配器配置的最大重试次数 */\r\n maxRetries: number;\r\n /** 本次退避等待时长(ms),仅 waiting 阶段有值 */\r\n delayMs?: number;\r\n /** 分类后的错误类型(APIErrorType) */\r\n errorType?: string;\r\n /** HTTP 状态码(如有) */\r\n statusCode?: number;\r\n}\r\n\r\n/**\r\n * 创建 LLM 重试通知(事件类)\r\n *\r\n * 适配器在自动重试的等待前 / 重发前发射。这是纯观测信号,\r\n * 不改变 Call 的执行控制流。\r\n */\r\nexport function createLLMRetry(data: LLMRetryData): Notification {\r\n return {\r\n type: 'llm.retry',\r\n category: 'event',\r\n timestamp: Date.now(),\r\n data: { ...data },\r\n };\r\n}\r\n\r\n/**\r\n * 创建工具开始通知\r\n * @param toolName 工具名称\r\n */\r\nexport function createToolStart(toolName: string): Notification {\r\n return {\r\n type: 'tool.start',\r\n category: 'event',\r\n timestamp: Date.now(),\r\n data: {\r\n toolName,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * 创建工具完成通知\r\n * @param toolName 工具名称\r\n * @param success 是否成功\r\n * @param duration 耗时(毫秒)\r\n */\r\nexport function createToolComplete(\r\n toolName: string,\r\n success: boolean,\r\n duration: number\r\n): Notification {\r\n return {\r\n type: 'tool.complete',\r\n category: 'event',\r\n timestamp: Date.now(),\r\n data: {\r\n toolName,\r\n success,\r\n duration,\r\n },\r\n };\r\n}\r\n\r\n/** tool.progress 通知 data:工具执行中的进度信号(ticket 023,schema 定义)。 */\r\nexport interface ToolProgressData {\r\n /** LLM 生成的 call.id(与 tool_call 条目配对) */\r\n callId: string;\r\n /** 工具名称 */\r\n toolName: string;\r\n /** 工具开始执行的 Unix 毫秒时间戳 */\r\n startedAt: number;\r\n /** 已执行时长(毫秒),由发射方计算 */\r\n elapsedMs: number;\r\n /** 本调用的生效超时(毫秒);工具未声明 timeout 时为 null */\r\n timeoutMs: number | null;\r\n /** 输出尾部文本;由发射方截尾,本票只定 schema */\r\n outputTail?: string;\r\n}\r\n\r\n/**\r\n * 创建工具进度通知(state 类,自动获得通知系统节流)\r\n *\r\n * 执行中可见性通道(ADR-0005):长任务工具可周期性发射,前端据此渲染\r\n * \"已运行 Ns / 超时 Xm / 尾部输出\" 卡片。不进 session-events 审计流——\r\n * 审计关心终态(interrupted 字段已覆盖),不节流的事件流加尾部输出会让\r\n * 无头 jsonl 爆炸。发射方负责 outputTail 截尾。\r\n */\r\nexport function createToolProgress(data: ToolProgressData): Notification {\r\n return {\r\n type: 'tool.progress',\r\n category: 'state',\r\n timestamp: Date.now(),\r\n data: { ...data },\r\n };\r\n}\r\n\r\n/**\r\n * 创建 Call 开始通知\r\n */\r\nexport function createCallStart(): Notification {\r\n return {\r\n type: 'call.start',\r\n category: 'state',\r\n timestamp: Date.now(),\r\n data: {},\r\n };\r\n}\r\n\r\n/**\r\n * 创建 Call 结束通知\r\n *\r\n * data 携带完整结构化终态(CallOutcome),消费端(ViewerWorker / 前端 /\r\n * 审计)依据 status/reason/error 判断结果,不再解析文本。\r\n */\r\nexport function createCallFinish(outcome: CallOutcome): Notification {\r\n return {\r\n type: 'call.finish',\r\n category: 'state',\r\n timestamp: Date.now(),\r\n data: { ...outcome },\r\n };\r\n}\r\n\r\n/** call.finish 通知 data 的形状(即 CallOutcome)。 */\r\nexport type CallFinishData = CallOutcome;\r\n","/**\r\n * DebugHub - 全局多 Agent 调试中心\r\n *\r\n * 职责:\r\n * - 管理所有 Agent 的注册和注销\r\n * - 连接到独立的 Viewer Worker UDS 服务器\r\n * - 路由 Agent 消息到 Worker\r\n *\r\n * 设计原则:\r\n * - 单例模式,全局唯一\r\n * - 轻量:只做路由,不存储消息\r\n * - 直观:API 简单明了\r\n */\r\n\r\nimport { connect, type Socket } from 'net';\r\nimport {\r\n getDefaultUDSPath,\r\n type Message,\r\n type Tool,\r\n type AgentInfo,\r\n type DebugHubIPCMessage,\r\n type Notification,\r\n type RequestInputMsg,\r\n type HookInspectorSnapshot,\r\n type AgentOverviewSnapshot,\r\n type TodoPlanSnapshot,\r\n type UserInputRequest,\r\n type UserInputResponse,\r\n type UserInputAction,\r\n type ImageInput,\r\n} from './types.js';\r\nimport { ClawDebugClient } from './claw-debug-client.js';\r\nimport { getClawRuntimeUrl, resolveDebugTransportMode } from './debug-transport.js';\r\nimport { getDebugCapabilities, type DebugCapabilities } from './debug-capabilities.js';\r\n\r\n// 前向声明 Agent 类型(避免循环依赖)\r\ntype Agent = any;\r\n\r\n/**\r\n * Hub 内部存储的 Agent 数据\r\n */\r\ninterface AgentData {\r\n info: AgentInfo;\r\n agent: Agent;\r\n}\r\n\r\ninterface PendingInputRequest {\r\n agentId: string;\r\n resolve: (response: UserInputResponse) => void;\r\n reject: (error: Error) => void;\r\n timer?: NodeJS.Timeout;\r\n abortController?: AbortController;\r\n}\r\n\r\nexport class DebugHub {\r\n private static instance: DebugHub;\r\n private readonly transportMode: 'viewer-worker' | 'claw';\r\n private readonly clawClient?: ClawDebugClient;\r\n\r\n // ========== 状态 ==========\r\n private agents: Map<string, AgentData> = new Map();\r\n private nextId: number = 1;\r\n private readonly processId: string; // 进程唯一标识\r\n\r\n // 输入请求回调映射:requestId → owned request lifecycle\r\n private pendingInputRequests = new Map<string, PendingInputRequest>();\r\n private interruptHandlers = new Map<string, (agentId: string, clearQueue: boolean) => void | Promise<void>>();\r\n private globalInterruptHandler?: (agentId: string, clearQueue: boolean) => void | Promise<void>;\r\n\r\n // 活跃的输入请求元数据(用于重连恢复):agentId → requestInfo\r\n private activeInputRequests = new Map<string, {\r\n requestId: string;\r\n prompt: string;\r\n placeholder?: string;\r\n initialValue?: string;\r\n actions?: UserInputAction[];\r\n mode?: UserInputRequest['mode'];\r\n questions?: UserInputRequest['questions'];\r\n timestamp: number;\r\n }>();\r\n\r\n // UDS 客户端连接\r\n private udsClient?: Socket;\r\n private udsPath: string;\r\n private workerReadBuffer: string = '';\r\n private workerPort: number | null = null;\r\n private clientReady: boolean = false;\r\n private stopped: boolean = false;\r\n /** Shared connection barrier for every Agent hosted by this process. */\r\n private connectionPromise?: Promise<void>;\r\n /** headless 运行时(无 ViewerWorker)下每类消息只告警一次,避免刷屏。 */\r\n private sendToWorkerWarnedTypes: Set<string> = new Set();\r\n\r\n // 注册锁(防止并发竞争)\r\n private registrationLock: boolean = false;\r\n\r\n // 重连机制\r\n private reconnectTimer?: NodeJS.Timeout;\r\n private reconnectAttempts: number = 0;\r\n private readonly MAX_RECONNECT_ATTEMPTS = 10;\r\n private readonly RECONNECT_DELAY = 2000;\r\n\r\n // 缓存每个 Agent 的模板装载载荷(mounts + entries,用于重连后重新注册)\r\n private agentTemplatePayload: Map<string, { mounts: string[]; entries: Record<string, { mount: number; rel: string }> }> = new Map();\r\n\r\n // 缓存每个 Agent 的外部输入策略(用于重连后重新注册)\r\n private agentInputPolicy: Map<string, 'standard' | 'none'> = new Map();\r\n\r\n // ========== 单例 ==========\r\n private constructor() {\r\n this.udsPath = process.env.AGENTDEV_UDS_PATH || getDefaultUDSPath();\r\n // 使用进程 PID 作为唯一标识,确保多进程环境下 Agent ID 不冲突\r\n this.processId = String(process.pid);\r\n this.transportMode = resolveDebugTransportMode();\r\n if (this.transportMode === 'claw') {\r\n this.clawClient = new ClawDebugClient({\r\n processId: this.processId,\r\n projectRoot: process.cwd(),\r\n });\r\n }\r\n }\r\n\r\n static getInstance(): DebugHub {\r\n if (!DebugHub.instance) {\r\n DebugHub.instance = new DebugHub();\r\n }\r\n return DebugHub.instance;\r\n }\r\n\r\n /**\r\n * 为 Agent 分配进程内稳定且唯一的运行时 ID。\r\n * 分配不等于注册;同一 ID 可在后续 Viewer attach/re-attach 时复用。\r\n */\r\n allocateAgentId(): string {\r\n return `agent-${this.nextId++}-${this.processId}`;\r\n }\r\n\r\n // ========== 公开 API ==========\r\n\r\n /**\r\n * 启动调试服务器\r\n * @param port HTTP 端口(默认 2026,仅用于显示)\r\n * @param openBrowser 是否自动打开浏览器(默认 true,已废弃参数)\r\n */\r\n async start(port: number = 2026, openBrowser: boolean = true): Promise<void> {\r\n if (this.clientReady) return;\r\n if (this.connectionPromise) return this.connectionPromise;\r\n\r\n const pending = this.startInternal(port, openBrowser);\r\n this.connectionPromise = pending;\r\n try {\r\n await pending;\r\n } finally {\r\n if (this.connectionPromise === pending) {\r\n this.connectionPromise = undefined;\r\n }\r\n }\r\n }\r\n\r\n private async startInternal(port: number, openBrowser: boolean): Promise<void> {\r\n this.stopped = false;\r\n if (this.transportMode === 'claw') {\r\n this.workerPort = port;\r\n try {\r\n await this.clawClient?.ping();\r\n this.clientReady = true;\r\n console.log(`[DebugHub] 已连接到 Claw runtime: ${getClawRuntimeUrl()}`);\r\n } catch (err) {\r\n console.warn(`[DebugHub] 无法连接到 Claw runtime: ${(err as Error).message}`);\r\n console.warn('[DebugHub] 调试功能将被禁用。请先启动 AgentDevClaw runtime。');\r\n this.clientReady = false;\r\n throw err;\r\n }\r\n return;\r\n }\r\n\r\n this.workerPort = port; // 保留用于信息显示\r\n this.openBrowser = openBrowser; // 保存浏览器打开设置\r\n \r\n try {\r\n await this.connectToWorker();\r\n console.log(`[DebugHub] 调试服务器已连接: http://localhost:${port}`);\r\n } catch (err) {\r\n // 连接失败,尝试自动启动 ViewerWorker\r\n console.log(`[DebugHub] ViewerWorker 未运行,正在自动启动...`);\r\n try {\r\n await this.spawnViewerWorker();\r\n // 等待服务器启动\r\n await new Promise(resolve => setTimeout(resolve, 1000));\r\n // 再次尝试连接\r\n await this.connectToWorker();\r\n console.log(`[DebugHub] 调试服务器已连接: http://localhost:${port}`);\r\n } catch (spawnErr) {\r\n console.warn(`[DebugHub] 无法启动 ViewerWorker: ${(spawnErr as Error).message}`);\r\n console.warn(`[DebugHub] 调试功能将被禁用。请手动运行 'agentdev-viewer' 启动调试服务器。`);\r\n this.clientReady = false;\r\n // A Viewer-backed Agent cannot safely continue without its transport:\r\n // registration/input messages would otherwise be silently discarded.\r\n throw spawnErr;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 自动启动 ViewerWorker 进程\r\n */\r\n private openBrowser: boolean = true;\r\n private viewerWorkerProcess?: ReturnType<typeof import('child_process').spawn>;\r\n\r\n private async spawnViewerWorker(): Promise<void> {\r\n const { spawn } = await import('child_process');\r\n const { existsSync } = await import('fs');\r\n const { fileURLToPath } = await import('url');\r\n const { dirname, join } = await import('path');\r\n\r\n // 查找 viewer.js 的路径\r\n let viewerPath: string;\r\n\r\n // 方式1: 优先使用 require.resolve(最可靠)\r\n try {\r\n const agentdevPath = require.resolve('agentdev/package.json');\r\n viewerPath = join(dirname(agentdevPath), 'dist', 'cli', 'viewer.js');\r\n } catch {\r\n // 方式2: 从当前模块解析\r\n try {\r\n const currentDir = dirname(fileURLToPath(import.meta.url));\r\n viewerPath = join(currentDir, '..', 'cli', 'viewer.js');\r\n } catch {\r\n // 方式3: 使用 bin 命令\r\n viewerPath = 'agentdev-viewer';\r\n }\r\n }\r\n\r\n // 验证文件存在\r\n if (viewerPath !== 'agentdev-viewer' && !existsSync(viewerPath)) {\r\n // 回退到 bin 命令\r\n viewerPath = 'agentdev-viewer';\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n try {\r\n // 设置环境变量\r\n const env = {\r\n ...process.env,\r\n AGENTDEV_PORT: String(this.workerPort || 2026),\r\n AGENTDEV_OPEN_BROWSER: this.openBrowser ? 'true' : 'false',\r\n AGENTDEV_UDS_PATH: this.udsPath,\r\n };\r\n\r\n console.log(`[DebugHub] 启动 ViewerWorker: ${viewerPath}`);\r\n\r\n // 如果是 bin 命令,直接运行命令;否则用 node 执行\r\n const isBinCommand = viewerPath === 'agentdev-viewer' || viewerPath === 'agentdev-server';\r\n const command = isBinCommand ? viewerPath : 'node';\r\n const args = isBinCommand ? [] : [viewerPath];\r\n\r\n this.viewerWorkerProcess = spawn(command, args, {\r\n env,\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n detached: false,\r\n shell: isBinCommand, // bin 命令需要 shell 来解析\r\n });\r\n\r\n this.viewerWorkerProcess.on('error', (err: Error) => {\r\n console.error('[DebugHub] ViewerWorker 进程错误:', err.message);\r\n reject(err);\r\n });\r\n\r\n // 输出 ViewerWorker 的日志\r\n this.viewerWorkerProcess.stdout?.on('data', (data: Buffer) => {\r\n const lines = data.toString().trim().split('\\n');\r\n for (const line of lines) {\r\n console.log(`[ViewerWorker] ${line}`);\r\n }\r\n });\r\n\r\n this.viewerWorkerProcess.stderr?.on('data', (data: Buffer) => {\r\n const lines = data.toString().trim().split('\\n');\r\n for (const line of lines) {\r\n console.error(`[ViewerWorker] ${line}`);\r\n }\r\n });\r\n\r\n // 给进程一点时间启动\r\n setTimeout(() => {\r\n if (this.viewerWorkerProcess && !this.viewerWorkerProcess.killed) {\r\n resolve();\r\n } else {\r\n reject(new Error('ViewerWorker 进程启动失败'));\r\n }\r\n }, 500);\r\n } catch (err) {\r\n reject(err);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * 停止调试服务器\r\n */\r\n stop(): void {\r\n this.stopped = true;\r\n if (this.transportMode === 'claw') {\r\n this.clientReady = false;\r\n return;\r\n }\r\n\r\n // 停止重连定时器\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = undefined;\r\n }\r\n\r\n if (this.udsClient) {\r\n this.sendToWorker({ type: 'stop' });\r\n this.udsClient.end();\r\n this.udsClient = undefined;\r\n this.clientReady = false;\r\n }\r\n }\r\n\r\n /**\r\n * 手动重连(可选)\r\n * 如果已经连接,则不执行任何操作\r\n */\r\n async reconnect(): Promise<void> {\r\n if (this.transportMode === 'claw') {\r\n await this.start(this.workerPort ?? 2026, false);\r\n if (!this.clientReady) {\r\n throw new Error('Claw runtime reconnect failed');\r\n }\r\n return;\r\n }\r\n\r\n if (this.clientReady && this.udsClient) {\r\n console.log('[DebugHub] 已经连接,无需重连');\r\n return;\r\n }\r\n\r\n // 重置重连状态\r\n this.reconnectAttempts = 0;\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = undefined;\r\n }\r\n\r\n try {\r\n await this.connectToWorker();\r\n console.log('[DebugHub] ✅ 手动重连成功');\r\n } catch (error) {\r\n console.error(`[DebugHub] 手动重连失败: ${(error as Error).message}`);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 注册 Agent\r\n * @param agent Agent 实例\r\n * @param name 显示名称(可选,默认使用类名)\r\n * @param templateMounts 模板装载点(真实目录根,可选)\r\n * @param templateEntries 模板名 → {mount 下标, rel 相对路径}(可选)\r\n * @returns 分配的 agentId\r\n */\r\n registerAgent(\r\n agent: Agent,\r\n name?: string,\r\n templateMounts?: string[],\r\n templateEntries?: Record<string, { mount: number; rel: string }>,\r\n hookInspector?: HookInspectorSnapshot,\r\n overview?: AgentOverviewSnapshot,\r\n projectRoot?: string,\r\n reservedAgentId?: string,\r\n inputPolicy?: 'standard' | 'none',\r\n ): string {\r\n // 等待注册锁\r\n while (this.registrationLock) {\r\n // 简单的忙等待(实际场景中竞争很少)\r\n }\r\n this.registrationLock = true;\r\n\r\n try {\r\n const resolvedProjectRoot = projectRoot || process.cwd();\r\n const id = reservedAgentId || this.allocateAgentId();\r\n const existing = this.agents.get(id);\r\n if (existing && existing.agent !== agent) {\r\n throw new Error(`Agent ID '${id}' is already registered by another Agent instance`);\r\n }\r\n const info: AgentInfo = {\r\n id,\r\n name: name || agent.constructor.name,\r\n registeredAt: existing?.info.registeredAt ?? Date.now(),\r\n projectRoot: resolvedProjectRoot,\r\n };\r\n\r\n this.agents.set(id, { info, agent });\r\n\r\n // 缓存模板装载载荷(用于重连后重新注册)\r\n if (templateMounts || templateEntries) {\r\n this.agentTemplatePayload.set(id, {\r\n mounts: templateMounts ?? [],\r\n entries: templateEntries ?? {},\r\n });\r\n }\r\n\r\n // 缓存外部输入策略(用于重连后重新注册)\r\n if (inputPolicy) {\r\n this.agentInputPolicy.set(id, inputPolicy);\r\n }\r\n\r\n // 通知 Worker\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.registerAgent({\r\n agentId: id,\r\n name: info.name,\r\n projectRoot: resolvedProjectRoot,\r\n templateMounts: templateMounts ?? [],\r\n templateEntries: templateEntries ?? {},\r\n hookInspector,\r\n overview,\r\n inputPolicy: inputPolicy || undefined,\r\n }).catch(error => {\r\n console.error(`[DebugHub] Claw registerAgent 失败: ${(error as Error).message}`);\r\n });\r\n } else {\r\n this.sendToWorker({\r\n type: 'register-agent',\r\n agentId: id,\r\n name: info.name,\r\n createdAt: info.registeredAt,\r\n projectRoot: resolvedProjectRoot,\r\n templateMounts: templateMounts ?? [],\r\n templateEntries: templateEntries ?? {},\r\n hookInspector,\r\n overview,\r\n inputPolicy: inputPolicy || undefined,\r\n });\r\n }\r\n\r\n console.log(`[DebugHub] Agent 已注册: ${id} (${info.name})`);\r\n return id;\r\n } finally {\r\n this.registrationLock = false;\r\n }\r\n }\r\n\r\n /**\r\n * 注销 Agent\r\n * @param agentId Agent ID\r\n */\r\n unregisterAgent(agentId: string): void {\r\n this.interruptHandlers.delete(agentId);\r\n this.cancelInputRequests(agentId, `Agent '${agentId}' was unregistered`);\r\n this.activeInputRequests.delete(agentId);\r\n\r\n const deleted = this.agents.delete(agentId);\r\n if (deleted) {\r\n this.agentTemplatePayload.delete(agentId);\r\n this.agentInputPolicy.delete(agentId);\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.unregisterAgent(agentId).catch(error => {\r\n console.error(`[DebugHub] Claw unregisterAgent 失败: ${(error as Error).message}`);\r\n });\r\n } else {\r\n this.sendToWorker({ type: 'unregister-agent', agentId });\r\n }\r\n console.log(`[DebugHub] Agent 已注销: ${agentId}`);\r\n }\r\n }\r\n\r\n /**\r\n * 推送 Agent 消息\r\n * @param agentId Agent ID\r\n * @param messages 消息数组\r\n */\r\n pushMessages(agentId: string, messages: Message[]): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.pushMessages(agentId, messages).catch(error => {\r\n console.error(`[DebugHub] Claw pushMessages 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'push-messages',\r\n agentId,\r\n messages,\r\n });\r\n }\r\n\r\n /**\r\n * 注册 Agent 工具\r\n * @param agentId Agent ID\r\n * @param tools 工具数组\r\n */\r\n registerAgentTools(agentId: string, tools: Tool[]): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.registerTools(agentId, tools).catch(error => {\r\n console.error(`[DebugHub] Claw registerTools 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'register-tools',\r\n agentId,\r\n tools,\r\n });\r\n }\r\n\r\n updateAgentInspector(agentId: string, hookInspector: HookInspectorSnapshot): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.updateInspector(agentId, hookInspector).catch(error => {\r\n console.error(`[DebugHub] Claw updateInspector 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'update-agent-inspector',\r\n agentId,\r\n hookInspector,\r\n });\r\n }\r\n\r\n updateAgentOverview(agentId: string, overview: AgentOverviewSnapshot): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.updateOverview(agentId, overview).catch(error => {\r\n console.error(`[DebugHub] Claw updateOverview 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'update-agent-overview',\r\n agentId,\r\n overview,\r\n });\r\n }\r\n\r\n updateTodoPlan(agentId: string, plan: TodoPlanSnapshot): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.updateTodoPlan(agentId, plan).catch(error => {\r\n console.error(`[DebugHub] Claw updateTodoPlan 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'update-todo-plan',\r\n agentId,\r\n plan,\r\n } as DebugHubIPCMessage);\r\n }\r\n\r\n /**\r\n * 获取所有已注册的 Agent 信息\r\n */\r\n getAgentList(): AgentInfo[] {\r\n return Array.from(this.agents.values()).map(v => v.info);\r\n }\r\n\r\n isAgentRegistered(agentId: string): boolean {\r\n return this.agents.has(agentId);\r\n }\r\n\r\n getTransportMode(): 'viewer-worker' | 'claw' {\r\n return this.transportMode;\r\n }\r\n\r\n getCapabilities(): DebugCapabilities {\r\n return getDebugCapabilities();\r\n }\r\n\r\n setInterruptHandler(\r\n agentIdOrHandler: string | ((agentId: string, clearQueue: boolean) => void | Promise<void>) | undefined,\r\n handler?: (agentId: string, clearQueue: boolean) => void | Promise<void>,\r\n ): () => void {\r\n if (typeof agentIdOrHandler === 'string') {\r\n // per-agent 注册\r\n if (handler) {\r\n this.interruptHandlers.set(agentIdOrHandler, handler);\r\n return () => {\r\n if (this.interruptHandlers.get(agentIdOrHandler) === handler) {\r\n this.interruptHandlers.delete(agentIdOrHandler);\r\n }\r\n };\r\n } else {\r\n this.interruptHandlers.delete(agentIdOrHandler);\r\n return () => {};\r\n }\r\n } else {\r\n // 旧式全局 fallback\r\n this.globalInterruptHandler = agentIdOrHandler;\r\n return () => {\r\n if (this.globalInterruptHandler === agentIdOrHandler) {\r\n this.globalInterruptHandler = undefined;\r\n }\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * 根据 Agent 实例获取其 ID\r\n */\r\n getAgentId(agent: Agent): string | undefined {\r\n for (const [id, data] of this.agents) {\r\n if (data.agent === agent) {\r\n return id;\r\n }\r\n }\r\n return undefined;\r\n }\r\n\r\n /**\r\n * 获取 Worker 端口\r\n */\r\n getPort(): number | null {\r\n return this.workerPort;\r\n }\r\n\r\n /**\r\n * 检查是否已连接到 ViewerWorker\r\n */\r\n isConnected(): boolean {\r\n if (this.transportMode === 'claw') {\r\n return this.clientReady;\r\n }\r\n return this.clientReady && !!this.udsClient;\r\n }\r\n\r\n /**\r\n * 推送通知\r\n * @param agentId Agent ID\r\n * @param notification 通知对象\r\n */\r\n pushNotification(agentId: string, notification: Notification): void {\r\n if (this.transportMode === 'claw') {\r\n void this.clawClient?.pushNotification(agentId, notification).catch(error => {\r\n console.error(`[DebugHub] Claw pushNotification 失败: ${(error as Error).message}`);\r\n });\r\n return;\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'push-notification',\r\n agentId,\r\n notification,\r\n });\r\n }\r\n\r\n /**\r\n * 请求用户输入\r\n * @param agentId Agent ID\r\n * @param prompt 提示信息\r\n * @param timeout 超时时间(毫秒),默认 Infinity(无限等待)\r\n * @returns Promise<string> 用户输入内容\r\n */\r\n requestUserInput(agentId: string, prompt: string, timeout: number = Infinity): Promise<string> {\r\n return this.requestUserInputEvent(agentId, { prompt }, timeout).then((response) => {\r\n if (response.kind !== 'text') {\r\n throw new Error(`Expected text user input but received action '${response.actionId ?? 'unknown'}'`);\r\n }\r\n return response.text ?? '';\r\n });\r\n }\r\n\r\n requestUserInputEvent(\r\n agentId: string,\r\n request: UserInputRequest,\r\n timeout: number = Infinity,\r\n ): Promise<UserInputResponse> {\r\n // 输入是每个 Agent 的单一租约,不允许多个 Promise 同时争抢下一次回复。\r\n if (this.activeInputRequests.has(agentId)) {\r\n return Promise.reject(new Error(`Agent '${agentId}' already has an active user input lease`));\r\n }\r\n\r\n if (this.transportMode === 'claw') {\r\n if (!this.clawClient) {\r\n return Promise.reject(new Error('Claw client is not available'));\r\n }\r\n const localRequestId = `claw-input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\r\n const abortController = new AbortController();\r\n return new Promise((resolve, reject) => {\r\n this.pendingInputRequests.set(localRequestId, {\r\n agentId,\r\n resolve,\r\n reject,\r\n abortController,\r\n });\r\n this.activeInputRequests.set(agentId, {\r\n requestId: localRequestId,\r\n prompt: request.prompt,\r\n placeholder: request.placeholder,\r\n initialValue: request.initialValue,\r\n actions: request.actions,\r\n mode: request.mode,\r\n questions: request.questions,\r\n timestamp: Date.now(),\r\n });\r\n this.clawClient!.requestUserInput(agentId, request, timeout, abortController.signal)\r\n .then(response => this.settleInputRequest(localRequestId, { response }))\r\n .catch(error => this.settleInputRequest(localRequestId, {\r\n error: error instanceof Error ? error : new Error(String(error)),\r\n }));\r\n });\r\n }\r\n\r\n const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\r\n\r\n return new Promise((resolve, reject) => {\r\n const pending: PendingInputRequest = { agentId, resolve, reject };\r\n if (timeout !== Infinity) {\r\n pending.timer = setTimeout(() => {\r\n this.settleInputRequest(requestId, {\r\n error: new Error(`User input timeout after ${timeout}ms`),\r\n });\r\n }, timeout);\r\n }\r\n\r\n this.pendingInputRequests.set(requestId, pending);\r\n\r\n // 记录活跃请求(用于重连恢复)\r\n this.activeInputRequests.set(agentId, {\r\n requestId,\r\n prompt: request.prompt,\r\n placeholder: request.placeholder,\r\n initialValue: request.initialValue,\r\n actions: request.actions,\r\n mode: request.mode,\r\n questions: request.questions,\r\n timestamp: Date.now(),\r\n });\r\n\r\n // 发送请求到 ViewerWorker\r\n this.sendToWorker({\r\n type: 'request-input',\r\n agentId,\r\n requestId,\r\n prompt: request.prompt,\r\n placeholder: request.placeholder,\r\n initialValue: request.initialValue,\r\n actions: request.actions,\r\n mode: request.mode,\r\n questions: request.questions,\r\n timeout,\r\n } as RequestInputMsg);\r\n });\r\n }\r\n\r\n // ========== 内部方法 ==========\r\n\r\n /**\r\n * 连接到 UDS 服务器\r\n */\r\n private async connectToWorker(): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const socket = connect(this.udsPath);\r\n this.udsClient = socket;\r\n\r\n socket.on('connect', () => {\r\n this.workerReadBuffer = '';\r\n this.clientReady = true;\r\n this.reconnectAttempts = 0; // 重置重连计数\r\n console.log(`[DebugHub] 已连接到 ViewerWorker: ${this.udsPath}`);\r\n\r\n // 关键:重新注册所有 Agent(用于重连后恢复状态)\r\n this.reregisterAllAgents();\r\n\r\n resolve();\r\n });\r\n\r\n socket.setEncoding('utf8');\r\n socket.on('data', (data: string) => {\r\n this.workerReadBuffer += data;\r\n const lines = this.workerReadBuffer.split('\\n');\r\n this.workerReadBuffer = lines.pop() ?? '';\r\n for (const line of lines) {\r\n if (!line.trim()) continue;\r\n try {\r\n const msg = JSON.parse(line);\r\n this.handleWorkerMessage(msg);\r\n } catch (err) {\r\n console.error('[DebugHub] Worker 消息解析失败:', err);\r\n }\r\n }\r\n });\r\n\r\n socket.on('error', (err: Error) => {\r\n if (this.udsClient === socket) {\r\n this.udsClient = undefined;\r\n this.clientReady = false;\r\n }\r\n reject(new Error(`连接 ViewerWorker 失败 (${this.udsPath}): ${err.message}\\n请先启动 ViewerWorker 服务器`));\r\n });\r\n\r\n socket.on('close', () => {\r\n this.workerReadBuffer = '';\r\n this.clientReady = false;\r\n if (this.udsClient === socket) {\r\n this.udsClient = undefined;\r\n }\r\n console.warn('[DebugHub] 与 ViewerWorker 的连接已断开');\r\n\r\n if (!this.stopped) {\r\n this.scheduleReconnect();\r\n }\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * 处理来自 Worker 的消息\r\n */\r\n private handleWorkerMessage(msg: any): void {\r\n switch (msg.type) {\r\n // 处理用户输入响应\r\n case 'input-response': {\r\n if (this.pendingInputRequests.has(msg.requestId)) {\r\n this.settleInputRequest(msg.requestId, { response: msg.response ?? {\r\n kind: 'text',\r\n text: msg.input,\r\n } });\r\n } else {\r\n console.warn(`[DebugHub] 未知输入响应: ${msg.requestId}`);\r\n }\r\n break;\r\n }\r\n\r\n // 处理中断信号\r\n case 'interrupt-agent': {\r\n console.log(`[DebugHub] 收到中断信号: agentId=${msg.agentId}, knownAgents=[${[...this.agents.keys()].join(',')}]`);\r\n const agentData = this.agents.get(msg.agentId);\r\n console.log(`[DebugHub] agentData found=${!!agentData}, hasAgent=${!!agentData?.agent}, hasInterrupt=${typeof (agentData?.agent as any)?.interrupt}`);\r\n let interruptAccepted = false;\r\n if (agentData?.agent && typeof (agentData.agent as any).interrupt === 'function') {\r\n interruptAccepted = (agentData.agent as any).interrupt() === true;\r\n console.log(`[DebugHub] agent.interrupt() returned: ${interruptAccepted}`);\r\n } else {\r\n console.warn(`[DebugHub] Agent 不支持中断: ${msg.agentId}`);\r\n }\r\n // 中断生效时同步取消该 Agent 挂起的交互输入请求。工具内的输入\r\n // 等待已被 tool-executor 的 abort 竞争丢弃,若不在此结算 DebugHub\r\n // 租约,宿主输入循环重开 getUserInputEvent 会被 \"already has an\r\n // active user input lease\" 永久拒绝;同时 cancelInputRequests 会\r\n // 通知 Worker 清除 inputLease,解除对后续 user-turn 的阻塞。\r\n // 空闲宿主输入槽(interrupt 未被接受)不受影响。\r\n if (interruptAccepted) {\r\n this.cancelInputRequests(msg.agentId, `Agent '${msg.agentId}' was interrupted`);\r\n }\r\n const specificHandler = this.interruptHandlers.get(msg.agentId);\r\n const effectiveHandler = specificHandler ?? this.globalInterruptHandler;\r\n if (effectiveHandler) {\r\n Promise.resolve(effectiveHandler(msg.agentId, msg.clearQueue === true)).catch((error) => {\r\n console.error('[DebugHub] interruptHandler 执行失败:', error);\r\n });\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 重新注册所有 Agent(重连后调用)\r\n * 确保 ViewerWorker 能够恢复所有 Agent 的注册信息\r\n */\r\n private reregisterAllAgents(): void {\r\n if (this.transportMode === 'claw') {\r\n for (const [id, data] of this.agents) {\r\n const hookInspector = (data.agent as any).buildHookInspectorSnapshot?.()\r\n || (data.agent as any).hookInspector;\r\n const overview = (data.agent as any).buildOverviewSnapshot?.();\r\n const templatePayload = this.agentTemplatePayload.get(id) || { mounts: [], entries: {} };\r\n\r\n void this.clawClient?.registerAgent({\r\n agentId: id,\r\n name: data.info.name,\r\n projectRoot: data.info.projectRoot || process.cwd(),\r\n templateMounts: templatePayload.mounts,\r\n templateEntries: templatePayload.entries,\r\n hookInspector,\r\n overview,\r\n inputPolicy: this.agentInputPolicy.get(id) || undefined,\r\n }).then(async () => {\r\n const tools = (data.agent as any).tools;\r\n if (tools && typeof tools.getEntries === 'function') {\r\n const entries = tools.getEntries();\r\n const toolList = entries.map((e: any) => e.tool);\r\n if (toolList.length > 0) {\r\n await this.clawClient?.registerTools(id, toolList);\r\n }\r\n }\r\n\r\n const context = (data.agent as any).getContext?.();\r\n if (context && typeof context.getAll === 'function') {\r\n const messages = context.getAll();\r\n if (messages.length > 0) {\r\n await this.clawClient?.pushMessages(id, messages);\r\n }\r\n }\r\n }).catch(error => {\r\n console.error(`[DebugHub] Claw re-register 失败: ${(error as Error).message}`);\r\n });\r\n }\r\n return;\r\n }\r\n\r\n if (this.agents.size === 0) {\r\n return;\r\n }\r\n\r\n console.log(`[DebugHub] 重新注册 ${this.agents.size} 个 Agent...`);\r\n\r\n for (const [id, data] of this.agents) {\r\n // 获取最新的 hookInspector\r\n const hookInspector = (data.agent as any).buildHookInspectorSnapshot?.()\r\n || (data.agent as any).hookInspector;\r\n const overview = (data.agent as any).buildOverviewSnapshot?.();\r\n\r\n // 获取缓存的模板装载载荷\r\n const templatePayload = this.agentTemplatePayload.get(id) || { mounts: [], entries: {} };\r\n\r\n // 获取活跃的输入请求(用于恢复输入框)\r\n const activeInputRequest = this.activeInputRequests.get(id);\r\n if (activeInputRequest) {\r\n console.log(`[DebugHub] 发现活跃输入请求: ${activeInputRequest.requestId}`);\r\n }\r\n\r\n this.sendToWorker({\r\n type: 'register-agent' as const,\r\n agentId: id,\r\n name: data.info.name,\r\n createdAt: data.info.registeredAt,\r\n projectRoot: data.info.projectRoot || process.cwd(),\r\n templateMounts: templatePayload.mounts,\r\n templateEntries: templatePayload.entries,\r\n hookInspector,\r\n overview,\r\n activeInputRequest, // 携带活跃输入请求\r\n inputPolicy: this.agentInputPolicy.get(id) || undefined,\r\n });\r\n\r\n // 重新注册工具(如果有)\r\n const tools = (data.agent as any).tools;\r\n if (tools && typeof tools.getEntries === 'function') {\r\n const entries = tools.getEntries();\r\n const toolList = entries.map((e: any) => e.tool);\r\n if (toolList.length > 0) {\r\n this.sendToWorker({\r\n type: 'register-tools',\r\n agentId: id,\r\n tools: toolList,\r\n });\r\n }\r\n }\r\n\r\n // 重新发送对话记录(用于重连后恢复消息历史)\r\n const context = (data.agent as any).getContext?.();\r\n if (context && typeof context.getAll === 'function') {\r\n const messages = context.getAll();\r\n if (messages.length > 0) {\r\n this.sendToWorker({\r\n type: 'push-messages',\r\n agentId: id,\r\n messages,\r\n });\r\n console.log(`[DebugHub] 恢复 Agent ${id} 的 ${messages.length} 条消息`);\r\n }\r\n }\r\n }\r\n\r\n console.log(`[DebugHub] ✅ 重新注册完成`);\r\n }\r\n\r\n /**\r\n * 安排重连(指数退避)\r\n */\r\n private scheduleReconnect(): void {\r\n if (this.stopped) {\r\n return;\r\n }\r\n\r\n // 清除现有的定时器\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n }\r\n\r\n // 检查是否达到最大重连次数\r\n if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {\r\n console.error(`[DebugHub] 达到最大重连次数 (${this.MAX_RECONNECT_ATTEMPTS}),停止重连`);\r\n return;\r\n }\r\n\r\n this.reconnectAttempts++;\r\n\r\n // 计算延迟时间(指数退避,最大 30 秒)\r\n const delay = Math.min(\r\n this.RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts - 1),\r\n 30000\r\n );\r\n\r\n console.log(`[DebugHub] ${delay}ms 后尝试第 ${this.reconnectAttempts} 次重连...`);\r\n\r\n this.reconnectTimer = setTimeout(async () => {\r\n try {\r\n await this.connectToWorker();\r\n console.log('[DebugHub] ✅ 重连成功,调试功能已恢复');\r\n } catch (error) {\r\n console.error(`[DebugHub] 重连失败: ${(error as Error).message}`);\r\n // 继续尝试重连\r\n if (!this.stopped) {\r\n this.scheduleReconnect();\r\n }\r\n }\r\n }, delay);\r\n }\r\n\r\n /**\r\n * 通过 UDS 发送消息\r\n */\r\n private sendViaUDS(msg: DebugHubIPCMessage): void {\r\n if (this.udsClient && this.clientReady) {\r\n this.udsClient.write(JSON.stringify(msg) + '\\n');\r\n }\r\n // 未连接时丢弃消息,不再队列(避免内存泄漏)\r\n }\r\n\r\n /** Cancel every input request owned by one Agent. Safe to call repeatedly. */\r\n cancelInputRequests(agentId: string, reason: string = `Agent '${agentId}' input was cancelled`): void {\r\n for (const [requestId, pending] of this.pendingInputRequests) {\r\n if (pending.agentId !== agentId) continue;\r\n pending.abortController?.abort();\r\n this.settleInputRequest(requestId, { error: this.createAbortError(reason) });\r\n // Worker 持有同名 inputLease(HTTP 投递面);结算后必须同步通知\r\n // 清除,否则前端永远提交不到该 requestId,陈旧租约会阻塞后续\r\n // user-turn 与新输入租约的重开。按 requestId 匹配,不会误删\r\n // 紧随其后打开的新租约。\r\n this.sendToWorker({ type: 'input-request-cancelled', agentId, requestId });\r\n }\r\n this.activeInputRequests.delete(agentId);\r\n }\r\n\r\n private settleInputRequest(\r\n requestId: string,\r\n outcome: { response: UserInputResponse } | { error: Error },\r\n ): void {\r\n const pending = this.pendingInputRequests.get(requestId);\r\n if (!pending) return;\r\n\r\n this.pendingInputRequests.delete(requestId);\r\n if (pending.timer) clearTimeout(pending.timer);\r\n const active = this.activeInputRequests.get(pending.agentId);\r\n if (active?.requestId === requestId) {\r\n this.activeInputRequests.delete(pending.agentId);\r\n }\r\n\r\n if ('response' in outcome) {\r\n pending.resolve(outcome.response);\r\n } else {\r\n pending.reject(outcome.error);\r\n }\r\n }\r\n\r\n private createAbortError(message: string): Error {\r\n const error = new Error(message);\r\n error.name = 'AbortError';\r\n return error;\r\n }\r\n\r\n /**\r\n * 发送消息到 Worker\r\n */\r\n private sendToWorker(msg: DebugHubIPCMessage): void {\r\n if (!this.udsClient || !this.clientReady) {\r\n // Do not present this as a delivered message. Stateful registrations and\r\n // input leases are reconciled by re-registerAllAgents on reconnect;\r\n // this warning keeps non-replayable debug updates observable instead of\r\n // silently pretending the transport accepted them. Warn once per message\r\n // type: headless runtimes (Test Runtime, one-shot agents) never connect.\r\n if (!this.sendToWorkerWarnedTypes.has(msg.type)) {\r\n this.sendToWorkerWarnedTypes.add(msg.type);\r\n console.warn(`[DebugHub] ViewerWorker transport is not ready; deferred state will reconcile on reconnect (message=${msg.type})`);\r\n }\r\n return;\r\n }\r\n this.sendViaUDS(msg);\r\n }\r\n}\r\n","/**\n * 基础类型定义\n * 所有类型集中在这里,简单直观\n */\n\n// continuation 类型仅作引用,纯类型 import 无循环依赖\nimport type { CallContinuationRequest } from './continuation.js';\n\n// ========== 通知系统类型 ==========\n\n/**\n * 通知分类\n * - state: 覆盖式更新(如 LLM 字符计数)\n * - event: 追加式记录(如工具开始/完成)\n */\nexport type NotificationCategory = 'state' | 'event';\n\n/**\n * LLM 生成阶段\n */\nexport type LLMPhase = 'thinking' | 'content' | 'tool_calling';\n\n/**\n * 通知基础接口\n */\nexport interface Notification {\n type: string;\n category: NotificationCategory;\n timestamp: number;\n data: unknown;\n}\n\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';\n\nexport type DebugLogDeliveryReason =\n | 'hub'\n | 'hub-unavailable'\n | 'no-agent-context';\n\nexport interface DebugLogDelivery {\n hub: boolean;\n console: boolean;\n reason: DebugLogDeliveryReason;\n}\n\nexport interface LogContextRef {\n agentId?: string;\n agentName?: string;\n parentAgentId?: string;\n callIndex?: number;\n step?: number;\n toolName?: string;\n toolCallId?: string;\n feature?: string;\n lifecycle?: string;\n hookMethod?: string;\n hookKind?: 'forward' | 'observe' | 'guard' | 'transform';\n sourceFile?: string;\n sourceLine?: number;\n tags?: string[];\n [key: string]: unknown;\n}\n\nexport interface DebugLogEntry {\n id: string;\n timestamp: number;\n level: LogLevel;\n message: string;\n namespace: string;\n context: LogContextRef;\n data?: unknown;\n delivery: DebugLogDelivery;\n}\n\n/**\n * LLM 字符计数通知数据\n */\nexport interface LLMCharCountData {\n charCount: number;\n phase: LLMPhase;\n thinkingChars?: number;\n contentChars?: number;\n toolCallCount?: number;\n /** 流式期间已检测到的工具名称(tool_calling 阶段可用) */\n streamToolNames?: string[];\n}\n\n/**\n * LLM 完成通知数据\n */\nexport interface LLMCompleteData {\n totalChars: number;\n}\n\nexport interface ToolStartData {\n toolName: string;\n}\n\nexport type RuntimeStage =\n | 'idle'\n | 'llm_thinking'\n | 'llm_content'\n | 'llm_tool_call_building'\n | 'awaiting_runtime'\n | 'tool_executing'\n | 'retry_waiting'\n | 'retry_requesting'\n | 'completed'\n | 'failed'\n | 'cancelled';\n\n/**\n * 工具终止原因(ticket 023 / ADR-0005)\n *\n * - 'timeout':框架统一超时计时触发(Tool 声明 timeout 后生效)\n * - 'user':外部用户中断(Agent.interrupt() 触发的 abort signal)\n */\nexport type ToolTerminationReason = 'timeout' | 'user';\n\nexport interface AgentRuntimeStateSnapshot {\n stage: RuntimeStage;\n callActive: boolean;\n charCount: number;\n thinkingChars: number;\n contentChars: number;\n toolCallCount: number;\n activeToolNames: string[];\n activeToolCount: number;\n /** LLM 流式期间检测到的工具名称(仅 llm_tool_call_building 阶段有值) */\n streamToolNames?: string[];\n callStartedAt?: number;\n stageStartedAt?: number;\n retryAttempt?: number;\n maxRetries?: number;\n nextRetryDelayMs?: number;\n lastErrorType?: string | null;\n lastErrorMessage?: string | null;\n /** 最近一次已结束 Call 的结构化终态。 */\n lastOutcome?: import('./lifecycle.js').CallOutcome | null;\n updatedAt: number;\n}\n\n/**\n * 工具执行上下文\n *\n * 由框架在执行工具时注入。Feature 通过 {@link AgentFeature.getContextInjectors}\n * 可以扩展额外字段。\n */\nexport interface ToolExecutionContext {\n /** 中断信号,用于取消工具执行(外部用户中断与框架超时合并后的 signal) */\n signal?: AbortSignal;\n /** 当前工具调用的 LLM 生成 call.id(tool.progress 等进度信号配对用) */\n callId?: string;\n /**\n * 查询当前工具执行是否已被终止及终止原因(ticket 023)。\n *\n * 返回 null 表示尚未终止;工具可据此在结果中填写模型可读的终止元数据。\n * reason 不挂在 AbortSignal 上(signal 保持标准形状),统一经此函数查询。\n */\n termination?: () => ToolTerminationReason | null;\n /** 当前终止 settle 的绝对截止时间(epoch ms);工具用于把内部 drain 纳入同一预算。 */\n terminationDeadline?: () => number | null;\n /** 本次调用生效的超时(毫秒,含 args 覆盖后的 clamp 结果);仅声明 timeout 的工具注入(ticket 025 进度显示用) */\n timeoutMs?: number;\n /** 注册 continuation request(供 checkpoint/rollback 等控制流工具使用) */\n registerContinuationRequest?: (request: CallContinuationRequest) => void;\n /** Feature 通过 contextInjectors 注入的自定义属性 */\n [key: string]: unknown;\n}\n\n/**\n * 工具执行返回值类型\n *\n * 工具可以返回纯文本或结构化对象。框架会自动序列化非 string 返回值。\n */\nexport type ToolResultValue = string | Record<string, unknown>;\n\n/**\n * 工具定义\n */\nexport interface Tool {\n name: string;\n description: string;\n parameters?: Record<string, unknown>;\n /**\n * 执行工具\n *\n * @param args 工具参数(来自 LLM 的 JSON 解析结果)\n * @param context 执行上下文,包含框架注入的 signal、registerContinuationRequest,\n * 以及各 Feature 通过 contextInjectors 注入的自定义属性\n */\n execute: (args: Record<string, unknown>, context?: ToolExecutionContext) => Promise<ToolResultValue>;\n /** 可选:渲染配置 */\n render?: ToolRenderConfig;\n /**\n * 工具执行模式\n * - 'normal'(默认):普通工具,可与其他工具在同一次 assistant turn 中并行调用\n * - 'exclusive':独占工具,必须是 assistant turn 中唯一的工具调用\n *\n * exclusive 工具适用于控制流工具(如 checkpoint、rollback),\n * 它们不应与其他工具产生副作用交织。\n */\n executionMode?: 'normal' | 'exclusive';\n /**\n * 工具是否可并行执行。\n *\n * - true: 该工具可以与同批次中其他 parallelizable 工具并发执行\n * - false/undefined: 串行执行(默认,向后兼容)\n *\n * 约束:\n * - exclusive 工具忽略此属性(exclusive 总是独占批次)\n * - 标记为 parallelizable 的工具应是无副作用的只读操作,\n * 或其副作用不会与同批次其他工具冲突\n */\n parallelizable?: boolean;\n /**\n * 超时契约声明(ticket 023 / ADR-0005)。\n *\n * 声明后由框架执行器统一计时:超时触发合并 AbortSignal(reason=timeout),\n * 并给工具一个 settle 窗口优雅收尾;未声明的工具不受框架超时管辖,行为不变。\n *\n * - defaultMs: 默认超时(模型未通过 fromArg 参数覆盖时生效)\n * - maxMs: 生效超时的硬上限,任何来源的超时值都会被 clamp 到 [1, maxMs]\n * - fromArg: 可选参数名;声明后生效超时取 args[fromArg](数字),再 clamp\n */\n timeout?: {\n defaultMs: number;\n maxMs: number;\n fromArg?: string;\n };\n}\n\n/**\n * 工具完成通知数据\n */\nexport interface ToolCompleteData {\n toolName: string;\n success: boolean;\n duration: number;\n}\n\n/**\n * 通知状态响应(GET /api/agents/:id/notification)\n */\nexport interface NotificationStateResponse {\n state: Notification | null;\n event: Notification | null;\n runtime: AgentRuntimeStateSnapshot;\n callActive: boolean;\n hasNewEvents: boolean;\n}\n\nexport interface AgentLogsResponse {\n scope: 'current' | 'all';\n selectedAgentId: string | null;\n total: number;\n logs: DebugLogEntry[];\n truncation?: {\n truncated: boolean;\n appliedLimit?: number;\n returnedCount: number;\n availableCount: number;\n nextOffset?: number;\n reason?: string;\n guidance?: string;\n };\n collectionPolicy: {\n hubConnected: boolean;\n includesOnlyHubDeliveredLogs: boolean;\n fallbackBehavior: string;\n };\n}\n\n/**\n * Agent 连接状态响应(GET /api/agents/:id/connection)\n */\nexport interface AgentConnectionResponse {\n connected: boolean;\n}\n\n// ========== 消息类型 ==========\n\n// 消息角色(支持子代理 ID 作为消息来源)\nexport type MessageRole = 'system' | 'user' | 'assistant' | 'tool' | string;\n\n/**\n * 图片输入(多模态支持)\n *\n * 支持两种数据来源:\n * - `path`:图片已落盘到本地文件,编译 LLM 请求时按需读取为 base64(推荐)\n * - `base64`:内联 base64 数据(向后兼容旧会话)\n *\n * 优先使用 `path`;`base64` 仅作为旧会话兼容或无法落盘时的回退。\n * - 视觉模式(vision: true):从 path 或 base64 读取图片数据传给 LLM API\n * - 非视觉模式(vision: false):source 用于生成文字占位符\n */\nexport interface ImageInput {\n /** 本地文件绝对路径(推荐方式,避免 session 膨胀) */\n path?: string;\n /** Base64 编码的图片数据(不含 data URI 前缀),向后兼容 */\n base64?: string;\n /** MIME 类型,如 'image/png'、'image/jpeg' */\n mediaType?: string;\n /** 来源描述或原始文件名(用于非视觉模式的文字占位符显示) */\n source?: string;\n}\n\n// 消息结构\nexport interface Message {\n role: MessageRole;\n content: string;\n turn?: number;\n toolCallId?: string;\n toolCalls?: ToolCall[];\n reasoning?: string; // 思考内容(GLM-4.7等模型的扩展字段)\n thinkingBlocks?: ThinkingBlock[];\n /** 图片附件(user 消息:用户输入的图片;tool 消息:工具返回的图片),多模态输入 */\n images?: ImageInput[];\n /**\n * 前端展示数据(仅 tool 消息)。\n *\n * 当工具使用 withDisplay() 分离返回时,display 携带富数据(如 diff),\n * 仅供前端渲染,不注入 LLM 上下文。LLM 只看到 content 中的精简文本。\n */\n display?: unknown;\n /**\n * 消息来源标记(仅 system 消息使用)。\n *\n * - undefined:agent 自身的系统提示词(由 templateResolver 生成),\n * Anthropic provider 将其放入顶层 system 参数。\n * - 有值(如 'handoff-seed'、'partial-compact'):Feature 注入的 system 消息,\n * Anthropic provider 将其包裹为 <reminder> 嵌入最近的 user turn,\n * 而非混入顶层 system 参数。\n */\n source?: string;\n /**\n * 消息语义标签(所有 role 通用)。\n *\n * 与 source 的区别:\n * - source 控制 LLM 编译层行为(顶层 system vs reminder)\n * - tag 控制上下文管理层行为(trim/compact 时的保留策略)\n *\n * undefined = 无标签,向后兼容,行为与当前完全一致。\n */\n tag?: string;\n /**\n * LLM 用量信息(仅 assistant 消息有值)。\n *\n * 由 LLM provider 在生成响应时返回,表示生成此消息时的上下文 token 开销。\n * inputTokens 是发送给 LLM 的完整上下文大小(包含所有历史消息),\n * 不是单条消息的 token 数。\n */\n usage?: MessageUsage;\n /**\n * 执行终态元数据(仅 assistant 消息有值)。\n *\n * 框架在写入错误/截断等执行结果消息时盖戳。展示端(Web UI / 查看器)\n * 应依据此字段渲染终态样式,而不是解析 content 文本前缀;该字段随\n * 会话快照持久化,重渲染后保持稳定。\n */\n execution?: MessageExecutionMeta;\n}\n\n/**\n * 消息级执行终态摘要(Message.execution)\n *\n * CallOutcome 的可序列化子集;不重复 response/steps 等会话级信息。\n */\nexport interface MessageExecutionMeta {\n status: import('./lifecycle.js').ExecutionStatus;\n reason: import('./lifecycle.js').ExecutionReason;\n error?: import('./lifecycle.js').ExecutionError;\n}\n\n/**\n * 消息级用量记录(盖戳在 assistant 消息上)\n */\nexport interface MessageUsage {\n /** 生成此消息时,发送给 LLM 的总输入 token(即当时的完整上下文大小) */\n inputTokens: number;\n /** LLM 生成的输出 token */\n outputTokens: number;\n}\n\nexport interface ThinkingBlock {\n signature: string;\n thinking: string;\n}\n\n// 工具调用\nexport interface ToolCall {\n id: string;\n name: string;\n arguments: Record<string, any>;\n}\n\n// LLM 响应\nexport interface LLMResponse {\n content: string;\n toolCalls?: ToolCall[];\n reasoning?: string; // 思考内容(GLM-4.7等模型的扩展字段)\n thinkingBlocks?: ThinkingBlock[];\n /** 用量统计(可选) */\n usage?: UsageInfo;\n /** 停止原因,由 LLM API 返回(如 end_turn, tool_use, stop 等) */\n stopReason?: string | null;\n}\n\n// ============= 渲染模板类型 =============\n/**\n * 渲染模板项\n * 可以是字符串模板或函数模板\n */\nexport type RenderTemplateItem =\n | string // 字符串模板,使用 {{key}} 插值\n | RenderTemplateFn; // 函数模板,处理复杂逻辑\n\n/**\n * 渲染模板函数类型\n */\nexport type RenderTemplateFn = (data: Record<string, any>, success?: boolean) => string;\n\n/**\n * 内联渲染模板\n * 直接定义在工具中的渲染模板(无需引用预设模板)\n */\nexport interface InlineRenderTemplate {\n call: RenderTemplateItem;\n result: RenderTemplateItem;\n}\n\n// 工具渲染配置\nexport interface ToolRenderConfig {\n /** 调用时的渲染模板(字符串引用或内联模板) */\n call?: string | InlineRenderTemplate;\n /** 结果时的渲染模板(字符串引用或内联模板) */\n result?: string | InlineRenderTemplate;\n}\n\n// LLM 接口 - 所有 LLM 适配器都需要实现这个\nexport interface LLMClient {\n chat(messages: Message[], tools: Tool[], options?: LLMChatOptions): Promise<LLMResponse>;\n /** 可选:返回当前 LLM 实例使用的模型名(用于调试显示) */\n readonly modelName?: string;\n}\n\n// LLM 调用选项\nexport interface LLMChatOptions {\n /** 允许中断正在进行的 LLM 调用 */\n signal?: AbortSignal;\n /** 请求非流式响应(适用于一次性摘要、标题生成等场景) */\n noStream?: boolean;\n}\n\n/**\n * 可热更新的模型元数据(与 LLMClient 实例解耦)\n *\n * 用于 Agent.setLLM() 时传递模型上下文信息,\n * 供 Feature(如 ContextGuard)根据新模型的 contextLength 调整行为。\n */\nexport interface LLMMeta {\n modelName?: string;\n contextLength?: number | null;\n compressRatio?: number;\n presetName?: string;\n thinkingEffort?: string | null;\n}\n\n// 占位符上下文类型\nimport type { PlaceholderContext, TemplateSource } from '../template/types.js';\n\n// MCP 类型导入(契约类型,协议中性;实现在 @agentdevjs/mcp)\nimport type { MCPConfig } from './mcp-contract.js';\nimport type { UsageInfo, UsageStatsSnapshot } from './usage.js';\n\n// UsageInfo 权威定义位于 usage.ts;此处 re-export 保持 types.js 的既有导入路径\nexport type { UsageInfo };\n\n// Agent 配置\nexport interface AgentConfig {\n llm: LLMClient;\n tools?: Tool[];\n maxTurns?: number;\n systemMessage?: string | TemplateSource;\n name?: string; // Agent 显示名称(用于调试)\n projectRoot?: string;\n workspaceDir?: string;\n\n // ========== Feature 系统 ==========\n /**\n * Feature 配置\n *\n * 新的声明式 Feature 注册方式\n */\n features?: {\n /** 启用的 Feature 列表 */\n enabled?: string[];\n /** Feature 特定配置 */\n [key: string]: unknown;\n };\n}\n\n// 上下文中间件 - 用于处理消息数组\nexport type ContextMiddleware = (messages: Message[]) => Message[];\n\n// ============= 多 Agent 调试支持 =============\n\n/**\n * Agent 注册信息(Hub 端)\n */\nexport interface AgentInfo {\n id: string; // 唯一标识,如 \"agent-1\"\n name: string; // 显示名称\n registeredAt: number; // 注册时间戳\n projectRoot?: string;\n}\n\n/**\n * 工具元数据(用于前端渲染)\n */\nexport interface ToolMetadata {\n name: string;\n description: string;\n render: {\n call: string | InlineRenderTemplate; // 模板名称或内联模板\n result: string | InlineRenderTemplate; // 模板名称或内联模板\n // 内联模板的可选直接存储(用于前端特殊标记)\n inlineCall?: InlineRenderTemplate;\n inlineResult?: InlineRenderTemplate;\n };\n}\n\nexport interface HookSourceLocation {\n file?: string;\n line?: number;\n column?: number;\n display: string;\n}\n\nexport interface HookEntryMetadata {\n order: number;\n featureName: string;\n methodName: string;\n lifecycle: string;\n /** 三原语(observe / guard / transform),由静态声明或装饰器路径推导 */\n kind: 'observe' | 'guard' | 'transform';\n /** guard 角色(policy 先于 advisor 执行)。仅 kind='guard' 条目存在。 */\n role?: 'policy' | 'advisor';\n source?: HookSourceLocation;\n description?: string;\n /** 是否启用。false 表示被运行时禁用。缺省视为 true(向后兼容)。 */\n enabled?: boolean;\n}\n\nexport interface HookLifecycleSnapshot {\n lifecycle: string;\n /** 生命周期级三原语汇总:桶内有 guard → guard,有 transform → transform,否则 observe */\n kind: 'observe' | 'guard' | 'transform';\n entries: HookEntryMetadata[];\n}\n\nexport interface FeatureInspectorSnapshot {\n name: string;\n enabled: boolean;\n status: 'enabled' | 'disabled' | 'removed' | 'partial';\n hookCount: number;\n toolCount: number;\n enabledToolCount: number;\n source?: string;\n description?: string;\n tools: Array<{\n name: string;\n description: string;\n state: 'enabled' | 'disabled' | 'removed' | 'superseded';\n enabled?: boolean;\n renderCall?: string;\n renderResult?: string;\n parameters?: Record<string, unknown>;\n }>;\n}\n\nexport interface HookInspectorSnapshot {\n lifecycleOrder: string[];\n features: FeatureInspectorSnapshot[];\n hooks: HookLifecycleSnapshot[];\n standaloneTools?: Array<{\n name: string;\n description: string;\n state: 'enabled' | 'disabled' | 'removed' | 'superseded';\n enabled?: boolean;\n source?: string;\n renderCall?: string;\n renderResult?: string;\n parameters?: Record<string, unknown>;\n }>;\n}\n\nexport interface AgentContextMetrics {\n messageCount: number;\n charCount: number;\n toolCallCount: number;\n turnCount: number;\n}\n\nexport interface AgentOverviewSnapshot {\n updatedAt: number;\n context: AgentContextMetrics;\n usageStats: UsageStatsSnapshot;\n runtime?: AgentRuntimeStateSnapshot;\n /** 可选:当前使用的模型名(由 agent 实例注入) */\n modelName?: string;\n /** 可选:当前使用的预设名(由 agent 实例注入,用于 UI dropdown 高亮) */\n presetName?: string;\n /** 可选:当前 LLM 实例的思考强度(由 agent 实例注入,用于 UI 状态同步) */\n thinkingEffort?: string | null;\n /** 可选:当前模型的上下文窗口长度(由 agent 实例注入,用于 UI 用量条实时同步) */\n contextLength?: number;\n /** 可选:当前模型的压缩阈值百分比(由 agent 实例注入,用于 UI 用量条实时同步) */\n compressRatio?: number;\n}\n\nexport interface TodoTaskSnapshot {\n id: string;\n subject: string;\n description: string;\n status: 'pending' | 'in_progress' | 'completed' | 'deleted';\n metadata?: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface TodoPlanSummary {\n total: number;\n pending: number;\n inProgress: number;\n completed: number;\n cancelled: number;\n}\n\nexport interface TodoPlanSnapshot {\n feature: 'todo';\n updatedAt: number;\n counter: number;\n tasks: TodoTaskSnapshot[];\n summary: TodoPlanSummary;\n /** 中断目标 task ID(由 ControlledTodoFeature 扩展,null = 无中断目标) */\n interruptTargetId?: string | null;\n /** 任务未完强制继续开关状态(由 ControlledTodoFeature 扩展,null = 未上报) */\n forceContinue?: {\n enabled: boolean;\n consecutive: number;\n max: number;\n } | null;\n}\n\n/**\n * Agent 会话数据(Worker 端)\n */\nexport interface AgentSession {\n id: string;\n name: string;\n messages: Message[];\n tools: ToolMetadata[];\n createdAt: number;\n lastActive: number;\n // 项目根目录(用于定位模板文件)\n projectRoot?: string;\n // 通知系统扩展\n currentState: Notification | null;\n // call 运行状态(独立于 currentState,不受 state 覆盖影响)\n callActive?: boolean;\n runtimeState?: AgentRuntimeStateSnapshot;\n events: Notification[];\n lastEventCount: number;\n logs: DebugLogEntry[];\n // 所属 UDS 客户端连接 ID(用于多进程输入响应路由)\n clientId?: string;\n // 内部:上次最后一条消息的签名(用于推送去重)\n _lastMessageSig?: string;\n hookInspector?: HookInspectorSnapshot;\n overview?: AgentOverviewSnapshot;\n todoPlan?: TodoPlanSnapshot;\n /**\n * 唯一的活动输入租约。一个 Agent 实例在任意时刻只能由一个输入请求\n * 消费用户回复;这是跨 reconnect / 多进程路由的归属锚点。\n */\n inputLease?: InputLease;\n // 运行期间排队等待的用户输入(用于输入框常驻 + 队列注入)\n queuedInputs: QueuedInput[];\n /**\n * 外部用户回合的接受策略,注册时声明。\n * 'standard'(默认):接受聊天邮箱排队与输入租约。\n * 'none':拒绝排队注入;测试沙盒等由宿主进程驱动输入的运行时使用。\n * 输入租约(interactive input request)不受此策略限制,仍由 feature 控制。\n */\n inputPolicy?: 'standard' | 'none';\n}\n\nexport interface InputLease {\n requestId: string;\n prompt: string;\n placeholder?: string;\n initialValue?: string;\n actions?: UserInputAction[];\n mode?: UserInputRequestMode;\n questions?: UserInputQuestion[];\n timestamp: number;\n}\n\n/**\n * 排队的用户输入\n */\nexport interface QueuedInput {\n id: string;\n text: string;\n timestamp: number;\n /** 图片附件(多模态输入) */\n images?: ImageInput[];\n /** 稳定的输入来源标识,供宿主诊断和后续路由扩展使用 */\n source?: string;\n /** 来源侧事件/请求标识,不承担全局幂等语义 */\n sourceRef?: string;\n /** 随消息流动的能力激活通知(capability refs,如 skill.grill-me) */\n capabilityActivations?: string[];\n}\n\n/**\n * 一个不绑定具体 input request 的新用户回合。\n *\n * ViewerWorker 会原子决定:若存在兼容的文本 input request,则直接响应;\n * 否则进入该已连接 runtime 的会话邮箱,等待下一次兼容的文本输入租约。\n * 这覆盖新建/恢复会话在输入循环尚未打开的启动窗口,不依赖 callActive。\n */\nexport interface UserTurnInput {\n text: string;\n images?: ImageInput[];\n source?: string;\n sourceRef?: string;\n /** 随消息流动的能力激活通知(capability refs);经 lease 响应 payload 与排队项原样随行 */\n capabilityActivations?: string[];\n}\n\nexport type UserTurnSubmissionResult =\n | {\n success: true;\n delivery: 'input';\n requestId: string;\n source?: string;\n sourceRef?: string;\n }\n | {\n success: true;\n delivery: 'queued';\n id: string;\n queueLength: number;\n source?: string;\n sourceRef?: string;\n }\n | {\n success: false;\n code: 'agent_not_found' | 'invalid_input' | 'input_mode_conflict' | 'runtime_not_accepting_input';\n error: string;\n pendingMode?: UserInputRequestMode;\n };\n\n/**\n * DebugHub IPC 消息类型(主进程 → Worker)\n * 使用 discriminated union 确保类型安全\n */\nexport type DebugHubIPCMessage =\n | RegisterAgentMsg\n | UpdateAgentInspectorMsg\n | UpdateAgentOverviewMsg\n | UpdateTodoPlanMsg\n | PushMessagesMsg\n | RegisterToolsMsg\n | UnregisterAgentMsg\n | PushNotificationMsg\n | RequestInputMsg\n | InputRequestCancelledMsg\n | InterruptAgentMsg\n | StopMsg;\n\n/**\n * 注册新 Agent\n */\nexport interface RegisterAgentMsg {\n type: 'register-agent';\n agentId: string;\n name: string;\n createdAt: number;\n projectRoot?: string; // 项目根目录\n /**\n * 模板装载点:Feature 所属包的真实目录根(junction 已解析)。\n * Agent 侧是唯一知道权威 mount root 的一层(来自 feature.getPackageInfo()),\n * 注册只传事实,不做 URL 推导;URL 由 viewer-worker 分配。\n */\n templateMounts?: string[];\n /**\n * 模板名 → 装载条目。mount 为 templateMounts 数组下标,\n * rel 为该 mount root 下的相对路径(POSIX 分隔符)。\n */\n templateEntries?: Record<string, { mount: number; rel: string }>;\n hookInspector?: HookInspectorSnapshot;\n overview?: AgentOverviewSnapshot;\n activeInputRequest?: ActiveInputRequest; // 活跃的输入请求(用于重连后恢复)\n inputPolicy?: 'standard' | 'none'; // 外部用户回合接受策略('none' = 拒绝排队注入)\n}\n\nexport interface UpdateAgentInspectorMsg {\n type: 'update-agent-inspector';\n agentId: string;\n hookInspector: HookInspectorSnapshot;\n}\n\nexport interface UpdateAgentOverviewMsg {\n type: 'update-agent-overview';\n agentId: string;\n overview: AgentOverviewSnapshot;\n}\n\nexport interface UpdateTodoPlanMsg {\n type: 'update-todo-plan';\n agentId: string;\n plan: TodoPlanSnapshot;\n}\n\n/**\n * 推送 Agent 消息\n */\nexport interface PushMessagesMsg {\n type: 'push-messages';\n agentId: string;\n messages: Message[];\n}\n\n/**\n * 注册 Agent 工具\n */\nexport interface RegisterToolsMsg {\n type: 'register-tools';\n agentId: string;\n tools: Tool[];\n}\n\n/**\n * 活跃的输入请求(用于重连后恢复)\n */\nexport interface ActiveInputRequest {\n requestId: string;\n prompt: string;\n placeholder?: string;\n initialValue?: string;\n actions?: UserInputAction[];\n timestamp: number;\n}\n\n/**\n * 注销 Agent\n */\nexport interface UnregisterAgentMsg {\n type: 'unregister-agent';\n agentId: string;\n}\n\n/**\n * 停止 Worker\n */\nexport interface StopMsg {\n type: 'stop';\n}\n\n/**\n * 推送通知\n */\nexport interface PushNotificationMsg {\n type: 'push-notification';\n agentId: string;\n notification: Notification;\n}\n\n/**\n * 请求用户输入\n */\nexport interface RequestInputMsg {\n type: 'request-input';\n agentId: string;\n requestId: string;\n prompt: string;\n timeout?: number;\n placeholder?: string;\n initialValue?: string;\n actions?: UserInputAction[];\n mode?: UserInputRequestMode;\n questions?: UserInputQuestion[];\n}\n\n/**\n * 通知 Worker 一个输入请求已被运行时结算/取消(中断、销毁等)。\n * Worker 持有同名 inputLease 作为 HTTP 投递面;收到后应清除对应租约,\n * 否则陈旧租约会永久阻塞后续 user-turn(input_mode_conflict)。\n */\nexport interface InputRequestCancelledMsg {\n type: 'input-request-cancelled';\n agentId: string;\n requestId: string;\n}\n\n/**\n * 中断正在运行的 Agent\n */\nexport interface InterruptAgentMsg {\n type: 'interrupt-agent';\n agentId: string;\n clearQueue?: boolean;\n}\n\n/**\n * 用户输入响应(Worker → Agent,通过 UDS)\n */\nexport interface InputResponseMsg {\n type: 'input-response';\n agentId: string;\n requestId: string;\n input: string;\n response?: UserInputResponse;\n}\n\nexport interface UserInputAction {\n id: string;\n label: string;\n kind?: 'rollback' | 'custom';\n variant?: 'primary' | 'secondary' | 'danger';\n payload?: Record<string, unknown>;\n}\n\nexport type UserInputRequestMode = 'text' | 'choices';\n\nexport interface UserInputOption {\n id: string;\n label: string;\n description?: string;\n /** Whether this option allows supplementary free-text input */\n allowSupplement?: boolean;\n /** Whether the supplement text is required (only meaningful when allowSupplement is true) */\n supplementRequired?: boolean;\n /** Label shown above the supplement textarea */\n supplementLabel?: string;\n /** Placeholder for the supplement textarea */\n supplementPlaceholder?: string;\n}\n\nexport interface UserInputQuestion {\n id: string;\n question: string;\n options: UserInputOption[];\n allowCustom?: boolean;\n customLabel?: string;\n customPlaceholder?: string;\n}\n\nexport interface UserInputRequest {\n prompt: string;\n placeholder?: string;\n initialValue?: string;\n actions?: UserInputAction[];\n mode?: UserInputRequestMode;\n questions?: UserInputQuestion[];\n}\n\nexport interface UserInputChoiceAnswer {\n questionId: string;\n optionId?: string;\n customText?: string;\n /** Supplementary free-text provided alongside the selected option */\n supplementText?: string;\n}\n\nexport interface UserInputResponse {\n kind: 'text' | 'action' | 'choices';\n text?: string;\n actionId?: string;\n choices?: UserInputChoiceAnswer[];\n payload?: Record<string, unknown>;\n}\n\n/**\n * Worker → 主进程 消息\n */\nexport type WorkerIPCMessage =\n | ReadyMsg\n | InputResponseMsg;\n\n/**\n * Worker 就绪\n */\nexport interface ReadyMsg {\n type: 'ready';\n}\n\n// ========== 上下文管理类型 ==========\n/**\n * 消息标签枚举\n *\n * 用于快速分类和过滤消息,一条消息可能有多个标签\n */\nexport type MessageTag =\n | 'user' // 用户输入消息\n | 'system' // 系统消息\n | 'assistant' // LLM 响应消息\n | 'tool-call' // assistant 消息且包含 toolCalls\n | 'tool-result' // role === 'tool' 的工具执行结果\n | 'sub-agent' // 来自子代理的消息(与 assistant/tool-result 组合使用)\n | 'reminder'; // Feature 注入的提醒消息(与 system 组合使用)\n\n/**\n * 解析结果结构\n *\n * 从消息 content 中提取的结构化信息\n */\nexport interface ParsedContent {\n /** 从 content 提取的任务 ID(正则匹配 \"taskId\":\"xxx\") */\n taskIds: string[];\n /** 从 content 提取的工具调用名称(从 toolCalls 或 content 解析) */\n toolCalls: string[];\n /** @ 提及的内容 */\n mentions: string[];\n /** 用户可继承扩展更多字段 */\n [key: string]: any;\n}\n\n/**\n * 消息元数据\n *\n * 用于 addMessage() 的元数据参数\n */\nexport interface MessageMeta {\n /** ReAct 循环轮次 */\n turn: number;\n /** 子代理 ID(子代理消息时填写) */\n agentId?: string;\n /** 来源 Feature(reminder 等消息时填写) */\n source?: string;\n /** 消息语义标签(透传到 EnrichedMessage) */\n tag?: string;\n}\n\n/**\n * 扩展的消息结构\n *\n * 在原始 Message 基础上添加元数据\n * 不破坏现有 Message 类型,保证 LLM 调用兼容性\n */\nexport interface EnrichedMessage extends Message {\n // === 元数据字段 ===\n\n /** 唯一标识(用于索引关联) */\n id: string;\n /** 消息产生时间戳(毫秒) */\n timestamp: number;\n /** 所属 ReAct 循环轮次(从 0 开始) */\n turn: number;\n /** 全局消息序号(从 0 开始递增) */\n sequence: number;\n /** 来源 Agent ID(子代理消息) */\n agentId?: string;\n /** 来源 Feature(如 'todo-feature',仅 reminder 等) */\n source?: string;\n\n // === 分类标签 ===\n\n /** 消息分类标签(用于快速查询) */\n tags: MessageTag[];\n\n // === 解析结果 ===\n\n /** 从 content 中提取的结构化信息 */\n parsed: ParsedContent;\n}\n\n// ========== 生命周期类型 re-export ==========\n// 生命周期类型从 lifecycle.ts 导出,保持类型定义集中管理\nexport type {\n AgentInitiateContext,\n AgentDestroyContext,\n CallStartContext,\n CallFinishContext,\n CallFinishReason,\n StepStartContext,\n StepFinishedContext,\n HookResult,\n ToolContext,\n ToolResult,\n} from './lifecycle.js';\n\n// ========== 决策上下文类型 ==========\n/**\n * 决策上下文(反向钩子参数)\n *\n * 所有决策上下文的联合类型\n */\nexport type DecisionContext =\n | import('./lifecycle.js').AgentInitiateContext\n | import('./lifecycle.js').AgentDestroyContext\n | import('./lifecycle.js').CallStartContext\n | import('./lifecycle.js').CallFinishContext\n | import('./lifecycle.js').StepStartContext\n | import('./lifecycle.js').StepFinishedContext\n | import('./lifecycle.js').ToolContext\n | import('./lifecycle.js').ToolResult\n | import('./lifecycle.js').StepFinishDecisionContext\n | import('./lifecycle.js').ToolFinishedDecisionContext\n | import('./lifecycle.js').ToolResultTransformContext;\n\n// ========== UDS 通信类型 ==========\n\n/**\n * UDS 配置\n */\nexport interface UDSConfig {\n /** UDS 路径(默认自动检测平台) */\n path?: string;\n /** HTTP 端口(Web 界面) */\n httpPort?: number;\n /** 是否自动打开浏览器 */\n openBrowser?: boolean;\n}\n\n/**\n * 平台检测后的 UDS 路径\n */\nexport function getDefaultUDSPath(): string {\n if (process.platform === 'win32') {\n return '\\\\\\\\.\\\\pipe\\\\agentdev-viewer';\n }\n return '/tmp/agentdev-viewer.sock';\n}\n","export type DebugTransportMode = 'viewer-worker' | 'claw';\n\nexport function resolveDebugTransportMode(): DebugTransportMode {\n return process.env.AGENTDEV_DEBUG_TRANSPORT === 'claw' ? 'claw' : 'viewer-worker';\n}\n\nexport function getClawRuntimeUrl(): string {\n return process.env.AGENTDEV_CLAW_RUNTIME_URL || 'http://127.0.0.1:3030';\n}\n","import type {\r\n AgentOverviewSnapshot,\r\n HookInspectorSnapshot,\r\n Message,\r\n Notification,\r\n TodoPlanSnapshot,\r\n Tool,\r\n UserInputRequest,\r\n UserInputResponse,\r\n} from './types.js';\r\nimport { getClawRuntimeUrl } from './debug-transport.js';\r\n\r\ntype ClawEventKind =\r\n | 'message'\r\n | 'notification'\r\n | 'snapshot'\r\n | 'lifecycle'\r\n | 'tools'\r\n | 'todo-plan'\r\n | 'input';\r\n\r\ninterface ClawSessionRegistration {\r\n sessionId: string;\r\n runtime: 'agentdev';\r\n agentName: string;\r\n projectRoot: string | null;\r\n metadata: Record<string, unknown>;\r\n state?: Record<string, unknown>;\r\n}\r\n\r\ninterface ClawEventInput {\r\n sessionId: string;\r\n kind: ClawEventKind;\r\n payload: Record<string, unknown>;\r\n}\r\n\r\nexport interface RegisterClawAgentInput {\r\n agentId: string;\r\n name: string;\r\n projectRoot?: string;\r\n templateMounts?: string[];\r\n templateEntries?: Record<string, { mount: number; rel: string }>;\r\n hookInspector?: HookInspectorSnapshot;\r\n overview?: AgentOverviewSnapshot;\r\n inputPolicy?: 'standard' | 'none';\r\n}\r\n\r\nexport class ClawDebugClient {\r\n private readonly runtimeUrl: string;\r\n private readonly processId: string;\r\n private readonly projectRoot: string;\r\n private readonly sessionByAgentId = new Map<string, string>();\r\n private readonly pendingSessionByAgentId = new Map<string, Promise<string>>();\r\n\r\n constructor(options: { runtimeUrl?: string; processId?: string; projectRoot?: string } = {}) {\r\n this.runtimeUrl = (options.runtimeUrl ?? getClawRuntimeUrl()).replace(/\\/$/, '');\r\n this.processId = options.processId ?? String(process.pid);\r\n this.projectRoot = options.projectRoot ?? process.cwd();\r\n }\r\n\r\n async ping(): Promise<void> {\r\n await this.requestJson('/health');\r\n }\r\n\r\n async registerAgent(input: RegisterClawAgentInput): Promise<string> {\r\n const sessionId = `${this.processId}:${input.agentId}`;\r\n const existing = this.pendingSessionByAgentId.get(input.agentId);\r\n if (existing) {\r\n return existing;\r\n }\r\n\r\n const pending = (async () => {\r\n const registration: ClawSessionRegistration = {\r\n sessionId,\r\n runtime: 'agentdev',\r\n agentName: input.name,\r\n projectRoot: input.projectRoot ?? this.projectRoot,\r\n metadata: {\r\n adapter: 'agentdev-debug-hub',\r\n agentId: input.agentId,\r\n templateMounts: input.templateMounts ?? [],\r\n templateEntries: input.templateEntries ?? {},\r\n },\r\n state: {\r\n hookInspector: input.hookInspector ?? null,\r\n overview: input.overview ?? null,\r\n },\r\n };\r\n\r\n await this.requestJson('/api/sessions/register', {\r\n method: 'POST',\r\n body: JSON.stringify(registration),\r\n });\r\n\r\n this.sessionByAgentId.set(input.agentId, sessionId);\r\n\r\n await this.pushLifecycle(input.agentId, {\r\n phase: 'agent-registered',\r\n agentId: input.agentId,\r\n name: input.name,\r\n });\r\n\r\n return sessionId;\r\n })();\r\n\r\n this.pendingSessionByAgentId.set(input.agentId, pending);\r\n\r\n try {\r\n return await pending;\r\n } finally {\r\n this.pendingSessionByAgentId.delete(input.agentId);\r\n }\r\n }\r\n\r\n async unregisterAgent(agentId: string): Promise<void> {\r\n await this.pushLifecycle(agentId, {\r\n phase: 'agent-unregistered',\r\n agentId,\r\n });\r\n }\r\n\r\n async pushMessages(agentId: string, messages: Message[]): Promise<void> {\r\n await this.pushEvent(agentId, 'message', { messages });\r\n }\r\n\r\n async registerTools(agentId: string, tools: Tool[]): Promise<void> {\r\n await this.pushEvent(agentId, 'tools', { tools });\r\n }\r\n\r\n async updateInspector(agentId: string, hookInspector: HookInspectorSnapshot): Promise<void> {\r\n await this.pushEvent(agentId, 'snapshot', {\r\n scope: 'hookInspector',\r\n hookInspector,\r\n });\r\n }\r\n\r\n async updateOverview(agentId: string, overview: AgentOverviewSnapshot): Promise<void> {\r\n await this.pushEvent(agentId, 'snapshot', {\r\n scope: 'overview',\r\n overview,\r\n });\r\n }\r\n\r\n async updateTodoPlan(agentId: string, plan: TodoPlanSnapshot): Promise<void> {\r\n await this.pushEvent(agentId, 'todo-plan', {\r\n scope: 'todoPlan',\r\n plan,\r\n });\r\n }\r\n\r\n async pushNotification(agentId: string, notification: Notification): Promise<void> {\r\n await this.pushEvent(agentId, 'notification', { notification });\r\n }\r\n\r\n async requestUserInput(\r\n _agentId: string,\r\n request: UserInputRequest,\r\n timeout: number,\r\n signal?: AbortSignal,\r\n ): Promise<UserInputResponse> {\r\n const agentId = _agentId;\r\n const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\r\n await this.pushEvent(agentId, 'input', {\r\n requestId,\r\n request,\r\n timeout,\r\n status: 'pending',\r\n });\r\n\r\n const sessionId = await this.requireSessionId(agentId);\r\n const startedAt = Date.now();\r\n const pollIntervalMs = 400;\r\n\r\n while (true) {\r\n if (signal?.aborted) {\r\n throw this.createAbortError(`User input request for Agent '${agentId}' was cancelled`);\r\n }\r\n if (timeout !== Infinity && Date.now() - startedAt > timeout) {\r\n throw new Error(`User input timeout after ${timeout}ms`);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(\r\n `${this.runtimeUrl}/api/agents/${encodeURIComponent(sessionId)}/input-response?requestId=${encodeURIComponent(requestId)}`,\r\n { signal },\r\n );\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl} while waiting for user input: ${message}`);\r\n }\r\n\r\n if (response.ok) {\r\n const body = await response.json();\r\n return body.response;\r\n }\r\n\r\n if (response.status !== 404) {\r\n const text = await response.text();\r\n throw new Error(`Input bridge request failed: ${response.status} ${response.statusText} ${text}`);\r\n }\r\n\r\n await this.delay(pollIntervalMs, signal);\r\n }\r\n }\r\n\r\n private delay(ms: number, signal?: AbortSignal): Promise<void> {\r\n if (signal?.aborted) {\r\n return Promise.reject(this.createAbortError('Operation was cancelled'));\r\n }\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n signal?.removeEventListener('abort', onAbort);\r\n resolve();\r\n }, ms);\r\n const onAbort = () => {\r\n clearTimeout(timer);\r\n reject(this.createAbortError('Operation was cancelled'));\r\n };\r\n signal?.addEventListener('abort', onAbort, { once: true });\r\n });\r\n }\r\n\r\n private createAbortError(message: string): Error {\r\n const error = new Error(message);\r\n error.name = 'AbortError';\r\n return error;\r\n }\r\n\r\n private async pushLifecycle(agentId: string, payload: Record<string, unknown>): Promise<void> {\r\n await this.pushEvent(agentId, 'lifecycle', payload);\r\n }\r\n\r\n private async pushEvent(agentId: string, kind: ClawEventKind, payload: Record<string, unknown>): Promise<void> {\r\n const sessionId = await this.requireSessionId(agentId);\r\n const event: ClawEventInput = {\r\n sessionId,\r\n kind,\r\n payload,\r\n };\r\n\r\n await this.requestJson('/api/events', {\r\n method: 'POST',\r\n body: JSON.stringify(event),\r\n });\r\n }\r\n\r\n private async requireSessionId(agentId: string): Promise<string> {\r\n const sessionId = this.sessionByAgentId.get(agentId);\r\n if (!sessionId) {\r\n const pending = this.pendingSessionByAgentId.get(agentId);\r\n if (pending) {\r\n return pending;\r\n }\r\n throw new Error(`No Claw session registered for agentId '${agentId}'`);\r\n }\r\n return sessionId;\r\n }\r\n\r\n private async requestJson(path: string, init?: RequestInit): Promise<any> {\r\n let response: Response;\r\n try {\r\n response = await fetch(`${this.runtimeUrl}${path}`, {\r\n ...init,\r\n headers: {\r\n 'content-type': 'application/json',\r\n ...(init?.headers ?? {}),\r\n },\r\n });\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl}: ${message}`);\r\n }\r\n\r\n if (!response.ok) {\r\n const body = await response.text();\r\n throw new Error(`Claw runtime request failed: ${response.status} ${response.statusText} ${body}`);\r\n }\r\n\r\n if (response.status === 204) {\r\n return null;\r\n }\r\n\r\n return response.json();\r\n }\r\n}\r\n","import { getClawRuntimeUrl, resolveDebugTransportMode, type DebugTransportMode } from './debug-transport.js';\n\nexport interface DebugCapabilities {\n transportMode: DebugTransportMode;\n interactiveInput: boolean;\n runtimeUrl: string | null;\n viewerCompatibleApi: boolean;\n debuggerMcpMetadata: boolean;\n}\n\nexport function getDebugCapabilities(): DebugCapabilities {\n const transportMode = resolveDebugTransportMode();\n const isClaw = transportMode === 'claw';\n\n return {\n transportMode,\n interactiveInput: true,\n runtimeUrl: isClaw ? getClawRuntimeUrl() : null,\n viewerCompatibleApi: isClaw,\n debuggerMcpMetadata: isClaw,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// src/template/resolver.ts
|
|
2
|
+
var PlaceholderResolver = class _PlaceholderResolver {
|
|
3
|
+
// 正则表达式模式
|
|
4
|
+
static PATTERNS = {
|
|
5
|
+
// {{#each}}...{{/each}} 循环渲染
|
|
6
|
+
each: /\{\{#each\}\}(.*?)\{\{\/each\}\}/gs,
|
|
7
|
+
// {{#if}}...{{/if}} 条件渲染
|
|
8
|
+
conditional: /\{\{#if\}\}(.*?)\{\{\/if\}\}/gs,
|
|
9
|
+
// {{variable}} 或 {{obj.key}} 或 {{var|default}}
|
|
10
|
+
variable: /\{\{([^}]+)\}\}/g
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 解析模板中的占位符
|
|
14
|
+
* @param template 模板内容
|
|
15
|
+
* @param context 变量上下文
|
|
16
|
+
* @returns 解析后的内容
|
|
17
|
+
*/
|
|
18
|
+
static resolve(template, context = {}) {
|
|
19
|
+
let result = _PlaceholderResolver.processEach(template, context);
|
|
20
|
+
result = _PlaceholderResolver.processConditionals(result, context);
|
|
21
|
+
result = result.replace(_PlaceholderResolver.PATTERNS.variable, (_, expr) => {
|
|
22
|
+
const trimmed = expr.trim();
|
|
23
|
+
const pipeIndex = trimmed.indexOf("|");
|
|
24
|
+
let path;
|
|
25
|
+
let defaultValue;
|
|
26
|
+
if (pipeIndex !== -1) {
|
|
27
|
+
path = trimmed.slice(0, pipeIndex).trim();
|
|
28
|
+
defaultValue = trimmed.slice(pipeIndex + 1).trim();
|
|
29
|
+
} else {
|
|
30
|
+
path = trimmed;
|
|
31
|
+
}
|
|
32
|
+
const value = _PlaceholderResolver.getNestedValue(context, path);
|
|
33
|
+
if (value !== void 0 && value !== null) {
|
|
34
|
+
return String(value);
|
|
35
|
+
}
|
|
36
|
+
return defaultValue ?? "";
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 处理循环渲染 {{#each}}...{{/each}}
|
|
42
|
+
* 格式:{{#each}}arrayName
|
|
43
|
+
* ...template...
|
|
44
|
+
* {{/each}}
|
|
45
|
+
* 在循环内可使用数组元素的字段(如 {{name}}, {{description}})或 {{this}} 引用整个对象
|
|
46
|
+
*/
|
|
47
|
+
static processEach(template, context) {
|
|
48
|
+
return template.replace(
|
|
49
|
+
_PlaceholderResolver.PATTERNS.each,
|
|
50
|
+
(match, body) => {
|
|
51
|
+
const lines = body.split("\n");
|
|
52
|
+
const arrayName = lines[0].trim();
|
|
53
|
+
const loopTemplate = lines.slice(1).join("\n");
|
|
54
|
+
const array = _PlaceholderResolver.getNestedValue(context, arrayName);
|
|
55
|
+
if (!Array.isArray(array)) {
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
return array.map((item) => {
|
|
59
|
+
const itemContext = {
|
|
60
|
+
...context,
|
|
61
|
+
...item,
|
|
62
|
+
this: item
|
|
63
|
+
};
|
|
64
|
+
return _PlaceholderResolver.resolve(loopTemplate, itemContext);
|
|
65
|
+
}).join("");
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 处理条件渲染 {{#if}}...{{/if}}
|
|
71
|
+
* 条件格式:{{#if}}varName{{/if}} - 只有当 varName 为真时才显示内容
|
|
72
|
+
*/
|
|
73
|
+
static processConditionals(template, context) {
|
|
74
|
+
return template.replace(
|
|
75
|
+
_PlaceholderResolver.PATTERNS.conditional,
|
|
76
|
+
(_, content) => {
|
|
77
|
+
const trimmed = content.trim();
|
|
78
|
+
const value = _PlaceholderResolver.getNestedValue(context, trimmed);
|
|
79
|
+
if (value === true || value === 1 || value === "true") {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
if (value) {
|
|
83
|
+
return content;
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 获取嵌套对象的值
|
|
91
|
+
* 支持点号分隔的路径,如 "user.name"
|
|
92
|
+
*/
|
|
93
|
+
static getNestedValue(obj, path) {
|
|
94
|
+
const keys = path.split(".");
|
|
95
|
+
let current = obj;
|
|
96
|
+
for (const key of keys) {
|
|
97
|
+
if (current === null || current === void 0) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
current = current[key];
|
|
101
|
+
}
|
|
102
|
+
return current;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 获取模板中使用的变量列表
|
|
106
|
+
*/
|
|
107
|
+
static extractVariables(template) {
|
|
108
|
+
const variables = /* @__PURE__ */ new Set();
|
|
109
|
+
let match;
|
|
110
|
+
const regex = /\{\{([^}|]+?)(?:\|[^}]*)?\}\}/g;
|
|
111
|
+
while ((match = regex.exec(template)) !== null) {
|
|
112
|
+
variables.add(match[1].trim());
|
|
113
|
+
}
|
|
114
|
+
return Array.from(variables);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 验证上下文是否包含所需变量
|
|
118
|
+
* @returns 缺失的变量数组
|
|
119
|
+
*/
|
|
120
|
+
static validate(template, context) {
|
|
121
|
+
const required = _PlaceholderResolver.extractVariables(template);
|
|
122
|
+
const missing = [];
|
|
123
|
+
for (const path of required) {
|
|
124
|
+
const value = _PlaceholderResolver.getNestedValue(context, path);
|
|
125
|
+
if (value === void 0 || value === null || value === "") {
|
|
126
|
+
missing.push(path);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return missing;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
PlaceholderResolver
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=chunk-BVF7RUXV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/template/resolver.ts"],"sourcesContent":["/**\n * 占位符解析器\n * 使用正则表达式实现占位符替换\n */\n\nimport type { PlaceholderContext } from './types.js';\n\n/**\n * 占位符解析器\n */\nexport class PlaceholderResolver {\n // 正则表达式模式\n private static PATTERNS = {\n // {{#each}}...{{/each}} 循环渲染\n each: /\\{\\{#each\\}\\}(.*?)\\{\\{\\/each\\}\\}/gs,\n // {{#if}}...{{/if}} 条件渲染\n conditional: /\\{\\{#if\\}\\}(.*?)\\{\\{\\/if\\}\\}/gs,\n // {{variable}} 或 {{obj.key}} 或 {{var|default}}\n variable: /\\{\\{([^}]+)\\}\\}/g,\n };\n\n /**\n * 解析模板中的占位符\n * @param template 模板内容\n * @param context 变量上下文\n * @returns 解析后的内容\n */\n static resolve(template: string, context: PlaceholderContext = {}): string {\n // 1. 处理循环渲染 {{#each}}...{{/each}}\n let result = PlaceholderResolver.processEach(template, context);\n\n // 2. 处理条件渲染 {{#if}}...{{/if}}\n result = PlaceholderResolver.processConditionals(result, context);\n\n // 2. 处理变量替换(支持嵌套和默认值)\n result = result.replace(PlaceholderResolver.PATTERNS.variable, (_, expr) => {\n const trimmed = expr.trim();\n\n // 分离默认值: var|default\n const pipeIndex = trimmed.indexOf('|');\n let path: string;\n let defaultValue: string | undefined;\n\n if (pipeIndex !== -1) {\n path = trimmed.slice(0, pipeIndex).trim();\n defaultValue = trimmed.slice(pipeIndex + 1).trim();\n } else {\n path = trimmed;\n }\n\n // 获取值(支持嵌套访问)\n const value = PlaceholderResolver.getNestedValue(context, path);\n\n // 返回值或默认值\n if (value !== undefined && value !== null) {\n return String(value);\n }\n return defaultValue ?? '';\n });\n\n return result;\n }\n\n /**\n * 处理循环渲染 {{#each}}...{{/each}}\n * 格式:{{#each}}arrayName\n * ...template...\n * {{/each}}\n * 在循环内可使用数组元素的字段(如 {{name}}, {{description}})或 {{this}} 引用整个对象\n */\n private static processEach(\n template: string,\n context: PlaceholderContext\n ): string {\n return template.replace(\n PlaceholderResolver.PATTERNS.each,\n (match, body: string) => {\n // 解析内容:第一行是数组变量名,其余是循环模板\n const lines = body.split('\\n');\n const arrayName = lines[0].trim();\n const loopTemplate = lines.slice(1).join('\\n');\n\n // 获取数组数据\n const array = PlaceholderResolver.getNestedValue(context, arrayName);\n\n // 不是数组则返回空\n if (!Array.isArray(array)) {\n return '';\n }\n\n // 渲染每个元素\n return array.map((item) => {\n // 将 item 的字段作为上下文,也提供 this 引用整个对象\n const itemContext: PlaceholderContext = {\n ...context,\n ...item,\n this: item,\n };\n return PlaceholderResolver.resolve(loopTemplate, itemContext);\n }).join('');\n });\n }\n\n /**\n * 处理条件渲染 {{#if}}...{{/if}}\n * 条件格式:{{#if}}varName{{/if}} - 只有当 varName 为真时才显示内容\n */\n private static processConditionals(\n template: string,\n context: PlaceholderContext\n ): string {\n return template.replace(\n PlaceholderResolver.PATTERNS.conditional,\n (_, content: string) => {\n // 检查条件变量是否存在且为真\n const trimmed = content.trim();\n const value = PlaceholderResolver.getNestedValue(context, trimmed);\n\n // 布尔值判断\n if (value === true || value === 1 || value === 'true') {\n return ''; // 条件为真,保留内容(但这里逻辑反了,应该返回内容)\n }\n if (value) {\n return content;\n }\n return ''; // 条件为假,移除内容\n });\n }\n\n /**\n * 获取嵌套对象的值\n * 支持点号分隔的路径,如 \"user.name\"\n */\n private static getNestedValue(obj: any, path: string): any {\n const keys = path.split('.');\n let current = obj;\n\n for (const key of keys) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = current[key];\n }\n\n return current;\n }\n\n /**\n * 获取模板中使用的变量列表\n */\n static extractVariables(template: string): string[] {\n const variables = new Set<string>();\n let match: RegExpExecArray | null;\n\n // 提取所有变量(不包括条件渲染)\n const regex = /\\{\\{([^}|]+?)(?:\\|[^}]*)?\\}\\}/g;\n while ((match = regex.exec(template)) !== null) {\n variables.add(match[1].trim());\n }\n\n return Array.from(variables);\n }\n\n /**\n * 验证上下文是否包含所需变量\n * @returns 缺失的变量数组\n */\n static validate(\n template: string,\n context: PlaceholderContext\n ): string[] {\n const required = PlaceholderResolver.extractVariables(template);\n const missing: string[] = [];\n\n for (const path of required) {\n const value = PlaceholderResolver.getNestedValue(context, path);\n if (value === undefined || value === null || value === '') {\n missing.push(path);\n }\n }\n\n return missing;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/core/session-events.ts
|
|
2
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
3
|
+
var nextItemSeq = 0;
|
|
4
|
+
function nextItemId() {
|
|
5
|
+
return `item_${nextItemSeq++}`;
|
|
6
|
+
}
|
|
7
|
+
function subscribeSessionEvents(listener) {
|
|
8
|
+
listeners.add(listener);
|
|
9
|
+
return () => {
|
|
10
|
+
listeners.delete(listener);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function emitSessionEvent(event) {
|
|
14
|
+
if (listeners.size === 0) return;
|
|
15
|
+
for (const listener of listeners) {
|
|
16
|
+
try {
|
|
17
|
+
listener(event);
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function emitAssistantResponseEvents(response, turn) {
|
|
23
|
+
if (listeners.size === 0) return;
|
|
24
|
+
if (response.reasoning && response.reasoning.trim()) {
|
|
25
|
+
emitSessionEvent({
|
|
26
|
+
type: "item.completed",
|
|
27
|
+
item: { id: nextItemId(), turn, type: "reasoning", text: response.reasoning }
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (response.content && response.content.trim()) {
|
|
31
|
+
emitSessionEvent({
|
|
32
|
+
type: "item.completed",
|
|
33
|
+
item: { id: nextItemId(), turn, type: "agent_message", text: response.content }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (response.toolCalls) {
|
|
37
|
+
for (const call of response.toolCalls) {
|
|
38
|
+
emitSessionEvent({
|
|
39
|
+
type: "item.started",
|
|
40
|
+
item: {
|
|
41
|
+
id: call.id ?? nextItemId(),
|
|
42
|
+
turn,
|
|
43
|
+
type: "tool_call",
|
|
44
|
+
tool: call.name,
|
|
45
|
+
arguments: call.arguments,
|
|
46
|
+
status: "in_progress"
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function emitToolResultEvents(call, result, turn) {
|
|
53
|
+
if (listeners.size === 0) return;
|
|
54
|
+
emitSessionEvent({
|
|
55
|
+
type: "item.completed",
|
|
56
|
+
item: {
|
|
57
|
+
id: call.id ?? nextItemId(),
|
|
58
|
+
turn,
|
|
59
|
+
type: "tool_call",
|
|
60
|
+
tool: call.name,
|
|
61
|
+
arguments: call.arguments,
|
|
62
|
+
status: result.success ? "completed" : "failed",
|
|
63
|
+
...result.success ? { result: result.result } : { error: result.error ?? "tool execution failed" },
|
|
64
|
+
...result.interrupted ? { interrupted: result.interrupted } : {}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function emitTurnCompleted(turn, usage) {
|
|
69
|
+
emitSessionEvent({ type: "turn.completed", turn, ...usage ? { usage } : {} });
|
|
70
|
+
}
|
|
71
|
+
function emitTurnFailed(turn, source) {
|
|
72
|
+
if (typeof source === "string") {
|
|
73
|
+
emitSessionEvent({ type: "turn.failed", turn, error: { message: source } });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
emitSessionEvent({
|
|
77
|
+
type: "turn.failed",
|
|
78
|
+
turn,
|
|
79
|
+
error: {
|
|
80
|
+
message: source.error?.message ?? source.response ?? source.reason,
|
|
81
|
+
reason: source.reason,
|
|
82
|
+
...source.error ? {
|
|
83
|
+
category: source.error.category,
|
|
84
|
+
...typeof source.error.statusCode === "number" ? { statusCode: source.error.statusCode } : {},
|
|
85
|
+
...typeof source.error.retryable === "boolean" ? { retryable: source.error.retryable } : {}
|
|
86
|
+
} : {}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
subscribeSessionEvents,
|
|
93
|
+
emitSessionEvent,
|
|
94
|
+
emitAssistantResponseEvents,
|
|
95
|
+
emitToolResultEvents,
|
|
96
|
+
emitTurnCompleted,
|
|
97
|
+
emitTurnFailed
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-KO4DJKM6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/session-events.ts"],"sourcesContent":["/**\r\n * Session Events - 会话事件流\r\n *\r\n * 职责:\r\n * - 定义无头模式(headless)消费的会话事件模型(codex exec 风格)\r\n * - 提供进程内订阅 API,消息变更单点发射\r\n *\r\n * 与通知系统(notification.ts)的分工:\r\n * - notification 是 UI 状态信号:节流、轻量、面向 Web UI 进度指示\r\n * - session event 是审计数据:不节流、完整、面向无头模式 stdout JSONL\r\n *\r\n * 事件模型(对齐 codex exec JSONL):\r\n * - thread.started 线程(会话)开始\r\n * - turn.started 一次用户输入触发的回合开始\r\n * - item.started / item.completed 会话条目生命周期\r\n * - turn.completed 回合完成(含 token 用量)\r\n * - turn.failed 回合失败\r\n *\r\n * 术语(ADR-0002/Q6,一词不混用):本事件流里的 `thread.started` 的 thread\r\n * 指**会话本身**(codex exec 对外 jsonl 审计契约,永不改义);框架的接续链概念\r\n * 定名 **WorkThread**(见 `core/workthread/`),一处不混用。\r\n *\r\n * item 类型:\r\n * - agent_message agent 的自然语言回复\r\n * - reasoning agent 的思考摘要\r\n * - tool_call 工具调用(参数、结果、错误、状态)\r\n */\r\n\r\nimport type { LLMResponse, ToolCall } from './types.js';\r\nimport type { ToolExecResult } from './context.js';\r\n\r\n// ========== Item 模型 ==========\r\n\r\nexport interface SessionItemBase {\r\n /** 条目唯一 ID:tool_call 用 LLM 生成的 call.id,其余用递增 item_N */\r\n id: string;\r\n /** 所属回合(callIndex) */\r\n turn: number;\r\n}\r\n\r\nexport interface AgentMessageItem extends SessionItemBase {\r\n type: 'agent_message';\r\n text: string;\r\n}\r\n\r\nexport interface ReasoningItem extends SessionItemBase {\r\n type: 'reasoning';\r\n text: string;\r\n}\r\n\r\nexport type ToolCallStatus = 'in_progress' | 'completed' | 'failed';\r\n\r\nexport interface ToolCallItem extends SessionItemBase {\r\n type: 'tool_call';\r\n /** 工具名称(如 shell / read / lsp_hover) */\r\n tool: string;\r\n /** 工具调用参数 */\r\n arguments?: unknown;\r\n status: ToolCallStatus;\r\n /** 成功时的结果内容 */\r\n result?: unknown;\r\n /** 失败时的错误信息 */\r\n error?: string;\r\n /**\r\n * 终止标注(ticket 023 / ADR-0005):工具被超时/用户打断终止但仍在 settle\r\n * 窗口内收尾时出现。success 结果可携带此字段,无头 jsonl 审计据此区分\r\n * 正常完成与被终止收尾。\r\n */\r\n interrupted?: { reason: 'timeout' | 'user' };\r\n}\r\n\r\nexport type SessionItem = AgentMessageItem | ReasoningItem | ToolCallItem;\r\n\r\n// ========== 事件模型 ==========\r\n\r\nexport interface TurnUsage {\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n}\r\n\r\n/**\r\n * 回合失败的结构化事实。\r\n *\r\n * message 只供人读;reason/category/retryable 供机器分流,\r\n * 与 CallOutcome 对齐(无头 CLI/JSONL 消费同一套语义)。\r\n */\r\nexport interface TurnFailure {\r\n message: string;\r\n /** 框架级终止原因(ExecutionReason) */\r\n reason?: string;\r\n /** 分类错误类型(APIErrorType / runtime_error / exception) */\r\n category?: string;\r\n statusCode?: number;\r\n retryable?: boolean;\r\n}\r\n\r\nexport type SessionEvent =\r\n | { type: 'thread.started'; threadId: string }\r\n | { type: 'turn.started'; turn: number }\r\n | { type: 'item.started'; item: SessionItem }\r\n | { type: 'item.completed'; item: SessionItem }\r\n | { type: 'turn.completed'; turn: number; usage?: TurnUsage }\r\n | { type: 'turn.failed'; turn: number; error: TurnFailure };\r\n\r\nexport type SessionEventListener = (event: SessionEvent) => void;\r\n\r\n// ========== 订阅管理 ==========\r\n\r\nconst listeners = new Set<SessionEventListener>();\r\n\r\nlet nextItemSeq = 0;\r\n\r\nfunction nextItemId(): string {\r\n return `item_${nextItemSeq++}`;\r\n}\r\n\r\n/**\r\n * 订阅会话事件流。\r\n * @returns 退订函数\r\n */\r\nexport function subscribeSessionEvents(listener: SessionEventListener): () => void {\r\n listeners.add(listener);\r\n return () => {\r\n listeners.delete(listener);\r\n };\r\n}\r\n\r\n/**\r\n * 发射会话事件。无订阅者时零开销。\r\n */\r\nexport function emitSessionEvent(event: SessionEvent): void {\r\n if (listeners.size === 0) return;\r\n for (const listener of listeners) {\r\n try {\r\n listener(event);\r\n } catch {\r\n // 订阅者异常不影响主流程\r\n }\r\n }\r\n}\r\n\r\n// ========== 发射辅助(context / agent 调用) ==========\r\n\r\n/**\r\n * 发射 assistant 响应产生的条目事件:\r\n * reasoning / agent_message 以 completed 出现(一次性内容),\r\n * 每个 toolCall 以 started 出现(等待执行结果)。\r\n */\r\nexport function emitAssistantResponseEvents(response: LLMResponse, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n if (response.reasoning && response.reasoning.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'reasoning', text: response.reasoning },\r\n });\r\n }\r\n\r\n if (response.content && response.content.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'agent_message', text: response.content },\r\n });\r\n }\r\n\r\n if (response.toolCalls) {\r\n for (const call of response.toolCalls) {\r\n emitSessionEvent({\r\n type: 'item.started',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: 'in_progress',\r\n },\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 发射工具结果条目事件(与 emitAssistantResponseEvents 的 started 配对,靠 call.id 关联)。\r\n */\r\nexport function emitToolResultEvents(call: ToolCall, result: ToolExecResult, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: result.success ? 'completed' : 'failed',\r\n ...(result.success\r\n ? { result: result.result }\r\n : { error: result.error ?? 'tool execution failed' }),\r\n ...(result.interrupted ? { interrupted: result.interrupted } : {}),\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 发射回合完成事件(附 token 用量)。\r\n */\r\nexport function emitTurnCompleted(turn: number, usage?: TurnUsage): void {\r\n emitSessionEvent({ type: 'turn.completed', turn, ...(usage ? { usage } : {}) });\r\n}\r\n\r\n/**\r\n * 发射回合失败事件。\r\n *\r\n * 支持两种入参:\r\n * - CallOutcome(agent.ts 主链路):展开为结构化 TurnFailure\r\n * - 简单 message(宿主致命错误等):仅带 message\r\n */\r\nexport function emitTurnFailed(turn: number, source: string | import('./lifecycle.js').CallOutcome): void {\r\n if (typeof source === 'string') {\r\n emitSessionEvent({ type: 'turn.failed', turn, error: { message: source } });\r\n return;\r\n }\r\n emitSessionEvent({\r\n type: 'turn.failed',\r\n turn,\r\n error: {\r\n message: source.error?.message ?? source.response ?? source.reason,\r\n reason: source.reason,\r\n ...(source.error ? {\r\n category: source.error.category,\r\n ...(typeof source.error.statusCode === 'number' ? { statusCode: source.error.statusCode } : {}),\r\n ...(typeof source.error.retryable === 'boolean' ? { retryable: source.error.retryable } : {}),\r\n } : {}),\r\n },\r\n });\r\n}\r\n"],"mappings":";AA6GA,IAAM,YAAY,oBAAI,IAA0B;AAEhD,IAAI,cAAc;AAElB,SAAS,aAAqB;AAC5B,SAAO,QAAQ,aAAa;AAC9B;AAMO,SAAS,uBAAuB,UAA4C;AACjF,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAKO,SAAS,iBAAiB,OAA2B;AAC1D,MAAI,UAAU,SAAS,EAAG;AAC1B,aAAW,YAAY,WAAW;AAChC,QAAI;AACF,eAAS,KAAK;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AASO,SAAS,4BAA4B,UAAuB,MAAoB;AACrF,MAAI,UAAU,SAAS,EAAG;AAE1B,MAAI,SAAS,aAAa,SAAS,UAAU,KAAK,GAAG;AACnD,qBAAiB;AAAA,MACf,MAAM;AAAA,MACN,MAAM,EAAE,IAAI,WAAW,GAAG,MAAM,MAAM,aAAa,MAAM,SAAS,UAAU;AAAA,IAC9E,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,SAAS,QAAQ,KAAK,GAAG;AAC/C,qBAAiB;AAAA,MACf,MAAM;AAAA,MACN,MAAM,EAAE,IAAI,WAAW,GAAG,MAAM,MAAM,iBAAiB,MAAM,SAAS,QAAQ;AAAA,IAChF,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW;AACtB,eAAW,QAAQ,SAAS,WAAW;AACrC,uBAAiB;AAAA,QACf,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,IAAI,KAAK,MAAM,WAAW;AAAA,UAC1B;AAAA,UACA,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,UACX,WAAW,KAAK;AAAA,UAChB,QAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKO,SAAS,qBAAqB,MAAgB,QAAwB,MAAoB;AAC/F,MAAI,UAAU,SAAS,EAAG;AAE1B,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,IAAI,KAAK,MAAM,WAAW;AAAA,MAC1B;AAAA,MACA,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,QAAQ,OAAO,UAAU,cAAc;AAAA,MACvC,GAAI,OAAO,UACP,EAAE,QAAQ,OAAO,OAAO,IACxB,EAAE,OAAO,OAAO,SAAS,wBAAwB;AAAA,MACrD,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,IAClE;AAAA,EACF,CAAC;AACH;AAKO,SAAS,kBAAkB,MAAc,OAAyB;AACvE,mBAAiB,EAAE,MAAM,kBAAkB,MAAM,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAChF;AASO,SAAS,eAAe,MAAc,QAA6D;AACxG,MAAI,OAAO,WAAW,UAAU;AAC9B,qBAAiB,EAAE,MAAM,eAAe,MAAM,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;AAC1E;AAAA,EACF;AACA,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN;AAAA,IACA,OAAO;AAAA,MACL,SAAS,OAAO,OAAO,WAAW,OAAO,YAAY,OAAO;AAAA,MAC5D,QAAQ,OAAO;AAAA,MACf,GAAI,OAAO,QAAQ;AAAA,QACjB,UAAU,OAAO,MAAM;AAAA,QACvB,GAAI,OAAO,OAAO,MAAM,eAAe,WAAW,EAAE,YAAY,OAAO,MAAM,WAAW,IAAI,CAAC;AAAA,QAC7F,GAAI,OAAO,OAAO,MAAM,cAAc,YAAY,EAAE,WAAW,OAAO,MAAM,UAAU,IAAI,CAAC;AAAA,MAC7F,IAAI,CAAC;AAAA,IACP;AAAA,EACF,CAAC;AACH;","names":[]}
|