@xgjktech/xg_cwork_im 1.17.24 → 1.17.26

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 (63) hide show
  1. package/README.md +480 -481
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +8 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/account-connection.d.ts.map +1 -1
  6. package/dist/src/account-connection.js +34 -22
  7. package/dist/src/account-connection.js.map +1 -1
  8. package/dist/src/account-reconciler.d.ts.map +1 -1
  9. package/dist/src/account-reconciler.js +6 -6
  10. package/dist/src/account-reconciler.js.map +1 -1
  11. package/dist/src/active-round-registry.d.ts +29 -0
  12. package/dist/src/active-round-registry.d.ts.map +1 -0
  13. package/dist/src/active-round-registry.js +60 -0
  14. package/dist/src/active-round-registry.js.map +1 -0
  15. package/dist/src/active-round-registry.test.d.ts +2 -0
  16. package/dist/src/active-round-registry.test.d.ts.map +1 -0
  17. package/dist/src/active-round-registry.test.js +62 -0
  18. package/dist/src/active-round-registry.test.js.map +1 -0
  19. package/dist/src/agent-context-types.js +2 -2
  20. package/dist/src/agent-context-types.js.map +1 -1
  21. package/dist/src/agent-context-types.test.js +9 -0
  22. package/dist/src/agent-context-types.test.js.map +1 -1
  23. package/dist/src/channel-config.d.ts +0 -12
  24. package/dist/src/channel-config.d.ts.map +1 -1
  25. package/dist/src/channel-config.js +0 -2
  26. package/dist/src/channel-config.js.map +1 -1
  27. package/dist/src/channel.d.ts +2 -1
  28. package/dist/src/channel.d.ts.map +1 -1
  29. package/dist/src/channel.js +2 -1
  30. package/dist/src/channel.js.map +1 -1
  31. package/dist/src/dispatch-mentioned-reply.d.ts +3 -0
  32. package/dist/src/dispatch-mentioned-reply.d.ts.map +1 -1
  33. package/dist/src/dispatch-mentioned-reply.js +43 -16
  34. package/dist/src/dispatch-mentioned-reply.js.map +1 -1
  35. package/dist/src/inbound-display.d.ts +0 -4
  36. package/dist/src/inbound-display.d.ts.map +1 -1
  37. package/dist/src/inbound-display.js +0 -8
  38. package/dist/src/inbound-display.js.map +1 -1
  39. package/dist/src/interrupt-round.d.ts +9 -0
  40. package/dist/src/interrupt-round.d.ts.map +1 -0
  41. package/dist/src/interrupt-round.js +44 -0
  42. package/dist/src/interrupt-round.js.map +1 -0
  43. package/dist/src/interrupt-round.test.d.ts +2 -0
  44. package/dist/src/interrupt-round.test.d.ts.map +1 -0
  45. package/dist/src/interrupt-round.test.js +133 -0
  46. package/dist/src/interrupt-round.test.js.map +1 -0
  47. package/dist/src/nas-volumes.d.ts.map +1 -1
  48. package/dist/src/nas-volumes.js +77 -7
  49. package/dist/src/nas-volumes.js.map +1 -1
  50. package/dist/src/nas-volumes.test.js +78 -0
  51. package/dist/src/nas-volumes.test.js.map +1 -1
  52. package/dist/src/session-im-context.d.ts +1 -0
  53. package/dist/src/session-im-context.d.ts.map +1 -1
  54. package/dist/src/session-im-context.js +9 -0
  55. package/dist/src/session-im-context.js.map +1 -1
  56. package/dist/src/subagent-monitor.d.ts +16 -10
  57. package/dist/src/subagent-monitor.d.ts.map +1 -1
  58. package/dist/src/subagent-monitor.js +210 -329
  59. package/dist/src/subagent-monitor.js.map +1 -1
  60. package/dist/src/types.d.ts +0 -12
  61. package/dist/src/types.d.ts.map +1 -1
  62. package/dist/src/types.js.map +1 -1
  63. package/package.json +2 -2
@@ -1,324 +1,205 @@
1
- import { getStreamClient } from "./agent-stream-client-registry.js";
2
- // ─── 模块级状态 ─────────────────────────────────────────────────────────
3
- /** childSessionKey { groupId, runId } 反向索引 */
4
- const childToGroup = new Map();
5
- /** groupId → monitor */
6
- const groupMonitors = new Map();
7
- /** groupId → replyToMsgId(dispatch 写入,startMonitor 读取) */
8
- const groupReplyTo = new Map();
9
- /** 延迟的 FINISHED 信息:groupId → { reason, groupId, replyToMsgId } */
1
+ import { completeActiveRound } from "./active-round-registry.js";
2
+ const childToRound = new Map();
3
+ const roundMonitors = new Map();
10
4
  const deferredFinished = new Map();
5
+ const roundAdmissionTombstones = new Map();
6
+ const childTombstones = new Map();
7
+ const POLL_INTERVAL_MS = 15_000;
8
+ const ROUND_TOMBSTONE_TTL_MS = 30_000;
9
+ const CHILD_TOMBSTONE_TTL_MS = 5 * 60_000;
10
+ const MAX_INCR_MSGS = 5;
11
+ const MSG_TRUNCATE_LEN = 40;
11
12
  let tickerTimer = null;
12
13
  let pluginApi = null;
13
- const POLL_INTERVAL_MS = 15000;
14
- console.log(`[subagent-monitor] module loaded, POLL_INTERVAL=${POLL_INTERVAL_MS}ms`);
15
- // ─── 公开 API ─────────────────────────────────────────────────────────
16
14
  export function setPluginApi(api) {
17
15
  pluginApi = api;
18
- console.log(`[subagent-monitor] setPluginApi called, api=${typeof api}`);
19
16
  }
20
- /** hook 调用:立即启动监控,发 HTTP 推送,首次 poll 立即执行 */
17
+ function pruneTombstones(now = Date.now()) {
18
+ for (const [key, expiresAt] of roundAdmissionTombstones) {
19
+ if (expiresAt <= now)
20
+ roundAdmissionTombstones.delete(key);
21
+ }
22
+ for (const [key, expiresAt] of childTombstones) {
23
+ if (expiresAt <= now)
24
+ childTombstones.delete(key);
25
+ }
26
+ }
27
+ function stopTickerIfIdle() {
28
+ if (roundMonitors.size === 0 && tickerTimer) {
29
+ clearInterval(tickerTimer);
30
+ tickerTimer = null;
31
+ }
32
+ }
33
+ function removeMonitor(monitor) {
34
+ if (roundMonitors.get(monitor.roundKey)?.generation === monitor.generation) {
35
+ roundMonitors.delete(monitor.roundKey);
36
+ }
37
+ if (deferredFinished.get(monitor.roundKey)?.generation === monitor.generation) {
38
+ deferredFinished.delete(monitor.roundKey);
39
+ }
40
+ for (const record of monitor.subagents.values()) {
41
+ const indexed = childToRound.get(record.childSessionKey);
42
+ if (indexed?.roundKey === monitor.roundKey && indexed.generation === monitor.generation) {
43
+ childToRound.delete(record.childSessionKey);
44
+ }
45
+ }
46
+ stopTickerIfIdle();
47
+ }
21
48
  export function startMonitor(params) {
22
- const { groupId, agentId, spawn } = params;
23
- console.log(`[subagent-monitor] startMonitor groupId=${groupId} agentId=${agentId} childSessionKey=${spawn.childSessionKey} runId=${spawn.runId}`);
24
- let monitor = groupMonitors.get(groupId);
49
+ pruneTombstones();
50
+ if (roundAdmissionTombstones.has(params.roundKey))
51
+ return false;
52
+ let monitor = roundMonitors.get(params.roundKey);
53
+ if (monitor && monitor.generation !== params.generation) {
54
+ removeMonitor(monitor);
55
+ monitor = undefined;
56
+ }
25
57
  if (!monitor) {
26
58
  monitor = {
27
- groupId,
28
- agentId,
29
- replyToMsgId: groupReplyTo.get(groupId) ?? undefined,
59
+ roundKey: params.roundKey,
60
+ generation: params.generation,
61
+ groupId: params.groupId,
62
+ agentId: params.agentId,
63
+ streamClient: params.streamClient,
64
+ replyToMsgId: params.replyToMsgId,
30
65
  subagents: new Map(),
31
- startedAt: Date.now(),
32
66
  };
33
- groupMonitors.set(groupId, monitor);
34
- console.log(`[subagent-monitor] created new monitor for groupId=${groupId} agentId=${agentId}`);
35
- }
36
- if (monitor.subagents.has(spawn.runId)) {
37
- console.log(`[subagent-monitor] duplicate subagent runId=${spawn.runId}, skipping`);
38
- return;
67
+ roundMonitors.set(params.roundKey, monitor);
39
68
  }
40
- monitor.subagents.set(spawn.runId, {
41
- childSessionKey: spawn.childSessionKey,
42
- runId: spawn.runId,
43
- label: spawn.label,
69
+ if (monitor.subagents.has(params.spawn.runId))
70
+ return false;
71
+ const record = {
72
+ ...params.spawn,
44
73
  lastMsgIndex: 0,
45
74
  completed: false,
46
75
  messages: [],
47
- startedAt: Date.now(),
48
76
  sendFrom: 0,
77
+ };
78
+ monitor.subagents.set(record.runId, record);
79
+ childToRound.set(record.childSessionKey, {
80
+ roundKey: monitor.roundKey,
81
+ generation: monitor.generation,
82
+ runId: record.runId,
49
83
  });
50
- childToGroup.set(spawn.childSessionKey, { groupId, runId: spawn.runId });
51
- console.log(`[subagent-monitor] subagent added, total subagents=${monitor.subagents.size}`);
52
- // 立即发一次空状态,让前端第一时间看到 subagent 已启动
53
- if (monitor.replyToMsgId) {
54
- const client = getStreamClient(monitor.agentId);
55
- if (client) {
56
- sendWsStat(groupId, monitor, client, monitor.replyToMsgId);
57
- }
58
- }
84
+ sendWsStat(monitor, monitor.streamClient);
59
85
  ensureTickerRunning();
60
- console.log(`[subagent-monitor] ensureTickerRunning done, tickerTimer=${tickerTimer !== null}`);
86
+ return true;
61
87
  }
62
- /** dispatch BEGIN 后调用:写入 replyToMsgId */
63
- export function storeReplyToMsgId(groupId, replyToMsgId) {
64
- console.log(`[subagent-monitor] storeReplyToMsgId groupId=${groupId} replyToMsgId=${replyToMsgId}`);
65
- groupReplyTo.set(groupId, replyToMsgId);
66
- const monitor = groupMonitors.get(groupId);
67
- if (monitor) {
68
- monitor.replyToMsgId = replyToMsgId;
69
- console.log(`[subagent-monitor] storeReplyToMsgId: monitor exists, sending stat`);
70
- const client = getStreamClient(monitor.agentId);
71
- if (client) {
72
- sendWsStat(groupId, monitor, client, replyToMsgId);
73
- }
74
- }
75
- else {
76
- console.log(`[subagent-monitor] storeReplyToMsgId: no monitor yet for groupId=${groupId}`);
77
- }
78
- }
79
- /** 检查指定 groupId 是否有活跃的 subagent(用于判断是否延迟 FINISHED) */
80
- export function hasActiveSubagents(groupId) {
81
- const monitor = groupMonitors.get(groupId);
82
- if (!monitor) {
83
- console.log(`[subagent-monitor] hasActiveSubagents: no monitor for groupId=${groupId}`);
84
- return false;
85
- }
86
- const active = Array.from(monitor.subagents.values()).some(r => !r.completed);
87
- console.log(`[subagent-monitor] hasActiveSubagents groupId=${groupId}: ${active} (total=${monitor.subagents.size})`);
88
- return active;
88
+ export function hasActiveSubagents(roundKey, generation) {
89
+ const monitor = roundMonitors.get(roundKey);
90
+ return Boolean(monitor &&
91
+ monitor.generation === generation &&
92
+ Array.from(monitor.subagents.values()).some((record) => !record.completed));
89
93
  }
90
- /** 记录延迟的 FINISHED,等所有 subagent 完成后发送 */
91
- export function deferFinished(groupId, reason, replyToMsgId) {
92
- console.log(`[subagent-monitor] deferFinished groupId=${groupId} reason=${reason}`);
93
- deferredFinished.set(groupId, { reason, groupId, replyToMsgId });
94
+ export function deferFinished(roundKey, generation, reason) {
95
+ deferredFinished.set(roundKey, { reason, generation });
94
96
  }
95
- /** 发送延迟的 FINISHED(在所有 subagent 完成时调用) */
96
- async function sendDeferredFinished(groupId, monitor) {
97
- const deferred = deferredFinished.get(groupId);
98
- if (!deferred) {
99
- console.log(`[subagent-monitor] sendDeferredFinished: no deferred FINISHED for groupId=${groupId}`);
100
- return;
101
- }
102
- const client = getStreamClient(monitor.agentId);
103
- if (!client) {
104
- console.warn(`[subagent-monitor] sendDeferredFinished: no stream client for agentId=${monitor.agentId}`);
105
- deferredFinished.delete(groupId);
106
- return;
107
- }
108
- try {
109
- await client.end(deferred.reason, {
110
- groupId: deferred.groupId,
111
- replyToMsgId: deferred.replyToMsgId,
112
- });
113
- console.log(`[subagent-monitor] sendDeferredFinished: FINISHED sent for groupId=${groupId} reason=${deferred.reason}`);
114
- }
115
- catch (err) {
116
- console.error(`[subagent-monitor] sendDeferredFinished: error sending FINISHED: ${String(err)}`);
117
- }
118
- finally {
119
- deferredFinished.delete(groupId);
120
- }
97
+ export function prepareSubagentInterrupt(roundKey, generation, now = Date.now()) {
98
+ pruneTombstones(now);
99
+ roundAdmissionTombstones.set(roundKey, now + ROUND_TOMBSTONE_TTL_MS);
100
+ const monitor = roundMonitors.get(roundKey);
101
+ if (!monitor || monitor.generation !== generation)
102
+ return [];
103
+ const result = Array.from(monitor.subagents.values()).map((record) => {
104
+ childTombstones.set(record.childSessionKey, now + CHILD_TOMBSTONE_TTL_MS);
105
+ return {
106
+ runId: record.runId,
107
+ label: record.label ?? record.runId.slice(0, 8),
108
+ status: record.completed ? "completed" : "suspended",
109
+ messages: record.messages,
110
+ };
111
+ });
112
+ removeMonitor(monitor);
113
+ return result;
121
114
  }
122
- /** subagent_ended hook 调用 */
123
115
  export function markSubagentCompleted(childSessionKey, runId) {
124
- console.log(`[subagent-monitor] markSubagentCompleted childSessionKey=${childSessionKey} runId=${runId}`);
125
- const entry = childToGroup.get(childSessionKey);
126
- if (!entry) {
127
- console.log(`[subagent-monitor] markSubagentCompleted: childSessionKey not found in childToGroup`);
116
+ pruneTombstones();
117
+ if (childTombstones.has(childSessionKey))
128
118
  return;
129
- }
130
- const monitor = groupMonitors.get(entry.groupId);
131
- if (!monitor) {
132
- console.log(`[subagent-monitor] markSubagentCompleted: monitor not found for groupId=${entry.groupId}`);
119
+ const entry = childToRound.get(childSessionKey);
120
+ if (!entry || entry.runId !== runId)
121
+ return;
122
+ const monitor = roundMonitors.get(entry.roundKey);
123
+ if (!monitor || monitor.generation !== entry.generation)
133
124
  return;
134
- }
135
125
  const record = monitor.subagents.get(runId);
136
- if (!record) {
137
- console.log(`[subagent-monitor] markSubagentCompleted: runId not found in monitor`);
126
+ if (!record)
138
127
  return;
139
- }
140
- // 立即读取最新消息(补全 tick 间隔中可能遗漏的内容)
141
- const readAndCheckCompletion = async () => {
142
- if (pluginApi) {
143
- try {
144
- await readSubagentMessages(record);
145
- }
146
- catch (err) {
147
- console.error(`[subagent-monitor] markSubagentCompleted: read error: ${String(err)}`);
148
- }
128
+ void (async () => {
129
+ try {
130
+ await readSubagentMessages(record);
131
+ }
132
+ catch (err) {
133
+ console.error(`[subagent-monitor] completion read failed: ${String(err)}`);
149
134
  }
150
135
  record.completed = true;
151
- console.log(`[subagent-monitor] markSubagentCompleted: marked completed for runId=${runId}`);
152
- // ✅ 检查是否所有 subagent 都完成了
153
- const allCompleted = Array.from(monitor.subagents.values()).every(r => r.completed);
154
- if (allCompleted) {
155
- console.log(`[subagent-monitor] markSubagentCompleted: all subagents completed for groupId=${entry.groupId}`);
156
- // 发送最终 SUBAGENT_STAT
157
- const client = getStreamClient(monitor.agentId);
158
- if (client && monitor.replyToMsgId) {
159
- try {
160
- const subagentList = Array.from(monitor.subagents.entries()).map(([rid, rec]) => ({
161
- runId: rid,
162
- label: rec.label ?? rid.slice(0, 8),
163
- status: rec.completed ? "completed" : "running",
164
- messages: rec.messages.slice(rec.sendFrom),
165
- }));
166
- await client.sendEvent({
167
- groupId: entry.groupId,
168
- replyToMsgId: monitor.replyToMsgId,
169
- event: "SUBAGENT_STAT",
170
- data: { subagentList },
171
- });
172
- console.log(`[subagent-monitor] markSubagentCompleted: final SUBAGENT_STAT sent`);
173
- }
174
- catch (err) {
175
- console.error(`[subagent-monitor] markSubagentCompleted: sendEvent error: ${String(err)}`);
176
- }
177
- }
178
- // ✅ 发送延迟的 FINISHED(如果有的话)
179
- await sendDeferredFinished(entry.groupId, monitor);
180
- // 清理资源
181
- console.log(`[subagent-monitor] markSubagentCompleted: cleaning up groupId=${entry.groupId}`);
182
- for (const [childKey] of childToGroup) {
183
- if (childToGroup.get(childKey)?.groupId === entry.groupId) {
184
- childToGroup.delete(childKey);
185
- }
186
- }
187
- groupReplyTo.delete(entry.groupId);
188
- groupMonitors.delete(entry.groupId);
189
- stopTickerIfIdle();
136
+ await settleMonitorIfComplete(monitor);
137
+ })();
138
+ }
139
+ async function settleMonitorIfComplete(monitor) {
140
+ if (roundMonitors.get(monitor.roundKey) !== monitor)
141
+ return;
142
+ if (!Array.from(monitor.subagents.values()).every((record) => record.completed))
143
+ return;
144
+ const deferred = deferredFinished.get(monitor.roundKey);
145
+ const shouldCompleteRound = deferred?.generation === monitor.generation;
146
+ try {
147
+ await sendFinalStat(monitor, monitor.streamClient);
148
+ if (shouldCompleteRound && deferred) {
149
+ await monitor.streamClient.end(deferred.reason, {
150
+ groupId: monitor.groupId,
151
+ replyToMsgId: monitor.replyToMsgId,
152
+ });
190
153
  }
191
- };
192
- // 异步执行,不阻塞
193
- readAndCheckCompletion().catch((err) => {
194
- console.error(`[subagent-monitor] markSubagentCompleted: async error: ${String(err)}`);
195
- });
154
+ }
155
+ catch (err) {
156
+ console.error(`[subagent-monitor] final state send failed: ${String(err)}`);
157
+ }
158
+ removeMonitor(monitor);
159
+ if (shouldCompleteRound) {
160
+ completeActiveRound(monitor.roundKey, monitor.generation);
161
+ }
196
162
  }
197
163
  async function readSubagentMessages(record) {
164
+ if (!pluginApi)
165
+ return;
198
166
  const result = await pluginApi.runtime.subagent.getSessionMessages({
199
167
  sessionKey: record.childSessionKey,
200
168
  });
201
169
  const messages = Array.isArray(result.messages) ? result.messages : [];
202
- if (messages.length > record.lastMsgIndex) {
203
- for (const msg of messages.slice(record.lastMsgIndex)) {
204
- record.messages.push(cleanMsg(msg));
205
- }
206
- record.lastMsgIndex = messages.length;
207
- console.log(`[subagent-monitor] readSubagentMessages: read ${messages.length} messages`);
208
- }
170
+ for (const msg of messages.slice(record.lastMsgIndex))
171
+ record.messages.push(cleanMsg(msg));
172
+ record.lastMsgIndex = messages.length;
209
173
  }
210
- // ─── 内部 ticker ───────────────────────────────────────────────────────
211
174
  function ensureTickerRunning() {
212
- console.log(`[subagent-monitor] ensureTickerRunning tickerTimer=${tickerTimer !== null}`);
213
175
  if (tickerTimer)
214
176
  return;
215
- tick();
216
- tickerTimer = setInterval(tick, POLL_INTERVAL_MS);
177
+ void tick();
178
+ tickerTimer = setInterval(() => void tick(), POLL_INTERVAL_MS);
217
179
  tickerTimer.unref?.();
218
- console.log(`[subagent-monitor] ensureTickerRunning: ticker started, interval=${POLL_INTERVAL_MS}ms`);
219
- }
220
- function stopTickerIfIdle() {
221
- console.log(`[subagent-monitor] stopTickerIfIdle monitors=${groupMonitors.size} tickerTimer=${tickerTimer !== null}`);
222
- if (groupMonitors.size === 0 && tickerTimer) {
223
- clearInterval(tickerTimer);
224
- tickerTimer = null;
225
- console.log(`[subagent-monitor] stopTickerIfIdle: ticker stopped`);
226
- }
227
180
  }
228
181
  async function tick() {
229
- console.log(`[subagent-monitor] tick start monitors=${groupMonitors.size}`);
230
- if (groupMonitors.size === 0) {
182
+ if (roundMonitors.size === 0) {
231
183
  stopTickerIfIdle();
232
184
  return;
233
185
  }
234
- for (const [groupId, monitor] of groupMonitors) {
235
- console.log(`[subagent-monitor] tick processing groupId=${groupId}`);
236
- try {
237
- await pollMonitor(monitor);
238
- }
239
- catch (err) {
240
- console.error(`[subagent-monitor] poll error groupId=${groupId}: ${String(err)}`);
241
- }
242
- if (Array.from(monitor.subagents.values()).every(r => r.completed)) {
243
- const client = monitor.replyToMsgId ? getStreamClient(monitor.agentId) : undefined;
244
- if (client && monitor.replyToMsgId) {
245
- try {
246
- const subagentList = Array.from(monitor.subagents.entries()).map(([runId, record]) => ({
247
- runId,
248
- label: record.label ?? runId.slice(0, 8),
249
- status: record.completed ? "completed" : "running",
250
- messages: record.messages,
251
- }));
252
- await client.sendEvent({
253
- groupId,
254
- replyToMsgId: monitor.replyToMsgId,
255
- event: "SUBAGENT_STAT",
256
- data: { subagentList },
257
- });
258
- console.log(`[subagent-monitor] final SUBAGENT_STAT sent for groupId=${groupId}`);
259
- // ✅ 发送延迟的 FINISHED
260
- await sendDeferredFinished(groupId, monitor);
261
- }
262
- catch {
263
- console.log(`[subagent-monitor] WS unavailable, defer cleanup for groupId=${groupId}`);
264
- continue;
265
- }
266
- }
267
- console.log(`[subagent-monitor] all completed, cleaning up groupId=${groupId}`);
268
- for (const [childKey] of childToGroup) {
269
- if (childToGroup.get(childKey)?.groupId === groupId)
270
- childToGroup.delete(childKey);
271
- }
272
- groupReplyTo.delete(groupId);
273
- groupMonitors.delete(groupId);
274
- }
275
- }
276
- stopTickerIfIdle();
277
- console.log(`[subagent-monitor] tick end monitors=${groupMonitors.size}`);
278
- }
279
- async function pollMonitor(monitor) {
280
- if (!pluginApi) {
281
- console.log(`[subagent-monitor] pollMonitor: pluginApi not set, skipping`);
282
- return;
283
- }
284
- console.log(`[subagent-monitor] pollMonitor start groupId=${monitor.groupId} subagents=${monitor.subagents.size}`);
285
- let hasNewData = false;
286
- for (const [runId, record] of monitor.subagents) {
287
- if (record.completed) {
288
- console.log(`[subagent-monitor] pollMonitor runId=${runId}: already completed, skip`);
289
- continue;
290
- }
291
- console.log(`[subagent-monitor] pollMonitor runId=${runId}: polling childSessionKey=${record.childSessionKey}`);
186
+ for (const monitor of Array.from(roundMonitors.values())) {
292
187
  try {
293
- const result = await pluginApi.runtime.subagent.getSessionMessages({
294
- sessionKey: record.childSessionKey,
295
- });
296
- const messages = Array.isArray(result.messages) ? result.messages : [];
297
- console.log(`[subagent-monitor] pollMonitor runId=${runId}: messages=${messages.length} lastMsgIndex=${record.lastMsgIndex}`);
298
- if (messages.length > record.lastMsgIndex) {
299
- const newMsgs = messages.slice(record.lastMsgIndex);
300
- console.log(`[subagent-monitor] pollMonitor runId=${runId}: new messages=${newMsgs.length}`);
301
- for (const msg of newMsgs) {
302
- record.messages.push(cleanMsg(msg));
303
- hasNewData = true;
304
- }
305
- record.lastMsgIndex = messages.length;
188
+ for (const record of monitor.subagents.values()) {
189
+ if (!record.completed)
190
+ await readSubagentMessages(record);
306
191
  }
192
+ if (roundMonitors.get(monitor.roundKey) !== monitor)
193
+ continue;
194
+ sendWsStat(monitor, monitor.streamClient);
195
+ await settleMonitorIfComplete(monitor);
307
196
  }
308
197
  catch (err) {
309
- console.error(`[subagent-monitor] pollMonitor runId=${runId}: getSessionMessages error: ${String(err)}`);
198
+ console.error(`[subagent-monitor] poll failed roundKey=${monitor.roundKey}: ${String(err)}`);
310
199
  }
311
200
  }
312
- // 只要 monitor 有活跃 subagent,就发 SUBAGENT_STAT
313
- if (monitor.replyToMsgId) {
314
- const client = getStreamClient(monitor.agentId);
315
- if (client) {
316
- sendWsStat(monitor.groupId, monitor, client, monitor.replyToMsgId);
317
- }
318
- }
319
- console.log(`[subagent-monitor] pollMonitor end`);
201
+ stopTickerIfIdle();
320
202
  }
321
- const MSG_TRUNCATE_LEN = 40;
322
203
  function cleanMsg(msg) {
323
204
  if (!msg || typeof msg !== "object")
324
205
  return "";
@@ -326,79 +207,79 @@ function cleanMsg(msg) {
326
207
  const role = typeof src.role === "string" ? src.role : "";
327
208
  if (role === "user")
328
209
  return "";
329
- // toolResult → [工具] 工具名 已调用完成
330
210
  if (role === "toolResult" || role === "tool" || typeof src.toolCallId === "string") {
331
211
  const toolName = typeof src.toolName === "string" && src.toolName.trim() ? src.toolName.trim() : "工具";
332
212
  return `[工具] ${toolName} 已调用`;
333
213
  }
334
- // assistant [助手] 文本内容 + [调用X个工具]
335
- if (role === "assistant") {
336
- const content = Array.isArray(src.content) ? src.content : typeof src.content === "string" ? [{ type: "text", text: src.content }] : [];
337
- let text = "";
338
- let toolCount = 0;
339
- for (const part of content) {
340
- if (!part || typeof part !== "object")
341
- continue;
342
- const p = part;
343
- if (p.type === "text" && typeof p.text === "string") {
344
- text += p.text;
345
- }
346
- else if (p.type === "toolCall" || p.type === "tool_use") {
347
- toolCount++;
348
- }
349
- }
350
- text = text.length > MSG_TRUNCATE_LEN ? text.slice(0, MSG_TRUNCATE_LEN) + "…" : text;
351
- let result = `[助手] ${text}`;
352
- if (toolCount > 0)
353
- result += ` [调用${toolCount}个工具]`;
354
- return result;
214
+ if (role !== "assistant")
215
+ return "";
216
+ const content = Array.isArray(src.content)
217
+ ? src.content
218
+ : typeof src.content === "string"
219
+ ? [{ type: "text", text: src.content }]
220
+ : [];
221
+ let text = "";
222
+ let toolCount = 0;
223
+ for (const part of content) {
224
+ if (!part || typeof part !== "object")
225
+ continue;
226
+ const value = part;
227
+ if (value.type === "text" && typeof value.text === "string")
228
+ text += value.text;
229
+ else if (value.type === "toolCall" || value.type === "tool_use")
230
+ toolCount += 1;
355
231
  }
356
- return "";
232
+ if (text.length > MSG_TRUNCATE_LEN)
233
+ text = `${text.slice(0, MSG_TRUNCATE_LEN)}…`;
234
+ return `[助手] ${text}${toolCount > 0 ? ` [调用${toolCount}个工具]` : ""}`;
357
235
  }
358
- const MAX_INCR_MSGS = 5;
359
- function sendWsStat(groupId, monitor, client, replyToMsgId) {
360
- // 构建增量消息列表,同时记录每个 subagent 发了几条
361
- const sentCounts = new Map();
362
- const subagentList = Array.from(monitor.subagents.entries()).map(([runId, record]) => {
363
- const newMsgs = record.messages.length > record.sendFrom
236
+ function toSubagentList(monitor, incremental) {
237
+ return Array.from(monitor.subagents.values()).map((record) => ({
238
+ runId: record.runId,
239
+ label: record.label ?? record.runId.slice(0, 8),
240
+ status: record.completed ? "completed" : "running",
241
+ messages: incremental
364
242
  ? record.messages.slice(record.sendFrom, record.sendFrom + MAX_INCR_MSGS)
365
- : [];
366
- sentCounts.set(runId, newMsgs.length);
367
- return {
368
- runId,
369
- label: record.label ?? runId.slice(0, 8),
370
- status: record.completed ? "completed" : "running",
371
- messages: newMsgs,
372
- };
373
- });
374
- client.sendEvent({
375
- groupId,
376
- replyToMsgId,
243
+ : record.messages,
244
+ }));
245
+ }
246
+ function sendWsStat(monitor, client) {
247
+ const subagentList = toSubagentList(monitor, true);
248
+ client
249
+ .sendEvent({
250
+ groupId: monitor.groupId,
251
+ replyToMsgId: monitor.replyToMsgId,
377
252
  event: "SUBAGENT_STAT",
378
253
  data: { subagentList },
379
- }).then(() => {
380
- // 发送成功后推进游标
381
- for (const [runId, count] of sentCounts) {
382
- const record = monitor.subagents.get(runId);
254
+ })
255
+ .then(() => {
256
+ subagentList.forEach((item) => {
257
+ const record = monitor.subagents.get(item.runId);
383
258
  if (record)
384
- record.sendFrom += count;
385
- }
386
- }).catch((err) => {
387
- console.error(`[subagent-monitor] WS SUBAGENT_STAT failed: ${String(err)}`);
388
- // 不推进游标,下次重试时消息不会丢失
259
+ record.sendFrom += item.messages.length;
260
+ });
261
+ })
262
+ .catch((err) => {
263
+ console.error(`[subagent-monitor] SUBAGENT_STAT failed: ${String(err)}`);
389
264
  });
390
265
  }
391
- function buildPayload(monitor) {
392
- const subagents = Array.from(monitor.subagents.entries()).map(([runId, record]) => ({
393
- runId,
394
- label: record.label ?? runId.slice(0, 8),
395
- status: record.completed ? "completed" : "running",
396
- messages: record.messages,
397
- }));
398
- return {
399
- ts: Date.now(),
266
+ async function sendFinalStat(monitor, client) {
267
+ await client.sendEvent({
400
268
  groupId: monitor.groupId,
401
- subagents,
402
- };
269
+ replyToMsgId: monitor.replyToMsgId,
270
+ event: "SUBAGENT_STAT",
271
+ data: { subagentList: toSubagentList(monitor, false) },
272
+ });
273
+ }
274
+ export function __resetSubagentMonitorForTests() {
275
+ if (tickerTimer)
276
+ clearInterval(tickerTimer);
277
+ tickerTimer = null;
278
+ pluginApi = null;
279
+ childToRound.clear();
280
+ roundMonitors.clear();
281
+ deferredFinished.clear();
282
+ roundAdmissionTombstones.clear();
283
+ childTombstones.clear();
403
284
  }
404
285
  //# sourceMappingURL=subagent-monitor.js.map