@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.
Files changed (79) hide show
  1. package/dist/BasicAgent-RPYJHYCM.js +12 -0
  2. package/dist/BasicAgent-RPYJHYCM.js.map +1 -0
  3. package/dist/ExplorerAgent-ITTSJRJI.js +12 -0
  4. package/dist/ExplorerAgent-ITTSJRJI.js.map +1 -0
  5. package/dist/chunk-574NG4UB.js +1259 -0
  6. package/dist/chunk-574NG4UB.js.map +1 -0
  7. package/dist/chunk-BVF7RUXV.js +136 -0
  8. package/dist/chunk-BVF7RUXV.js.map +1 -0
  9. package/dist/chunk-DGUM43GV.js +11 -0
  10. package/dist/chunk-DGUM43GV.js.map +1 -0
  11. package/dist/chunk-KO4DJKM6.js +99 -0
  12. package/dist/chunk-KO4DJKM6.js.map +1 -0
  13. package/dist/chunk-TIJV6GJW.js +51 -0
  14. package/dist/chunk-TIJV6GJW.js.map +1 -0
  15. package/dist/chunk-UCEIJEM5.js +61 -0
  16. package/dist/chunk-UCEIJEM5.js.map +1 -0
  17. package/dist/chunk-YFSM6IYU.js +6572 -0
  18. package/dist/chunk-YFSM6IYU.js.map +1 -0
  19. package/dist/features/example-feature/templates/example-tool.render.d.ts +3 -0
  20. package/dist/features/example-feature/templates/example-tool.render.js +20 -0
  21. package/dist/features/example-feature/templates/example-tool.render.js.map +1 -0
  22. package/dist/features/opencode-basic/templates/edit.render.d.ts +12 -0
  23. package/dist/features/opencode-basic/templates/edit.render.js +44 -0
  24. package/dist/features/opencode-basic/templates/edit.render.js.map +1 -0
  25. package/dist/features/opencode-basic/templates/glob.render.d.ts +13 -0
  26. package/dist/features/opencode-basic/templates/glob.render.js +44 -0
  27. package/dist/features/opencode-basic/templates/glob.render.js.map +1 -0
  28. package/dist/features/opencode-basic/templates/grep.render.d.ts +13 -0
  29. package/dist/features/opencode-basic/templates/grep.render.js +67 -0
  30. package/dist/features/opencode-basic/templates/grep.render.js.map +1 -0
  31. package/dist/features/opencode-basic/templates/ls.render.d.ts +12 -0
  32. package/dist/features/opencode-basic/templates/ls.render.js +35 -0
  33. package/dist/features/opencode-basic/templates/ls.render.js.map +1 -0
  34. package/dist/features/opencode-basic/templates/read.render.d.ts +13 -0
  35. package/dist/features/opencode-basic/templates/read.render.js +83 -0
  36. package/dist/features/opencode-basic/templates/read.render.js.map +1 -0
  37. package/dist/features/opencode-basic/templates/write.render.d.ts +13 -0
  38. package/dist/features/opencode-basic/templates/write.render.js +44 -0
  39. package/dist/features/opencode-basic/templates/write.render.js.map +1 -0
  40. package/dist/features/skill/templates/skill.render.d.ts +13 -0
  41. package/dist/features/skill/templates/skill.render.js +33 -0
  42. package/dist/features/skill/templates/skill.render.js.map +1 -0
  43. package/dist/features/subagent/templates/agent-close.render.d.ts +9 -0
  44. package/dist/features/subagent/templates/agent-close.render.js +29 -0
  45. package/dist/features/subagent/templates/agent-close.render.js.map +1 -0
  46. package/dist/features/subagent/templates/agent-list.render.d.ts +9 -0
  47. package/dist/features/subagent/templates/agent-list.render.js +23 -0
  48. package/dist/features/subagent/templates/agent-list.render.js.map +1 -0
  49. package/dist/features/subagent/templates/agent-send.render.d.ts +9 -0
  50. package/dist/features/subagent/templates/agent-send.render.js +37 -0
  51. package/dist/features/subagent/templates/agent-send.render.js.map +1 -0
  52. package/dist/features/subagent/templates/agent-spawn.render.d.ts +9 -0
  53. package/dist/features/subagent/templates/agent-spawn.render.js +38 -0
  54. package/dist/features/subagent/templates/agent-spawn.render.js.map +1 -0
  55. package/dist/features/subagent/templates/wait.render.d.ts +9 -0
  56. package/dist/features/subagent/templates/wait.render.js +29 -0
  57. package/dist/features/subagent/templates/wait.render.js.map +1 -0
  58. package/dist/features/todo/templates/task-clear.render.d.ts +12 -0
  59. package/dist/features/todo/templates/task-clear.render.js +32 -0
  60. package/dist/features/todo/templates/task-clear.render.js.map +1 -0
  61. package/dist/features/todo/templates/task-create.render.d.ts +12 -0
  62. package/dist/features/todo/templates/task-create.render.js +56 -0
  63. package/dist/features/todo/templates/task-create.render.js.map +1 -0
  64. package/dist/features/todo/templates/task-list.render.d.ts +12 -0
  65. package/dist/features/todo/templates/task-list.render.js +72 -0
  66. package/dist/features/todo/templates/task-list.render.js.map +1 -0
  67. package/dist/features/todo/templates/task-update.render.d.ts +12 -0
  68. package/dist/features/todo/templates/task-update.render.js +58 -0
  69. package/dist/features/todo/templates/task-update.render.js.map +1 -0
  70. package/dist/index.d.ts +6831 -0
  71. package/dist/index.js +8376 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/notification-5DFBYKKN.js +32 -0
  74. package/dist/notification-5DFBYKKN.js.map +1 -0
  75. package/dist/resolver-F5SM2RHQ.js +8 -0
  76. package/dist/resolver-F5SM2RHQ.js.map +1 -0
  77. package/dist/session-events-TETMQI45.js +18 -0
  78. package/dist/session-events-TETMQI45.js.map +1 -0
  79. package/package.json +55 -0
@@ -0,0 +1,1259 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-DGUM43GV.js";
4
+
5
+ // src/core/notification.ts
6
+ import { AsyncLocalStorage } from "async_hooks";
7
+
8
+ // src/core/debug-hub.ts
9
+ import { connect } from "net";
10
+
11
+ // src/core/types.ts
12
+ function getDefaultUDSPath() {
13
+ if (process.platform === "win32") {
14
+ return "\\\\.\\pipe\\agentdev-viewer";
15
+ }
16
+ return "/tmp/agentdev-viewer.sock";
17
+ }
18
+
19
+ // src/core/debug-transport.ts
20
+ function resolveDebugTransportMode() {
21
+ return process.env.AGENTDEV_DEBUG_TRANSPORT === "claw" ? "claw" : "viewer-worker";
22
+ }
23
+ function getClawRuntimeUrl() {
24
+ return process.env.AGENTDEV_CLAW_RUNTIME_URL || "http://127.0.0.1:3030";
25
+ }
26
+
27
+ // src/core/claw-debug-client.ts
28
+ var ClawDebugClient = class {
29
+ runtimeUrl;
30
+ processId;
31
+ projectRoot;
32
+ sessionByAgentId = /* @__PURE__ */ new Map();
33
+ pendingSessionByAgentId = /* @__PURE__ */ new Map();
34
+ constructor(options = {}) {
35
+ this.runtimeUrl = (options.runtimeUrl ?? getClawRuntimeUrl()).replace(/\/$/, "");
36
+ this.processId = options.processId ?? String(process.pid);
37
+ this.projectRoot = options.projectRoot ?? process.cwd();
38
+ }
39
+ async ping() {
40
+ await this.requestJson("/health");
41
+ }
42
+ async registerAgent(input) {
43
+ const sessionId = `${this.processId}:${input.agentId}`;
44
+ const existing = this.pendingSessionByAgentId.get(input.agentId);
45
+ if (existing) {
46
+ return existing;
47
+ }
48
+ const pending = (async () => {
49
+ const registration = {
50
+ sessionId,
51
+ runtime: "agentdev",
52
+ agentName: input.name,
53
+ projectRoot: input.projectRoot ?? this.projectRoot,
54
+ metadata: {
55
+ adapter: "agentdev-debug-hub",
56
+ agentId: input.agentId,
57
+ templateMounts: input.templateMounts ?? [],
58
+ templateEntries: input.templateEntries ?? {}
59
+ },
60
+ state: {
61
+ hookInspector: input.hookInspector ?? null,
62
+ overview: input.overview ?? null
63
+ }
64
+ };
65
+ await this.requestJson("/api/sessions/register", {
66
+ method: "POST",
67
+ body: JSON.stringify(registration)
68
+ });
69
+ this.sessionByAgentId.set(input.agentId, sessionId);
70
+ await this.pushLifecycle(input.agentId, {
71
+ phase: "agent-registered",
72
+ agentId: input.agentId,
73
+ name: input.name
74
+ });
75
+ return sessionId;
76
+ })();
77
+ this.pendingSessionByAgentId.set(input.agentId, pending);
78
+ try {
79
+ return await pending;
80
+ } finally {
81
+ this.pendingSessionByAgentId.delete(input.agentId);
82
+ }
83
+ }
84
+ async unregisterAgent(agentId) {
85
+ await this.pushLifecycle(agentId, {
86
+ phase: "agent-unregistered",
87
+ agentId
88
+ });
89
+ }
90
+ async pushMessages(agentId, messages) {
91
+ await this.pushEvent(agentId, "message", { messages });
92
+ }
93
+ async registerTools(agentId, tools) {
94
+ await this.pushEvent(agentId, "tools", { tools });
95
+ }
96
+ async updateInspector(agentId, hookInspector) {
97
+ await this.pushEvent(agentId, "snapshot", {
98
+ scope: "hookInspector",
99
+ hookInspector
100
+ });
101
+ }
102
+ async updateOverview(agentId, overview) {
103
+ await this.pushEvent(agentId, "snapshot", {
104
+ scope: "overview",
105
+ overview
106
+ });
107
+ }
108
+ async updateTodoPlan(agentId, plan) {
109
+ await this.pushEvent(agentId, "todo-plan", {
110
+ scope: "todoPlan",
111
+ plan
112
+ });
113
+ }
114
+ async pushNotification(agentId, notification) {
115
+ await this.pushEvent(agentId, "notification", { notification });
116
+ }
117
+ async requestUserInput(_agentId, request, timeout, signal) {
118
+ const agentId = _agentId;
119
+ const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
120
+ await this.pushEvent(agentId, "input", {
121
+ requestId,
122
+ request,
123
+ timeout,
124
+ status: "pending"
125
+ });
126
+ const sessionId = await this.requireSessionId(agentId);
127
+ const startedAt = Date.now();
128
+ const pollIntervalMs = 400;
129
+ while (true) {
130
+ if (signal?.aborted) {
131
+ throw this.createAbortError(`User input request for Agent '${agentId}' was cancelled`);
132
+ }
133
+ if (timeout !== Infinity && Date.now() - startedAt > timeout) {
134
+ throw new Error(`User input timeout after ${timeout}ms`);
135
+ }
136
+ let response;
137
+ try {
138
+ response = await fetch(
139
+ `${this.runtimeUrl}/api/agents/${encodeURIComponent(sessionId)}/input-response?requestId=${encodeURIComponent(requestId)}`,
140
+ { signal }
141
+ );
142
+ } catch (error) {
143
+ const message = error instanceof Error ? error.message : String(error);
144
+ throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl} while waiting for user input: ${message}`);
145
+ }
146
+ if (response.ok) {
147
+ const body = await response.json();
148
+ return body.response;
149
+ }
150
+ if (response.status !== 404) {
151
+ const text = await response.text();
152
+ throw new Error(`Input bridge request failed: ${response.status} ${response.statusText} ${text}`);
153
+ }
154
+ await this.delay(pollIntervalMs, signal);
155
+ }
156
+ }
157
+ delay(ms, signal) {
158
+ if (signal?.aborted) {
159
+ return Promise.reject(this.createAbortError("Operation was cancelled"));
160
+ }
161
+ return new Promise((resolve, reject) => {
162
+ const timer = setTimeout(() => {
163
+ signal?.removeEventListener("abort", onAbort);
164
+ resolve();
165
+ }, ms);
166
+ const onAbort = () => {
167
+ clearTimeout(timer);
168
+ reject(this.createAbortError("Operation was cancelled"));
169
+ };
170
+ signal?.addEventListener("abort", onAbort, { once: true });
171
+ });
172
+ }
173
+ createAbortError(message) {
174
+ const error = new Error(message);
175
+ error.name = "AbortError";
176
+ return error;
177
+ }
178
+ async pushLifecycle(agentId, payload) {
179
+ await this.pushEvent(agentId, "lifecycle", payload);
180
+ }
181
+ async pushEvent(agentId, kind, payload) {
182
+ const sessionId = await this.requireSessionId(agentId);
183
+ const event = {
184
+ sessionId,
185
+ kind,
186
+ payload
187
+ };
188
+ await this.requestJson("/api/events", {
189
+ method: "POST",
190
+ body: JSON.stringify(event)
191
+ });
192
+ }
193
+ async requireSessionId(agentId) {
194
+ const sessionId = this.sessionByAgentId.get(agentId);
195
+ if (!sessionId) {
196
+ const pending = this.pendingSessionByAgentId.get(agentId);
197
+ if (pending) {
198
+ return pending;
199
+ }
200
+ throw new Error(`No Claw session registered for agentId '${agentId}'`);
201
+ }
202
+ return sessionId;
203
+ }
204
+ async requestJson(path, init) {
205
+ let response;
206
+ try {
207
+ response = await fetch(`${this.runtimeUrl}${path}`, {
208
+ ...init,
209
+ headers: {
210
+ "content-type": "application/json",
211
+ ...init?.headers ?? {}
212
+ }
213
+ });
214
+ } catch (error) {
215
+ const message = error instanceof Error ? error.message : String(error);
216
+ throw new Error(`Unable to reach Claw runtime at ${this.runtimeUrl}: ${message}`);
217
+ }
218
+ if (!response.ok) {
219
+ const body = await response.text();
220
+ throw new Error(`Claw runtime request failed: ${response.status} ${response.statusText} ${body}`);
221
+ }
222
+ if (response.status === 204) {
223
+ return null;
224
+ }
225
+ return response.json();
226
+ }
227
+ };
228
+
229
+ // src/core/debug-capabilities.ts
230
+ function getDebugCapabilities() {
231
+ const transportMode = resolveDebugTransportMode();
232
+ const isClaw = transportMode === "claw";
233
+ return {
234
+ transportMode,
235
+ interactiveInput: true,
236
+ runtimeUrl: isClaw ? getClawRuntimeUrl() : null,
237
+ viewerCompatibleApi: isClaw,
238
+ debuggerMcpMetadata: isClaw
239
+ };
240
+ }
241
+
242
+ // src/core/debug-hub.ts
243
+ var DebugHub = class _DebugHub {
244
+ static instance;
245
+ transportMode;
246
+ clawClient;
247
+ // ========== 状态 ==========
248
+ agents = /* @__PURE__ */ new Map();
249
+ nextId = 1;
250
+ processId;
251
+ // 进程唯一标识
252
+ // 输入请求回调映射:requestId → owned request lifecycle
253
+ pendingInputRequests = /* @__PURE__ */ new Map();
254
+ interruptHandlers = /* @__PURE__ */ new Map();
255
+ globalInterruptHandler;
256
+ // 活跃的输入请求元数据(用于重连恢复):agentId → requestInfo
257
+ activeInputRequests = /* @__PURE__ */ new Map();
258
+ // UDS 客户端连接
259
+ udsClient;
260
+ udsPath;
261
+ workerReadBuffer = "";
262
+ workerPort = null;
263
+ clientReady = false;
264
+ stopped = false;
265
+ /** Shared connection barrier for every Agent hosted by this process. */
266
+ connectionPromise;
267
+ /** headless 运行时(无 ViewerWorker)下每类消息只告警一次,避免刷屏。 */
268
+ sendToWorkerWarnedTypes = /* @__PURE__ */ new Set();
269
+ // 注册锁(防止并发竞争)
270
+ registrationLock = false;
271
+ // 重连机制
272
+ reconnectTimer;
273
+ reconnectAttempts = 0;
274
+ MAX_RECONNECT_ATTEMPTS = 10;
275
+ RECONNECT_DELAY = 2e3;
276
+ // 缓存每个 Agent 的模板装载载荷(mounts + entries,用于重连后重新注册)
277
+ agentTemplatePayload = /* @__PURE__ */ new Map();
278
+ // 缓存每个 Agent 的外部输入策略(用于重连后重新注册)
279
+ agentInputPolicy = /* @__PURE__ */ new Map();
280
+ // ========== 单例 ==========
281
+ constructor() {
282
+ this.udsPath = process.env.AGENTDEV_UDS_PATH || getDefaultUDSPath();
283
+ this.processId = String(process.pid);
284
+ this.transportMode = resolveDebugTransportMode();
285
+ if (this.transportMode === "claw") {
286
+ this.clawClient = new ClawDebugClient({
287
+ processId: this.processId,
288
+ projectRoot: process.cwd()
289
+ });
290
+ }
291
+ }
292
+ static getInstance() {
293
+ if (!_DebugHub.instance) {
294
+ _DebugHub.instance = new _DebugHub();
295
+ }
296
+ return _DebugHub.instance;
297
+ }
298
+ /**
299
+ * 为 Agent 分配进程内稳定且唯一的运行时 ID。
300
+ * 分配不等于注册;同一 ID 可在后续 Viewer attach/re-attach 时复用。
301
+ */
302
+ allocateAgentId() {
303
+ return `agent-${this.nextId++}-${this.processId}`;
304
+ }
305
+ // ========== 公开 API ==========
306
+ /**
307
+ * 启动调试服务器
308
+ * @param port HTTP 端口(默认 2026,仅用于显示)
309
+ * @param openBrowser 是否自动打开浏览器(默认 true,已废弃参数)
310
+ */
311
+ async start(port = 2026, openBrowser = true) {
312
+ if (this.clientReady) return;
313
+ if (this.connectionPromise) return this.connectionPromise;
314
+ const pending = this.startInternal(port, openBrowser);
315
+ this.connectionPromise = pending;
316
+ try {
317
+ await pending;
318
+ } finally {
319
+ if (this.connectionPromise === pending) {
320
+ this.connectionPromise = void 0;
321
+ }
322
+ }
323
+ }
324
+ async startInternal(port, openBrowser) {
325
+ this.stopped = false;
326
+ if (this.transportMode === "claw") {
327
+ this.workerPort = port;
328
+ try {
329
+ await this.clawClient?.ping();
330
+ this.clientReady = true;
331
+ console.log(`[DebugHub] \u5DF2\u8FDE\u63A5\u5230 Claw runtime: ${getClawRuntimeUrl()}`);
332
+ } catch (err) {
333
+ console.warn(`[DebugHub] \u65E0\u6CD5\u8FDE\u63A5\u5230 Claw runtime: ${err.message}`);
334
+ console.warn("[DebugHub] \u8C03\u8BD5\u529F\u80FD\u5C06\u88AB\u7981\u7528\u3002\u8BF7\u5148\u542F\u52A8 AgentDevClaw runtime\u3002");
335
+ this.clientReady = false;
336
+ throw err;
337
+ }
338
+ return;
339
+ }
340
+ this.workerPort = port;
341
+ this.openBrowser = openBrowser;
342
+ try {
343
+ await this.connectToWorker();
344
+ console.log(`[DebugHub] \u8C03\u8BD5\u670D\u52A1\u5668\u5DF2\u8FDE\u63A5: http://localhost:${port}`);
345
+ } catch (err) {
346
+ console.log(`[DebugHub] ViewerWorker \u672A\u8FD0\u884C\uFF0C\u6B63\u5728\u81EA\u52A8\u542F\u52A8...`);
347
+ try {
348
+ await this.spawnViewerWorker();
349
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
350
+ await this.connectToWorker();
351
+ console.log(`[DebugHub] \u8C03\u8BD5\u670D\u52A1\u5668\u5DF2\u8FDE\u63A5: http://localhost:${port}`);
352
+ } catch (spawnErr) {
353
+ console.warn(`[DebugHub] \u65E0\u6CD5\u542F\u52A8 ViewerWorker: ${spawnErr.message}`);
354
+ console.warn(`[DebugHub] \u8C03\u8BD5\u529F\u80FD\u5C06\u88AB\u7981\u7528\u3002\u8BF7\u624B\u52A8\u8FD0\u884C 'agentdev-viewer' \u542F\u52A8\u8C03\u8BD5\u670D\u52A1\u5668\u3002`);
355
+ this.clientReady = false;
356
+ throw spawnErr;
357
+ }
358
+ }
359
+ }
360
+ /**
361
+ * 自动启动 ViewerWorker 进程
362
+ */
363
+ openBrowser = true;
364
+ viewerWorkerProcess;
365
+ async spawnViewerWorker() {
366
+ const { spawn } = await import("child_process");
367
+ const { existsSync } = await import("fs");
368
+ const { fileURLToPath } = await import("url");
369
+ const { dirname, join } = await import("path");
370
+ let viewerPath;
371
+ try {
372
+ const agentdevPath = __require.resolve("agentdev/package.json");
373
+ viewerPath = join(dirname(agentdevPath), "dist", "cli", "viewer.js");
374
+ } catch {
375
+ try {
376
+ const currentDir = dirname(fileURLToPath(import.meta.url));
377
+ viewerPath = join(currentDir, "..", "cli", "viewer.js");
378
+ } catch {
379
+ viewerPath = "agentdev-viewer";
380
+ }
381
+ }
382
+ if (viewerPath !== "agentdev-viewer" && !existsSync(viewerPath)) {
383
+ viewerPath = "agentdev-viewer";
384
+ }
385
+ return new Promise((resolve, reject) => {
386
+ try {
387
+ const env = {
388
+ ...process.env,
389
+ AGENTDEV_PORT: String(this.workerPort || 2026),
390
+ AGENTDEV_OPEN_BROWSER: this.openBrowser ? "true" : "false",
391
+ AGENTDEV_UDS_PATH: this.udsPath
392
+ };
393
+ console.log(`[DebugHub] \u542F\u52A8 ViewerWorker: ${viewerPath}`);
394
+ const isBinCommand = viewerPath === "agentdev-viewer" || viewerPath === "agentdev-server";
395
+ const command = isBinCommand ? viewerPath : "node";
396
+ const args = isBinCommand ? [] : [viewerPath];
397
+ this.viewerWorkerProcess = spawn(command, args, {
398
+ env,
399
+ stdio: ["ignore", "pipe", "pipe"],
400
+ detached: false,
401
+ shell: isBinCommand
402
+ // bin 命令需要 shell 来解析
403
+ });
404
+ this.viewerWorkerProcess.on("error", (err) => {
405
+ console.error("[DebugHub] ViewerWorker \u8FDB\u7A0B\u9519\u8BEF:", err.message);
406
+ reject(err);
407
+ });
408
+ this.viewerWorkerProcess.stdout?.on("data", (data) => {
409
+ const lines = data.toString().trim().split("\n");
410
+ for (const line of lines) {
411
+ console.log(`[ViewerWorker] ${line}`);
412
+ }
413
+ });
414
+ this.viewerWorkerProcess.stderr?.on("data", (data) => {
415
+ const lines = data.toString().trim().split("\n");
416
+ for (const line of lines) {
417
+ console.error(`[ViewerWorker] ${line}`);
418
+ }
419
+ });
420
+ setTimeout(() => {
421
+ if (this.viewerWorkerProcess && !this.viewerWorkerProcess.killed) {
422
+ resolve();
423
+ } else {
424
+ reject(new Error("ViewerWorker \u8FDB\u7A0B\u542F\u52A8\u5931\u8D25"));
425
+ }
426
+ }, 500);
427
+ } catch (err) {
428
+ reject(err);
429
+ }
430
+ });
431
+ }
432
+ /**
433
+ * 停止调试服务器
434
+ */
435
+ stop() {
436
+ this.stopped = true;
437
+ if (this.transportMode === "claw") {
438
+ this.clientReady = false;
439
+ return;
440
+ }
441
+ if (this.reconnectTimer) {
442
+ clearTimeout(this.reconnectTimer);
443
+ this.reconnectTimer = void 0;
444
+ }
445
+ if (this.udsClient) {
446
+ this.sendToWorker({ type: "stop" });
447
+ this.udsClient.end();
448
+ this.udsClient = void 0;
449
+ this.clientReady = false;
450
+ }
451
+ }
452
+ /**
453
+ * 手动重连(可选)
454
+ * 如果已经连接,则不执行任何操作
455
+ */
456
+ async reconnect() {
457
+ if (this.transportMode === "claw") {
458
+ await this.start(this.workerPort ?? 2026, false);
459
+ if (!this.clientReady) {
460
+ throw new Error("Claw runtime reconnect failed");
461
+ }
462
+ return;
463
+ }
464
+ if (this.clientReady && this.udsClient) {
465
+ console.log("[DebugHub] \u5DF2\u7ECF\u8FDE\u63A5\uFF0C\u65E0\u9700\u91CD\u8FDE");
466
+ return;
467
+ }
468
+ this.reconnectAttempts = 0;
469
+ if (this.reconnectTimer) {
470
+ clearTimeout(this.reconnectTimer);
471
+ this.reconnectTimer = void 0;
472
+ }
473
+ try {
474
+ await this.connectToWorker();
475
+ console.log("[DebugHub] \u2705 \u624B\u52A8\u91CD\u8FDE\u6210\u529F");
476
+ } catch (error) {
477
+ console.error(`[DebugHub] \u624B\u52A8\u91CD\u8FDE\u5931\u8D25: ${error.message}`);
478
+ throw error;
479
+ }
480
+ }
481
+ /**
482
+ * 注册 Agent
483
+ * @param agent Agent 实例
484
+ * @param name 显示名称(可选,默认使用类名)
485
+ * @param templateMounts 模板装载点(真实目录根,可选)
486
+ * @param templateEntries 模板名 → {mount 下标, rel 相对路径}(可选)
487
+ * @returns 分配的 agentId
488
+ */
489
+ registerAgent(agent, name, templateMounts, templateEntries, hookInspector, overview, projectRoot, reservedAgentId, inputPolicy) {
490
+ while (this.registrationLock) {
491
+ }
492
+ this.registrationLock = true;
493
+ try {
494
+ const resolvedProjectRoot = projectRoot || process.cwd();
495
+ const id = reservedAgentId || this.allocateAgentId();
496
+ const existing = this.agents.get(id);
497
+ if (existing && existing.agent !== agent) {
498
+ throw new Error(`Agent ID '${id}' is already registered by another Agent instance`);
499
+ }
500
+ const info = {
501
+ id,
502
+ name: name || agent.constructor.name,
503
+ registeredAt: existing?.info.registeredAt ?? Date.now(),
504
+ projectRoot: resolvedProjectRoot
505
+ };
506
+ this.agents.set(id, { info, agent });
507
+ if (templateMounts || templateEntries) {
508
+ this.agentTemplatePayload.set(id, {
509
+ mounts: templateMounts ?? [],
510
+ entries: templateEntries ?? {}
511
+ });
512
+ }
513
+ if (inputPolicy) {
514
+ this.agentInputPolicy.set(id, inputPolicy);
515
+ }
516
+ if (this.transportMode === "claw") {
517
+ void this.clawClient?.registerAgent({
518
+ agentId: id,
519
+ name: info.name,
520
+ projectRoot: resolvedProjectRoot,
521
+ templateMounts: templateMounts ?? [],
522
+ templateEntries: templateEntries ?? {},
523
+ hookInspector,
524
+ overview,
525
+ inputPolicy: inputPolicy || void 0
526
+ }).catch((error) => {
527
+ console.error(`[DebugHub] Claw registerAgent \u5931\u8D25: ${error.message}`);
528
+ });
529
+ } else {
530
+ this.sendToWorker({
531
+ type: "register-agent",
532
+ agentId: id,
533
+ name: info.name,
534
+ createdAt: info.registeredAt,
535
+ projectRoot: resolvedProjectRoot,
536
+ templateMounts: templateMounts ?? [],
537
+ templateEntries: templateEntries ?? {},
538
+ hookInspector,
539
+ overview,
540
+ inputPolicy: inputPolicy || void 0
541
+ });
542
+ }
543
+ console.log(`[DebugHub] Agent \u5DF2\u6CE8\u518C: ${id} (${info.name})`);
544
+ return id;
545
+ } finally {
546
+ this.registrationLock = false;
547
+ }
548
+ }
549
+ /**
550
+ * 注销 Agent
551
+ * @param agentId Agent ID
552
+ */
553
+ unregisterAgent(agentId) {
554
+ this.interruptHandlers.delete(agentId);
555
+ this.cancelInputRequests(agentId, `Agent '${agentId}' was unregistered`);
556
+ this.activeInputRequests.delete(agentId);
557
+ const deleted = this.agents.delete(agentId);
558
+ if (deleted) {
559
+ this.agentTemplatePayload.delete(agentId);
560
+ this.agentInputPolicy.delete(agentId);
561
+ if (this.transportMode === "claw") {
562
+ void this.clawClient?.unregisterAgent(agentId).catch((error) => {
563
+ console.error(`[DebugHub] Claw unregisterAgent \u5931\u8D25: ${error.message}`);
564
+ });
565
+ } else {
566
+ this.sendToWorker({ type: "unregister-agent", agentId });
567
+ }
568
+ console.log(`[DebugHub] Agent \u5DF2\u6CE8\u9500: ${agentId}`);
569
+ }
570
+ }
571
+ /**
572
+ * 推送 Agent 消息
573
+ * @param agentId Agent ID
574
+ * @param messages 消息数组
575
+ */
576
+ pushMessages(agentId, messages) {
577
+ if (this.transportMode === "claw") {
578
+ void this.clawClient?.pushMessages(agentId, messages).catch((error) => {
579
+ console.error(`[DebugHub] Claw pushMessages \u5931\u8D25: ${error.message}`);
580
+ });
581
+ return;
582
+ }
583
+ this.sendToWorker({
584
+ type: "push-messages",
585
+ agentId,
586
+ messages
587
+ });
588
+ }
589
+ /**
590
+ * 注册 Agent 工具
591
+ * @param agentId Agent ID
592
+ * @param tools 工具数组
593
+ */
594
+ registerAgentTools(agentId, tools) {
595
+ if (this.transportMode === "claw") {
596
+ void this.clawClient?.registerTools(agentId, tools).catch((error) => {
597
+ console.error(`[DebugHub] Claw registerTools \u5931\u8D25: ${error.message}`);
598
+ });
599
+ return;
600
+ }
601
+ this.sendToWorker({
602
+ type: "register-tools",
603
+ agentId,
604
+ tools
605
+ });
606
+ }
607
+ updateAgentInspector(agentId, hookInspector) {
608
+ if (this.transportMode === "claw") {
609
+ void this.clawClient?.updateInspector(agentId, hookInspector).catch((error) => {
610
+ console.error(`[DebugHub] Claw updateInspector \u5931\u8D25: ${error.message}`);
611
+ });
612
+ return;
613
+ }
614
+ this.sendToWorker({
615
+ type: "update-agent-inspector",
616
+ agentId,
617
+ hookInspector
618
+ });
619
+ }
620
+ updateAgentOverview(agentId, overview) {
621
+ if (this.transportMode === "claw") {
622
+ void this.clawClient?.updateOverview(agentId, overview).catch((error) => {
623
+ console.error(`[DebugHub] Claw updateOverview \u5931\u8D25: ${error.message}`);
624
+ });
625
+ return;
626
+ }
627
+ this.sendToWorker({
628
+ type: "update-agent-overview",
629
+ agentId,
630
+ overview
631
+ });
632
+ }
633
+ updateTodoPlan(agentId, plan) {
634
+ if (this.transportMode === "claw") {
635
+ void this.clawClient?.updateTodoPlan(agentId, plan).catch((error) => {
636
+ console.error(`[DebugHub] Claw updateTodoPlan \u5931\u8D25: ${error.message}`);
637
+ });
638
+ return;
639
+ }
640
+ this.sendToWorker({
641
+ type: "update-todo-plan",
642
+ agentId,
643
+ plan
644
+ });
645
+ }
646
+ /**
647
+ * 获取所有已注册的 Agent 信息
648
+ */
649
+ getAgentList() {
650
+ return Array.from(this.agents.values()).map((v) => v.info);
651
+ }
652
+ isAgentRegistered(agentId) {
653
+ return this.agents.has(agentId);
654
+ }
655
+ getTransportMode() {
656
+ return this.transportMode;
657
+ }
658
+ getCapabilities() {
659
+ return getDebugCapabilities();
660
+ }
661
+ setInterruptHandler(agentIdOrHandler, handler) {
662
+ if (typeof agentIdOrHandler === "string") {
663
+ if (handler) {
664
+ this.interruptHandlers.set(agentIdOrHandler, handler);
665
+ return () => {
666
+ if (this.interruptHandlers.get(agentIdOrHandler) === handler) {
667
+ this.interruptHandlers.delete(agentIdOrHandler);
668
+ }
669
+ };
670
+ } else {
671
+ this.interruptHandlers.delete(agentIdOrHandler);
672
+ return () => {
673
+ };
674
+ }
675
+ } else {
676
+ this.globalInterruptHandler = agentIdOrHandler;
677
+ return () => {
678
+ if (this.globalInterruptHandler === agentIdOrHandler) {
679
+ this.globalInterruptHandler = void 0;
680
+ }
681
+ };
682
+ }
683
+ }
684
+ /**
685
+ * 根据 Agent 实例获取其 ID
686
+ */
687
+ getAgentId(agent) {
688
+ for (const [id, data] of this.agents) {
689
+ if (data.agent === agent) {
690
+ return id;
691
+ }
692
+ }
693
+ return void 0;
694
+ }
695
+ /**
696
+ * 获取 Worker 端口
697
+ */
698
+ getPort() {
699
+ return this.workerPort;
700
+ }
701
+ /**
702
+ * 检查是否已连接到 ViewerWorker
703
+ */
704
+ isConnected() {
705
+ if (this.transportMode === "claw") {
706
+ return this.clientReady;
707
+ }
708
+ return this.clientReady && !!this.udsClient;
709
+ }
710
+ /**
711
+ * 推送通知
712
+ * @param agentId Agent ID
713
+ * @param notification 通知对象
714
+ */
715
+ pushNotification(agentId, notification) {
716
+ if (this.transportMode === "claw") {
717
+ void this.clawClient?.pushNotification(agentId, notification).catch((error) => {
718
+ console.error(`[DebugHub] Claw pushNotification \u5931\u8D25: ${error.message}`);
719
+ });
720
+ return;
721
+ }
722
+ this.sendToWorker({
723
+ type: "push-notification",
724
+ agentId,
725
+ notification
726
+ });
727
+ }
728
+ /**
729
+ * 请求用户输入
730
+ * @param agentId Agent ID
731
+ * @param prompt 提示信息
732
+ * @param timeout 超时时间(毫秒),默认 Infinity(无限等待)
733
+ * @returns Promise<string> 用户输入内容
734
+ */
735
+ requestUserInput(agentId, prompt, timeout = Infinity) {
736
+ return this.requestUserInputEvent(agentId, { prompt }, timeout).then((response) => {
737
+ if (response.kind !== "text") {
738
+ throw new Error(`Expected text user input but received action '${response.actionId ?? "unknown"}'`);
739
+ }
740
+ return response.text ?? "";
741
+ });
742
+ }
743
+ requestUserInputEvent(agentId, request, timeout = Infinity) {
744
+ if (this.activeInputRequests.has(agentId)) {
745
+ return Promise.reject(new Error(`Agent '${agentId}' already has an active user input lease`));
746
+ }
747
+ if (this.transportMode === "claw") {
748
+ if (!this.clawClient) {
749
+ return Promise.reject(new Error("Claw client is not available"));
750
+ }
751
+ const localRequestId = `claw-input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
752
+ const abortController = new AbortController();
753
+ return new Promise((resolve, reject) => {
754
+ this.pendingInputRequests.set(localRequestId, {
755
+ agentId,
756
+ resolve,
757
+ reject,
758
+ abortController
759
+ });
760
+ this.activeInputRequests.set(agentId, {
761
+ requestId: localRequestId,
762
+ prompt: request.prompt,
763
+ placeholder: request.placeholder,
764
+ initialValue: request.initialValue,
765
+ actions: request.actions,
766
+ mode: request.mode,
767
+ questions: request.questions,
768
+ timestamp: Date.now()
769
+ });
770
+ this.clawClient.requestUserInput(agentId, request, timeout, abortController.signal).then((response) => this.settleInputRequest(localRequestId, { response })).catch((error) => this.settleInputRequest(localRequestId, {
771
+ error: error instanceof Error ? error : new Error(String(error))
772
+ }));
773
+ });
774
+ }
775
+ const requestId = `input-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
776
+ return new Promise((resolve, reject) => {
777
+ const pending = { agentId, resolve, reject };
778
+ if (timeout !== Infinity) {
779
+ pending.timer = setTimeout(() => {
780
+ this.settleInputRequest(requestId, {
781
+ error: new Error(`User input timeout after ${timeout}ms`)
782
+ });
783
+ }, timeout);
784
+ }
785
+ this.pendingInputRequests.set(requestId, pending);
786
+ this.activeInputRequests.set(agentId, {
787
+ requestId,
788
+ prompt: request.prompt,
789
+ placeholder: request.placeholder,
790
+ initialValue: request.initialValue,
791
+ actions: request.actions,
792
+ mode: request.mode,
793
+ questions: request.questions,
794
+ timestamp: Date.now()
795
+ });
796
+ this.sendToWorker({
797
+ type: "request-input",
798
+ agentId,
799
+ requestId,
800
+ prompt: request.prompt,
801
+ placeholder: request.placeholder,
802
+ initialValue: request.initialValue,
803
+ actions: request.actions,
804
+ mode: request.mode,
805
+ questions: request.questions,
806
+ timeout
807
+ });
808
+ });
809
+ }
810
+ // ========== 内部方法 ==========
811
+ /**
812
+ * 连接到 UDS 服务器
813
+ */
814
+ async connectToWorker() {
815
+ return new Promise((resolve, reject) => {
816
+ const socket = connect(this.udsPath);
817
+ this.udsClient = socket;
818
+ socket.on("connect", () => {
819
+ this.workerReadBuffer = "";
820
+ this.clientReady = true;
821
+ this.reconnectAttempts = 0;
822
+ console.log(`[DebugHub] \u5DF2\u8FDE\u63A5\u5230 ViewerWorker: ${this.udsPath}`);
823
+ this.reregisterAllAgents();
824
+ resolve();
825
+ });
826
+ socket.setEncoding("utf8");
827
+ socket.on("data", (data) => {
828
+ this.workerReadBuffer += data;
829
+ const lines = this.workerReadBuffer.split("\n");
830
+ this.workerReadBuffer = lines.pop() ?? "";
831
+ for (const line of lines) {
832
+ if (!line.trim()) continue;
833
+ try {
834
+ const msg = JSON.parse(line);
835
+ this.handleWorkerMessage(msg);
836
+ } catch (err) {
837
+ console.error("[DebugHub] Worker \u6D88\u606F\u89E3\u6790\u5931\u8D25:", err);
838
+ }
839
+ }
840
+ });
841
+ socket.on("error", (err) => {
842
+ if (this.udsClient === socket) {
843
+ this.udsClient = void 0;
844
+ this.clientReady = false;
845
+ }
846
+ reject(new Error(`\u8FDE\u63A5 ViewerWorker \u5931\u8D25 (${this.udsPath}): ${err.message}
847
+ \u8BF7\u5148\u542F\u52A8 ViewerWorker \u670D\u52A1\u5668`));
848
+ });
849
+ socket.on("close", () => {
850
+ this.workerReadBuffer = "";
851
+ this.clientReady = false;
852
+ if (this.udsClient === socket) {
853
+ this.udsClient = void 0;
854
+ }
855
+ console.warn("[DebugHub] \u4E0E ViewerWorker \u7684\u8FDE\u63A5\u5DF2\u65AD\u5F00");
856
+ if (!this.stopped) {
857
+ this.scheduleReconnect();
858
+ }
859
+ });
860
+ });
861
+ }
862
+ /**
863
+ * 处理来自 Worker 的消息
864
+ */
865
+ handleWorkerMessage(msg) {
866
+ switch (msg.type) {
867
+ // 处理用户输入响应
868
+ case "input-response": {
869
+ if (this.pendingInputRequests.has(msg.requestId)) {
870
+ this.settleInputRequest(msg.requestId, { response: msg.response ?? {
871
+ kind: "text",
872
+ text: msg.input
873
+ } });
874
+ } else {
875
+ console.warn(`[DebugHub] \u672A\u77E5\u8F93\u5165\u54CD\u5E94: ${msg.requestId}`);
876
+ }
877
+ break;
878
+ }
879
+ // 处理中断信号
880
+ case "interrupt-agent": {
881
+ console.log(`[DebugHub] \u6536\u5230\u4E2D\u65AD\u4FE1\u53F7: agentId=${msg.agentId}, knownAgents=[${[...this.agents.keys()].join(",")}]`);
882
+ const agentData = this.agents.get(msg.agentId);
883
+ console.log(`[DebugHub] agentData found=${!!agentData}, hasAgent=${!!agentData?.agent}, hasInterrupt=${typeof agentData?.agent?.interrupt}`);
884
+ let interruptAccepted = false;
885
+ if (agentData?.agent && typeof agentData.agent.interrupt === "function") {
886
+ interruptAccepted = agentData.agent.interrupt() === true;
887
+ console.log(`[DebugHub] agent.interrupt() returned: ${interruptAccepted}`);
888
+ } else {
889
+ console.warn(`[DebugHub] Agent \u4E0D\u652F\u6301\u4E2D\u65AD: ${msg.agentId}`);
890
+ }
891
+ if (interruptAccepted) {
892
+ this.cancelInputRequests(msg.agentId, `Agent '${msg.agentId}' was interrupted`);
893
+ }
894
+ const specificHandler = this.interruptHandlers.get(msg.agentId);
895
+ const effectiveHandler = specificHandler ?? this.globalInterruptHandler;
896
+ if (effectiveHandler) {
897
+ Promise.resolve(effectiveHandler(msg.agentId, msg.clearQueue === true)).catch((error) => {
898
+ console.error("[DebugHub] interruptHandler \u6267\u884C\u5931\u8D25:", error);
899
+ });
900
+ }
901
+ break;
902
+ }
903
+ }
904
+ }
905
+ /**
906
+ * 重新注册所有 Agent(重连后调用)
907
+ * 确保 ViewerWorker 能够恢复所有 Agent 的注册信息
908
+ */
909
+ reregisterAllAgents() {
910
+ if (this.transportMode === "claw") {
911
+ for (const [id, data] of this.agents) {
912
+ const hookInspector = data.agent.buildHookInspectorSnapshot?.() || data.agent.hookInspector;
913
+ const overview = data.agent.buildOverviewSnapshot?.();
914
+ const templatePayload = this.agentTemplatePayload.get(id) || { mounts: [], entries: {} };
915
+ void this.clawClient?.registerAgent({
916
+ agentId: id,
917
+ name: data.info.name,
918
+ projectRoot: data.info.projectRoot || process.cwd(),
919
+ templateMounts: templatePayload.mounts,
920
+ templateEntries: templatePayload.entries,
921
+ hookInspector,
922
+ overview,
923
+ inputPolicy: this.agentInputPolicy.get(id) || void 0
924
+ }).then(async () => {
925
+ const tools = data.agent.tools;
926
+ if (tools && typeof tools.getEntries === "function") {
927
+ const entries = tools.getEntries();
928
+ const toolList = entries.map((e) => e.tool);
929
+ if (toolList.length > 0) {
930
+ await this.clawClient?.registerTools(id, toolList);
931
+ }
932
+ }
933
+ const context = data.agent.getContext?.();
934
+ if (context && typeof context.getAll === "function") {
935
+ const messages = context.getAll();
936
+ if (messages.length > 0) {
937
+ await this.clawClient?.pushMessages(id, messages);
938
+ }
939
+ }
940
+ }).catch((error) => {
941
+ console.error(`[DebugHub] Claw re-register \u5931\u8D25: ${error.message}`);
942
+ });
943
+ }
944
+ return;
945
+ }
946
+ if (this.agents.size === 0) {
947
+ return;
948
+ }
949
+ console.log(`[DebugHub] \u91CD\u65B0\u6CE8\u518C ${this.agents.size} \u4E2A Agent...`);
950
+ for (const [id, data] of this.agents) {
951
+ const hookInspector = data.agent.buildHookInspectorSnapshot?.() || data.agent.hookInspector;
952
+ const overview = data.agent.buildOverviewSnapshot?.();
953
+ const templatePayload = this.agentTemplatePayload.get(id) || { mounts: [], entries: {} };
954
+ const activeInputRequest = this.activeInputRequests.get(id);
955
+ if (activeInputRequest) {
956
+ console.log(`[DebugHub] \u53D1\u73B0\u6D3B\u8DC3\u8F93\u5165\u8BF7\u6C42: ${activeInputRequest.requestId}`);
957
+ }
958
+ this.sendToWorker({
959
+ type: "register-agent",
960
+ agentId: id,
961
+ name: data.info.name,
962
+ createdAt: data.info.registeredAt,
963
+ projectRoot: data.info.projectRoot || process.cwd(),
964
+ templateMounts: templatePayload.mounts,
965
+ templateEntries: templatePayload.entries,
966
+ hookInspector,
967
+ overview,
968
+ activeInputRequest,
969
+ // 携带活跃输入请求
970
+ inputPolicy: this.agentInputPolicy.get(id) || void 0
971
+ });
972
+ const tools = data.agent.tools;
973
+ if (tools && typeof tools.getEntries === "function") {
974
+ const entries = tools.getEntries();
975
+ const toolList = entries.map((e) => e.tool);
976
+ if (toolList.length > 0) {
977
+ this.sendToWorker({
978
+ type: "register-tools",
979
+ agentId: id,
980
+ tools: toolList
981
+ });
982
+ }
983
+ }
984
+ const context = data.agent.getContext?.();
985
+ if (context && typeof context.getAll === "function") {
986
+ const messages = context.getAll();
987
+ if (messages.length > 0) {
988
+ this.sendToWorker({
989
+ type: "push-messages",
990
+ agentId: id,
991
+ messages
992
+ });
993
+ console.log(`[DebugHub] \u6062\u590D Agent ${id} \u7684 ${messages.length} \u6761\u6D88\u606F`);
994
+ }
995
+ }
996
+ }
997
+ console.log(`[DebugHub] \u2705 \u91CD\u65B0\u6CE8\u518C\u5B8C\u6210`);
998
+ }
999
+ /**
1000
+ * 安排重连(指数退避)
1001
+ */
1002
+ scheduleReconnect() {
1003
+ if (this.stopped) {
1004
+ return;
1005
+ }
1006
+ if (this.reconnectTimer) {
1007
+ clearTimeout(this.reconnectTimer);
1008
+ }
1009
+ if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
1010
+ console.error(`[DebugHub] \u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.MAX_RECONNECT_ATTEMPTS})\uFF0C\u505C\u6B62\u91CD\u8FDE`);
1011
+ return;
1012
+ }
1013
+ this.reconnectAttempts++;
1014
+ const delay = Math.min(
1015
+ this.RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts - 1),
1016
+ 3e4
1017
+ );
1018
+ console.log(`[DebugHub] ${delay}ms \u540E\u5C1D\u8BD5\u7B2C ${this.reconnectAttempts} \u6B21\u91CD\u8FDE...`);
1019
+ this.reconnectTimer = setTimeout(async () => {
1020
+ try {
1021
+ await this.connectToWorker();
1022
+ console.log("[DebugHub] \u2705 \u91CD\u8FDE\u6210\u529F\uFF0C\u8C03\u8BD5\u529F\u80FD\u5DF2\u6062\u590D");
1023
+ } catch (error) {
1024
+ console.error(`[DebugHub] \u91CD\u8FDE\u5931\u8D25: ${error.message}`);
1025
+ if (!this.stopped) {
1026
+ this.scheduleReconnect();
1027
+ }
1028
+ }
1029
+ }, delay);
1030
+ }
1031
+ /**
1032
+ * 通过 UDS 发送消息
1033
+ */
1034
+ sendViaUDS(msg) {
1035
+ if (this.udsClient && this.clientReady) {
1036
+ this.udsClient.write(JSON.stringify(msg) + "\n");
1037
+ }
1038
+ }
1039
+ /** Cancel every input request owned by one Agent. Safe to call repeatedly. */
1040
+ cancelInputRequests(agentId, reason = `Agent '${agentId}' input was cancelled`) {
1041
+ for (const [requestId, pending] of this.pendingInputRequests) {
1042
+ if (pending.agentId !== agentId) continue;
1043
+ pending.abortController?.abort();
1044
+ this.settleInputRequest(requestId, { error: this.createAbortError(reason) });
1045
+ this.sendToWorker({ type: "input-request-cancelled", agentId, requestId });
1046
+ }
1047
+ this.activeInputRequests.delete(agentId);
1048
+ }
1049
+ settleInputRequest(requestId, outcome) {
1050
+ const pending = this.pendingInputRequests.get(requestId);
1051
+ if (!pending) return;
1052
+ this.pendingInputRequests.delete(requestId);
1053
+ if (pending.timer) clearTimeout(pending.timer);
1054
+ const active = this.activeInputRequests.get(pending.agentId);
1055
+ if (active?.requestId === requestId) {
1056
+ this.activeInputRequests.delete(pending.agentId);
1057
+ }
1058
+ if ("response" in outcome) {
1059
+ pending.resolve(outcome.response);
1060
+ } else {
1061
+ pending.reject(outcome.error);
1062
+ }
1063
+ }
1064
+ createAbortError(message) {
1065
+ const error = new Error(message);
1066
+ error.name = "AbortError";
1067
+ return error;
1068
+ }
1069
+ /**
1070
+ * 发送消息到 Worker
1071
+ */
1072
+ sendToWorker(msg) {
1073
+ if (!this.udsClient || !this.clientReady) {
1074
+ if (!this.sendToWorkerWarnedTypes.has(msg.type)) {
1075
+ this.sendToWorkerWarnedTypes.add(msg.type);
1076
+ console.warn(`[DebugHub] ViewerWorker transport is not ready; deferred state will reconcile on reconnect (message=${msg.type})`);
1077
+ }
1078
+ return;
1079
+ }
1080
+ this.sendViaUDS(msg);
1081
+ }
1082
+ };
1083
+
1084
+ // src/core/notification.ts
1085
+ var notificationAls = new AsyncLocalStorage();
1086
+ function runWithNotificationScope(agentId, fn) {
1087
+ return notificationAls.run(
1088
+ { agentId, lastNotificationTime: 0, lastLLMPhase: null },
1089
+ fn
1090
+ );
1091
+ }
1092
+ var _fallbackAgentId = null;
1093
+ var _fallbackLastTime = 0;
1094
+ var _fallbackLastPhase = null;
1095
+ var _fallbackScope = {
1096
+ get agentId() {
1097
+ return _fallbackAgentId;
1098
+ },
1099
+ get lastNotificationTime() {
1100
+ return _fallbackLastTime;
1101
+ },
1102
+ set lastNotificationTime(v) {
1103
+ _fallbackLastTime = v;
1104
+ },
1105
+ get lastLLMPhase() {
1106
+ return _fallbackLastPhase;
1107
+ },
1108
+ set lastLLMPhase(v) {
1109
+ _fallbackLastPhase = v;
1110
+ }
1111
+ };
1112
+ function getActiveScope() {
1113
+ const als = notificationAls.getStore();
1114
+ if (als) return als;
1115
+ if (_fallbackAgentId) return _fallbackScope;
1116
+ return null;
1117
+ }
1118
+ var THROTTLE_INTERVAL = 100;
1119
+ function _setNotificationAgent(agentId) {
1120
+ _fallbackAgentId = agentId;
1121
+ }
1122
+ function _clearNotificationAgent() {
1123
+ _fallbackAgentId = null;
1124
+ _fallbackLastTime = 0;
1125
+ _fallbackLastPhase = null;
1126
+ }
1127
+ function _getCurrentNotificationAgent() {
1128
+ return notificationAls.getStore()?.agentId ?? _fallbackAgentId;
1129
+ }
1130
+ function emitNotification(notification) {
1131
+ const scope = getActiveScope();
1132
+ if (!scope) {
1133
+ return;
1134
+ }
1135
+ const now = Date.now();
1136
+ const notificationType = String(notification.type || "");
1137
+ const bypassThrottle = notificationType === "call.start" || notificationType === "call.finish" || notificationType === "llm.complete";
1138
+ let phaseTransition = false;
1139
+ if (notificationType === "llm.char_count") {
1140
+ const data = notification.data;
1141
+ const phase = typeof data?.phase === "string" ? data.phase : "";
1142
+ if (phase && phase !== scope.lastLLMPhase) {
1143
+ phaseTransition = true;
1144
+ scope.lastLLMPhase = phase;
1145
+ }
1146
+ }
1147
+ if (notification.category === "state" && !bypassThrottle && !phaseTransition) {
1148
+ const timeSinceLast = now - scope.lastNotificationTime;
1149
+ if (timeSinceLast < THROTTLE_INTERVAL) {
1150
+ return;
1151
+ }
1152
+ scope.lastNotificationTime = now;
1153
+ } else if (phaseTransition) {
1154
+ scope.lastNotificationTime = now;
1155
+ }
1156
+ const debugHub = DebugHub.getInstance();
1157
+ debugHub.pushNotification(scope.agentId, notification);
1158
+ }
1159
+ function createLLMCharCount(charCount, phase, extras) {
1160
+ return {
1161
+ type: "llm.char_count",
1162
+ category: "state",
1163
+ timestamp: Date.now(),
1164
+ data: {
1165
+ charCount,
1166
+ phase,
1167
+ ...typeof extras?.thinkingChars === "number" ? { thinkingChars: extras.thinkingChars } : {},
1168
+ ...typeof extras?.contentChars === "number" ? { contentChars: extras.contentChars } : {},
1169
+ ...typeof extras?.toolCallCount === "number" ? { toolCallCount: extras.toolCallCount } : {},
1170
+ ...Array.isArray(extras?.streamToolNames) && extras.streamToolNames.length > 0 ? { streamToolNames: extras.streamToolNames } : {}
1171
+ }
1172
+ };
1173
+ }
1174
+ function createLLMComplete(totalChars) {
1175
+ return {
1176
+ type: "llm.complete",
1177
+ category: "state",
1178
+ timestamp: Date.now(),
1179
+ data: {
1180
+ totalChars
1181
+ }
1182
+ };
1183
+ }
1184
+ function createLLMRetry(data) {
1185
+ return {
1186
+ type: "llm.retry",
1187
+ category: "event",
1188
+ timestamp: Date.now(),
1189
+ data: { ...data }
1190
+ };
1191
+ }
1192
+ function createToolStart(toolName) {
1193
+ return {
1194
+ type: "tool.start",
1195
+ category: "event",
1196
+ timestamp: Date.now(),
1197
+ data: {
1198
+ toolName
1199
+ }
1200
+ };
1201
+ }
1202
+ function createToolComplete(toolName, success, duration) {
1203
+ return {
1204
+ type: "tool.complete",
1205
+ category: "event",
1206
+ timestamp: Date.now(),
1207
+ data: {
1208
+ toolName,
1209
+ success,
1210
+ duration
1211
+ }
1212
+ };
1213
+ }
1214
+ function createToolProgress(data) {
1215
+ return {
1216
+ type: "tool.progress",
1217
+ category: "state",
1218
+ timestamp: Date.now(),
1219
+ data: { ...data }
1220
+ };
1221
+ }
1222
+ function createCallStart() {
1223
+ return {
1224
+ type: "call.start",
1225
+ category: "state",
1226
+ timestamp: Date.now(),
1227
+ data: {}
1228
+ };
1229
+ }
1230
+ function createCallFinish(outcome) {
1231
+ return {
1232
+ type: "call.finish",
1233
+ category: "state",
1234
+ timestamp: Date.now(),
1235
+ data: { ...outcome }
1236
+ };
1237
+ }
1238
+
1239
+ export {
1240
+ getDefaultUDSPath,
1241
+ resolveDebugTransportMode,
1242
+ getClawRuntimeUrl,
1243
+ getDebugCapabilities,
1244
+ DebugHub,
1245
+ runWithNotificationScope,
1246
+ _setNotificationAgent,
1247
+ _clearNotificationAgent,
1248
+ _getCurrentNotificationAgent,
1249
+ emitNotification,
1250
+ createLLMCharCount,
1251
+ createLLMComplete,
1252
+ createLLMRetry,
1253
+ createToolStart,
1254
+ createToolComplete,
1255
+ createToolProgress,
1256
+ createCallStart,
1257
+ createCallFinish
1258
+ };
1259
+ //# sourceMappingURL=chunk-574NG4UB.js.map