@xgjktech/xg_cwork_im 1.17.25 → 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.
- package/README.md +484 -484
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/src/account-connection.d.ts.map +1 -1
- package/dist/src/account-connection.js +32 -18
- package/dist/src/account-connection.js.map +1 -1
- package/dist/src/account-reconciler.d.ts.map +1 -1
- package/dist/src/account-reconciler.js +6 -6
- package/dist/src/account-reconciler.js.map +1 -1
- package/dist/src/active-round-registry.d.ts +29 -0
- package/dist/src/active-round-registry.d.ts.map +1 -0
- package/dist/src/active-round-registry.js +60 -0
- package/dist/src/active-round-registry.js.map +1 -0
- package/dist/src/active-round-registry.test.d.ts +2 -0
- package/dist/src/active-round-registry.test.d.ts.map +1 -0
- package/dist/src/active-round-registry.test.js +62 -0
- package/dist/src/active-round-registry.test.js.map +1 -0
- package/dist/src/agent-context-types.js +2 -2
- package/dist/src/agent-context-types.js.map +1 -1
- package/dist/src/agent-context-types.test.js +9 -0
- package/dist/src/agent-context-types.test.js.map +1 -1
- package/dist/src/build-project-prompt.test.js +18 -18
- package/dist/src/channel-config.d.ts +60 -12
- package/dist/src/channel-config.d.ts.map +1 -1
- package/dist/src/channel-config.js +6 -2
- package/dist/src/channel-config.js.map +1 -1
- package/dist/src/dispatch-mentioned-reply.d.ts +3 -0
- package/dist/src/dispatch-mentioned-reply.d.ts.map +1 -1
- package/dist/src/dispatch-mentioned-reply.js +43 -16
- package/dist/src/dispatch-mentioned-reply.js.map +1 -1
- package/dist/src/gateway-session-queue.d.ts +3 -0
- package/dist/src/gateway-session-queue.d.ts.map +1 -1
- package/dist/src/gateway-session-queue.js +1 -0
- package/dist/src/gateway-session-queue.js.map +1 -1
- package/dist/src/interrupt-round.d.ts +9 -0
- package/dist/src/interrupt-round.d.ts.map +1 -0
- package/dist/src/interrupt-round.js +44 -0
- package/dist/src/interrupt-round.js.map +1 -0
- package/dist/src/interrupt-round.test.d.ts +2 -0
- package/dist/src/interrupt-round.test.d.ts.map +1 -0
- package/dist/src/interrupt-round.test.js +133 -0
- package/dist/src/interrupt-round.test.js.map +1 -0
- package/dist/src/nas-volumes.d.ts.map +1 -1
- package/dist/src/nas-volumes.js +145 -29
- package/dist/src/nas-volumes.js.map +1 -1
- package/dist/src/nas-volumes.test.js +135 -17
- package/dist/src/nas-volumes.test.js.map +1 -1
- package/dist/src/session-im-context.d.ts +1 -0
- package/dist/src/session-im-context.d.ts.map +1 -1
- package/dist/src/session-im-context.js +9 -0
- package/dist/src/session-im-context.js.map +1 -1
- package/dist/src/single-chat-no-reply-fallback.d.ts +14 -0
- package/dist/src/single-chat-no-reply-fallback.d.ts.map +1 -0
- package/dist/src/single-chat-no-reply-fallback.js +20 -0
- package/dist/src/single-chat-no-reply-fallback.js.map +1 -0
- package/dist/src/subagent-monitor.d.ts +16 -10
- package/dist/src/subagent-monitor.d.ts.map +1 -1
- package/dist/src/subagent-monitor.js +210 -329
- package/dist/src/subagent-monitor.js.map +1 -1
- package/dist/src/subagent-progress-hooks.d.ts +11 -0
- package/dist/src/subagent-progress-hooks.d.ts.map +1 -0
- package/dist/src/subagent-progress-hooks.js +41 -0
- package/dist/src/subagent-progress-hooks.js.map +1 -0
- package/dist/src/types.d.ts +9 -4
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,324 +1,205 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
return true;
|
|
61
87
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
230
|
-
if (groupMonitors.size === 0) {
|
|
182
|
+
if (roundMonitors.size === 0) {
|
|
231
183
|
stopTickerIfIdle();
|
|
232
184
|
return;
|
|
233
185
|
}
|
|
234
|
-
for (const
|
|
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
|
|
294
|
-
|
|
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]
|
|
198
|
+
console.error(`[subagent-monitor] poll failed roundKey=${monitor.roundKey}: ${String(err)}`);
|
|
310
199
|
}
|
|
311
200
|
}
|
|
312
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
232
|
+
if (text.length > MSG_TRUNCATE_LEN)
|
|
233
|
+
text = `${text.slice(0, MSG_TRUNCATE_LEN)}…`;
|
|
234
|
+
return `[助手] ${text}${toolCount > 0 ? ` [调用${toolCount}个工具]` : ""}`;
|
|
357
235
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
})
|
|
380
|
-
|
|
381
|
-
|
|
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 +=
|
|
385
|
-
}
|
|
386
|
-
})
|
|
387
|
-
|
|
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
|
|
392
|
-
|
|
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
|
-
|
|
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
|