@cuylabs/agent-core 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GFTW23FV.js → chunk-BGG2HVIR.js} +4 -2
- package/dist/{chunk-TOTDGK3P.js → chunk-CSR75JVC.js} +4 -5
- package/dist/{chunk-ICZ66572.js → chunk-CZ5XOVDV.js} +40 -9
- package/dist/{chunk-2O4MCSQS.js → chunk-FYC33XFI.js} +31 -10
- package/dist/{chunk-WBPOZ7CL.js → chunk-HNEI7JVE.js} +114 -38
- package/dist/{chunk-SPILYYDF.js → chunk-I7EJGKUP.js} +79 -7
- package/dist/{chunk-SSFBF3US.js → chunk-JPBFAQNS.js} +7 -12
- package/dist/{chunk-5FMSGQVX.js → chunk-JZRLCTSD.js} +8 -2
- package/dist/{chunk-V4RFNEET.js → chunk-MLTJHUVG.js} +34 -16
- package/dist/{chunk-QAL3OMI3.js → chunk-MO3N6M32.js} +4 -1
- package/dist/{chunk-CMYN2RCB.js → chunk-NWQUZWLT.js} +13 -7
- package/dist/{chunk-MXAP4UG6.js → chunk-QJV5XPPS.js} +238 -86
- package/dist/{chunk-T4UIX5D7.js → chunk-S6AKEPAX.js} +9 -3
- package/dist/{chunk-N3VX7FEE.js → chunk-STDJYXYK.js} +1 -4
- package/dist/{chunk-6HZBHFOL.js → chunk-TPZ37IWI.js} +10 -1
- package/dist/{chunk-NDZWXCBZ.js → chunk-TZ4VA4VX.js} +33 -11
- package/dist/chunk-US7S4FYW.js +610 -0
- package/dist/{chunk-RN6WZEUF.js → chunk-WI5JFEAI.js} +71 -36
- package/dist/{chunk-5NVVNXPQ.js → chunk-ZKEC7MFQ.js} +5 -30
- package/dist/dispatch/index.d.ts +5 -3
- package/dist/dispatch/index.js +3 -3
- package/dist/execution/index.d.ts +6 -4
- package/dist/execution/index.js +7 -7
- package/dist/index.d.ts +8 -5
- package/dist/index.js +119 -116
- package/dist/inference/errors/index.js +1 -1
- package/dist/inference/index.d.ts +6 -4
- package/dist/inference/index.js +6 -6
- package/dist/{instance-DzPiv6EK.d.ts → instance-N5VhcNT2.d.ts} +45 -7
- package/dist/logger/index.js +1 -1
- package/dist/mcp/index.d.ts +116 -3
- package/dist/mcp/index.js +6 -2
- package/dist/middleware/index.d.ts +5 -3
- package/dist/middleware/index.js +3 -3
- package/dist/{model-messages-CJfwfzGe.d.ts → model-messages-DnsiSiZj.d.ts} +1 -1
- package/dist/models/index.js +2 -2
- package/dist/models/reasoning/index.js +2 -2
- package/dist/plugin/index.d.ts +4 -2
- package/dist/plugin/index.js +1 -1
- package/dist/profiles/index.d.ts +4 -2
- package/dist/profiles/index.js +1 -1
- package/dist/prompt/index.d.ts +5 -3
- package/dist/prompt/index.js +2 -2
- package/dist/safety/index.d.ts +5 -3
- package/dist/safety/index.js +1 -1
- package/dist/skill/index.d.ts +5 -3
- package/dist/skill/index.js +2 -2
- package/dist/storage/index.d.ts +5 -3
- package/dist/storage/index.js +1 -1
- package/dist/subagents/index.d.ts +4 -2
- package/dist/subagents/index.js +4 -4
- package/dist/team/index.d.ts +5 -3
- package/dist/team/index.js +1 -1
- package/dist/tool/index.d.ts +5 -3
- package/dist/tool/index.js +2 -2
- package/dist/{types-Bj_J8u_W.d.ts → types-DMjoFKKv.d.ts} +55 -7
- package/package.json +1 -6
- package/dist/chunk-ROTGCYDW.js +0 -221
|
@@ -40,8 +40,11 @@ function requireNonEmpty(value, label) {
|
|
|
40
40
|
}
|
|
41
41
|
function normalizeDeps(taskId, deps) {
|
|
42
42
|
if (!deps || deps.length === 0) return void 0;
|
|
43
|
-
const unique = [
|
|
44
|
-
|
|
43
|
+
const unique = [
|
|
44
|
+
...new Set(deps.map((d) => requireNonEmpty(d, "dependency ID")))
|
|
45
|
+
];
|
|
46
|
+
if (unique.includes(taskId))
|
|
47
|
+
throw new Error(`Task "${taskId}" cannot depend on itself`);
|
|
45
48
|
return unique;
|
|
46
49
|
}
|
|
47
50
|
function isTerminal(status) {
|
|
@@ -205,10 +208,14 @@ var TaskBoard = class {
|
|
|
205
208
|
const id = requireNonEmpty(memberId, "member ID");
|
|
206
209
|
const task = await this.requireTask(taskId);
|
|
207
210
|
if (task.status !== "pending") {
|
|
208
|
-
throw new Error(
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Task "${taskId}" is not claimable (status: ${task.status})`
|
|
213
|
+
);
|
|
209
214
|
}
|
|
210
215
|
if (task.memberId && task.memberId !== id) {
|
|
211
|
-
throw new Error(
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Task "${taskId}" is assigned to ${task.memberId}, not ${id}`
|
|
218
|
+
);
|
|
212
219
|
}
|
|
213
220
|
const ts = now();
|
|
214
221
|
return this.store.putTask({
|
|
@@ -238,7 +245,9 @@ var TaskBoard = class {
|
|
|
238
245
|
return this.retryOnConflict(async () => {
|
|
239
246
|
const task = await this.requireTask(taskId);
|
|
240
247
|
if (task.status !== "claimed") {
|
|
241
|
-
throw new Error(
|
|
248
|
+
throw new Error(
|
|
249
|
+
`Task "${taskId}" must be claimed before starting (status: ${task.status})`
|
|
250
|
+
);
|
|
242
251
|
}
|
|
243
252
|
return this.store.putTask({
|
|
244
253
|
...task,
|
|
@@ -266,7 +275,10 @@ var TaskBoard = class {
|
|
|
266
275
|
const cascaded = await this.refreshDependents();
|
|
267
276
|
return {
|
|
268
277
|
task: stored,
|
|
269
|
-
transitions: [
|
|
278
|
+
transitions: [
|
|
279
|
+
{ task: stored, reason: "completed", previous: task.status },
|
|
280
|
+
...cascaded
|
|
281
|
+
]
|
|
270
282
|
};
|
|
271
283
|
});
|
|
272
284
|
}
|
|
@@ -288,7 +300,10 @@ var TaskBoard = class {
|
|
|
288
300
|
const cascaded = await this.refreshDependents();
|
|
289
301
|
return {
|
|
290
302
|
task: stored,
|
|
291
|
-
transitions: [
|
|
303
|
+
transitions: [
|
|
304
|
+
{ task: stored, reason: "failed", previous: task.status },
|
|
305
|
+
...cascaded
|
|
306
|
+
]
|
|
292
307
|
};
|
|
293
308
|
});
|
|
294
309
|
}
|
|
@@ -302,7 +317,9 @@ var TaskBoard = class {
|
|
|
302
317
|
return this.retryOnConflict(async () => {
|
|
303
318
|
const task = await this.requireTask(taskId);
|
|
304
319
|
if (isTerminal(task.status)) {
|
|
305
|
-
throw new Error(
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Task "${taskId}" is already terminal (status: ${task.status})`
|
|
322
|
+
);
|
|
306
323
|
}
|
|
307
324
|
const aborted = {
|
|
308
325
|
...task,
|
|
@@ -314,7 +331,9 @@ var TaskBoard = class {
|
|
|
314
331
|
const stored = await this.store.putTask(aborted);
|
|
315
332
|
return {
|
|
316
333
|
task: stored,
|
|
317
|
-
transitions: [
|
|
334
|
+
transitions: [
|
|
335
|
+
{ task: stored, reason: "aborted", previous: task.status }
|
|
336
|
+
]
|
|
318
337
|
};
|
|
319
338
|
});
|
|
320
339
|
}
|
|
@@ -326,7 +345,9 @@ var TaskBoard = class {
|
|
|
326
345
|
return this.retryOnConflict(async () => {
|
|
327
346
|
const task = await this.requireTask(taskId);
|
|
328
347
|
if (isTerminal(task.status)) {
|
|
329
|
-
throw new Error(
|
|
348
|
+
throw new Error(
|
|
349
|
+
`Task "${taskId}" is already terminal (status: ${task.status})`
|
|
350
|
+
);
|
|
330
351
|
}
|
|
331
352
|
const cancelled = {
|
|
332
353
|
...task,
|
|
@@ -339,7 +360,10 @@ var TaskBoard = class {
|
|
|
339
360
|
const cascaded = await this.refreshDependents();
|
|
340
361
|
return {
|
|
341
362
|
task: stored,
|
|
342
|
-
transitions: [
|
|
363
|
+
transitions: [
|
|
364
|
+
{ task: stored, reason: "cancelled", previous: task.status },
|
|
365
|
+
...cascaded
|
|
366
|
+
]
|
|
343
367
|
};
|
|
344
368
|
});
|
|
345
369
|
}
|
|
@@ -502,7 +526,9 @@ function matchesRule(rule, memberId, toolName, args, ctx) {
|
|
|
502
526
|
if (rule.members && !rule.members.includes(memberId)) return false;
|
|
503
527
|
if (!rule.pattern) return true;
|
|
504
528
|
const patterns = ctx.permissionPatterns ? ctx.permissionPatterns(args) : extractApprovalPatterns(toolName, args);
|
|
505
|
-
return patterns.some(
|
|
529
|
+
return patterns.some(
|
|
530
|
+
(pattern) => matchApprovalPattern(rule.pattern, pattern)
|
|
531
|
+
);
|
|
506
532
|
}
|
|
507
533
|
function teamPermissionPolicy(config) {
|
|
508
534
|
const rules = config.rules ?? [];
|
|
@@ -549,7 +575,9 @@ async function collectPendingMessages(ctx, runtime) {
|
|
|
549
575
|
teamId: ctx.teamId,
|
|
550
576
|
kind: ["direct", "broadcast"]
|
|
551
577
|
});
|
|
552
|
-
const relevant = messages.filter(
|
|
578
|
+
const relevant = messages.filter(
|
|
579
|
+
(message) => isQueuedMessageForMember(message, runtime.member.id)
|
|
580
|
+
);
|
|
553
581
|
if (!runtime.lastDeliveredMessageAt || !runtime.lastDeliveredMessageId) {
|
|
554
582
|
return relevant;
|
|
555
583
|
}
|
|
@@ -579,7 +607,9 @@ async function claimAndExecute(ctx, runtime) {
|
|
|
579
607
|
const claimed = await ctx.taskBoard.claimNextTask(runtime.member.id);
|
|
580
608
|
if (!claimed) return void 0;
|
|
581
609
|
await ctx.updateMemberStatus(runtime.member.id, "busy", claimed.id);
|
|
582
|
-
await ctx.emitTransitions([
|
|
610
|
+
await ctx.emitTransitions([
|
|
611
|
+
{ task: claimed, reason: "claimed", previous: "pending" }
|
|
612
|
+
]);
|
|
583
613
|
const started = await ctx.taskBoard.startTask(claimed.id, ctx.createId());
|
|
584
614
|
await ctx.emit({
|
|
585
615
|
type: "team-task-transition",
|
|
@@ -598,14 +628,20 @@ async function claimAndExecute(ctx, runtime) {
|
|
|
598
628
|
const prompt = buildTaskPrompt(runtime.role, started, pendingMessages);
|
|
599
629
|
runtime.taskAbort = new AbortController();
|
|
600
630
|
runtime.taskStartedAt = Date.now();
|
|
601
|
-
runtime.activeRun = executeTask(ctx, runtime, started, prompt).catch(
|
|
602
|
-
|
|
603
|
-
|
|
631
|
+
runtime.activeRun = executeTask(ctx, runtime, started, prompt).catch(
|
|
632
|
+
(err) => {
|
|
633
|
+
ctx.log?.(
|
|
634
|
+
`[${runtime.member.id}] executeTask unhandled error: ${err instanceof Error ? err.message : String(err)}`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
);
|
|
604
638
|
return started;
|
|
605
639
|
}
|
|
606
640
|
async function prepareTaskForExternalExecution(ctx, runtime, taskId, runId) {
|
|
607
641
|
if (ctx.taskDispatchMode !== "external") {
|
|
608
|
-
throw new Error(
|
|
642
|
+
throw new Error(
|
|
643
|
+
'External task preparation requires taskDispatchMode="external"'
|
|
644
|
+
);
|
|
609
645
|
}
|
|
610
646
|
if (runtime.member.status !== "idle") return void 0;
|
|
611
647
|
const task = await ctx.taskBoard.getTask(taskId);
|
|
@@ -614,7 +650,9 @@ async function prepareTaskForExternalExecution(ctx, runtime, taskId, runId) {
|
|
|
614
650
|
}
|
|
615
651
|
const claimed = await ctx.taskBoard.claimTask(taskId, runtime.member.id);
|
|
616
652
|
await ctx.updateMemberStatus(runtime.member.id, "busy", claimed.id);
|
|
617
|
-
await ctx.emitTransitions([
|
|
653
|
+
await ctx.emitTransitions([
|
|
654
|
+
{ task: claimed, reason: "claimed", previous: "pending" }
|
|
655
|
+
]);
|
|
618
656
|
const started = await ctx.taskBoard.startTask(claimed.id, runId);
|
|
619
657
|
await ctx.emit({
|
|
620
658
|
type: "team-task-transition",
|
|
@@ -674,13 +712,17 @@ async function executeTask(ctx, runtime, task, prompt) {
|
|
|
674
712
|
}
|
|
675
713
|
await failPreparedTask(ctx, runtime, task.id, error);
|
|
676
714
|
} catch (inner) {
|
|
677
|
-
ctx.log?.(
|
|
715
|
+
ctx.log?.(
|
|
716
|
+
`[${runtime.member.id}] task finalization error: ${inner instanceof Error ? inner.message : String(inner)}`
|
|
717
|
+
);
|
|
678
718
|
}
|
|
679
719
|
} finally {
|
|
680
720
|
try {
|
|
681
721
|
await finalizePreparedTask(ctx, runtime, true);
|
|
682
722
|
} catch (cleanupErr) {
|
|
683
|
-
ctx.log?.(
|
|
723
|
+
ctx.log?.(
|
|
724
|
+
`[${runtime.member.id}] finalizePreparedTask error: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
|
|
725
|
+
);
|
|
684
726
|
runtime.activeRun = void 0;
|
|
685
727
|
runtime.taskAbort = void 0;
|
|
686
728
|
runtime.taskStartedAt = void 0;
|
|
@@ -704,7 +746,10 @@ async function completePreparedTask(ctx, runtime, taskId, taskResult) {
|
|
|
704
746
|
totalTokens: (runtime.stats.totalTokens.totalTokens ?? 0) + (taskResult.usage.totalTokens ?? 0)
|
|
705
747
|
};
|
|
706
748
|
}
|
|
707
|
-
const { task, transitions } = await ctx.taskBoard.completeTask(
|
|
749
|
+
const { task, transitions } = await ctx.taskBoard.completeTask(
|
|
750
|
+
taskId,
|
|
751
|
+
taskResult
|
|
752
|
+
);
|
|
708
753
|
await ctx.emit({
|
|
709
754
|
type: "team-task-completed",
|
|
710
755
|
teamId: ctx.teamId,
|
|
@@ -745,7 +790,9 @@ async function abortPreparedTask(ctx, runtime, taskId, reason) {
|
|
|
745
790
|
return { task, transitions };
|
|
746
791
|
}
|
|
747
792
|
function buildTaskPrompt(role, task, pendingMessages = []) {
|
|
748
|
-
const queuedMessages = pendingMessages.length > 0 ? "\n\nTeam messages to consider before you start:\n" + pendingMessages.map(
|
|
793
|
+
const queuedMessages = pendingMessages.length > 0 ? "\n\nTeam messages to consider before you start:\n" + pendingMessages.map(
|
|
794
|
+
(message) => `- [${message.kind}] ${message.from}: ${message.body}`
|
|
795
|
+
).join("\n") : "";
|
|
749
796
|
return `You are the "${role.name}" specialist.
|
|
750
797
|
|
|
751
798
|
Role: ${role.description}
|
|
@@ -796,7 +843,9 @@ async function runWorkLoop(ctx, runtime) {
|
|
|
796
843
|
continue;
|
|
797
844
|
}
|
|
798
845
|
} catch (err) {
|
|
799
|
-
ctx.log?.(
|
|
846
|
+
ctx.log?.(
|
|
847
|
+
`[${runtime.member.id}] beforeIteration error: ${err instanceof Error ? err.message : String(err)}`
|
|
848
|
+
);
|
|
800
849
|
await sleep(ctx.pollIntervalMs);
|
|
801
850
|
continue;
|
|
802
851
|
}
|
|
@@ -807,7 +856,9 @@ async function runWorkLoop(ctx, runtime) {
|
|
|
807
856
|
try {
|
|
808
857
|
await runtime.activeRun;
|
|
809
858
|
} catch (err) {
|
|
810
|
-
ctx.log?.(
|
|
859
|
+
ctx.log?.(
|
|
860
|
+
`[${runtime.member.id}] work-loop: task error \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
861
|
+
);
|
|
811
862
|
}
|
|
812
863
|
}
|
|
813
864
|
continue;
|
|
@@ -817,7 +868,9 @@ async function runWorkLoop(ctx, runtime) {
|
|
|
817
868
|
try {
|
|
818
869
|
await runtime.activeRun;
|
|
819
870
|
} catch (err) {
|
|
820
|
-
ctx.log?.(
|
|
871
|
+
ctx.log?.(
|
|
872
|
+
`[${runtime.member.id}] work-loop: active run error \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
873
|
+
);
|
|
821
874
|
}
|
|
822
875
|
continue;
|
|
823
876
|
}
|
|
@@ -872,7 +925,11 @@ async function requestShutdown(ctx, memberId, reason, timeoutMs) {
|
|
|
872
925
|
runtime.pendingShutdownId = void 0;
|
|
873
926
|
return true;
|
|
874
927
|
}
|
|
875
|
-
await ctx.updateMemberStatus(
|
|
928
|
+
await ctx.updateMemberStatus(
|
|
929
|
+
memberId,
|
|
930
|
+
"shutting-down",
|
|
931
|
+
runtime.member.activeTaskId
|
|
932
|
+
);
|
|
876
933
|
const accepted = await waitForOffline(runtime, timeoutMs);
|
|
877
934
|
await emitShutdownResolved(ctx, memberId, requestId, accepted);
|
|
878
935
|
runtime.pendingShutdownId = void 0;
|
|
@@ -1063,16 +1120,18 @@ ${notification.body}`;
|
|
|
1063
1120
|
}
|
|
1064
1121
|
function formatCoordinatorInboxBatch(items) {
|
|
1065
1122
|
return items.map(
|
|
1066
|
-
(item) => item.kind === "user-message" ? item.body : formatNotification(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1123
|
+
(item) => item.kind === "user-message" ? item.body : formatNotification(
|
|
1124
|
+
item.notification ?? {
|
|
1125
|
+
id: item.id,
|
|
1126
|
+
teamId: "",
|
|
1127
|
+
memberId: "unknown",
|
|
1128
|
+
kind: "worker-report",
|
|
1129
|
+
priority: "immediate",
|
|
1130
|
+
title: "Notification",
|
|
1131
|
+
body: item.body,
|
|
1132
|
+
createdAt: item.createdAt
|
|
1133
|
+
}
|
|
1134
|
+
)
|
|
1076
1135
|
).join("\n\n---\n\n");
|
|
1077
1136
|
}
|
|
1078
1137
|
function createCoordinatorTools(coordinator) {
|
|
@@ -1086,9 +1145,7 @@ function createCoordinatorTools(coordinator) {
|
|
|
1086
1145
|
prompt: z.string().describe(
|
|
1087
1146
|
"Self-contained task prompt with all context the teammate needs"
|
|
1088
1147
|
),
|
|
1089
|
-
dependsOn: z.array(z.string()).optional().describe(
|
|
1090
|
-
"Task IDs this task must wait for before starting."
|
|
1091
|
-
)
|
|
1148
|
+
dependsOn: z.array(z.string()).optional().describe("Task IDs this task must wait for before starting.")
|
|
1092
1149
|
}),
|
|
1093
1150
|
capabilities: { readOnly: true },
|
|
1094
1151
|
execute: async (params) => {
|
|
@@ -1186,7 +1243,9 @@ async function runCoordinatorTurn(coordinator, agent, sessionId, message, option
|
|
|
1186
1243
|
if (inactivityTimer) clearTimeout(inactivityTimer);
|
|
1187
1244
|
const ms = hasDelegated ? INACTIVITY_MS_DELEGATED : INACTIVITY_MS_NORMAL;
|
|
1188
1245
|
inactivityTimer = setTimeout(() => {
|
|
1189
|
-
log(
|
|
1246
|
+
log(
|
|
1247
|
+
`Aborting coordinator stream after ${ms / 1e3}s inactivity (delegated=${hasDelegated})`
|
|
1248
|
+
);
|
|
1190
1249
|
roundAbort.abort();
|
|
1191
1250
|
}, ms);
|
|
1192
1251
|
};
|
|
@@ -1201,7 +1260,10 @@ async function runCoordinatorTurn(coordinator, agent, sessionId, message, option
|
|
|
1201
1260
|
pendingInboxCount: coordinator.getCoordinatorInboxSize(),
|
|
1202
1261
|
activeTaskCount: await coordinator.getActiveTaskCount()
|
|
1203
1262
|
});
|
|
1204
|
-
for await (const event of raceAbort(
|
|
1263
|
+
for await (const event of raceAbort(
|
|
1264
|
+
agent.chat(sessionId, message, { abort: roundAbort.signal }),
|
|
1265
|
+
roundAbort.signal
|
|
1266
|
+
)) {
|
|
1205
1267
|
resetInactivityTimer();
|
|
1206
1268
|
if (event.type !== "text-delta" && event.type !== "reasoning-delta") {
|
|
1207
1269
|
const detail = event.type === "tool-start" ? ` ${event.toolName}` : event.type === "tool-result" ? ` ${event.toolName}` : event.type === "error" ? ` ${event.error?.message?.slice(0, 80)}` : event.type === "status" ? ` ${event.status}` : "";
|
|
@@ -1213,7 +1275,11 @@ async function runCoordinatorTurn(coordinator, agent, sessionId, message, option
|
|
|
1213
1275
|
onEvent(event);
|
|
1214
1276
|
}
|
|
1215
1277
|
if (event.type === "tool-start") {
|
|
1216
|
-
const isCoordinatorTool = [
|
|
1278
|
+
const isCoordinatorTool = [
|
|
1279
|
+
"assignTask",
|
|
1280
|
+
"sendMessage",
|
|
1281
|
+
"abortTask"
|
|
1282
|
+
].includes(event.toolName);
|
|
1217
1283
|
if (isCoordinatorTool) {
|
|
1218
1284
|
hasDelegated = true;
|
|
1219
1285
|
resetInactivityTimer();
|
|
@@ -1245,7 +1311,9 @@ async function runCoordinatorTurn(coordinator, agent, sessionId, message, option
|
|
|
1245
1311
|
}
|
|
1246
1312
|
} catch (error) {
|
|
1247
1313
|
if (!(isAbortError(error) && roundAbort.signal.aborted)) {
|
|
1248
|
-
log(
|
|
1314
|
+
log(
|
|
1315
|
+
`runCoordinatorTurn: error \u2014 ${error instanceof Error ? error.message : String(error)}`
|
|
1316
|
+
);
|
|
1249
1317
|
throw error;
|
|
1250
1318
|
}
|
|
1251
1319
|
log(`runCoordinatorTurn: aborted (expected)`);
|
|
@@ -1257,7 +1325,9 @@ async function runCoordinatorTurn(coordinator, agent, sessionId, message, option
|
|
|
1257
1325
|
}
|
|
1258
1326
|
coordinator.endCoordinatorTurnState();
|
|
1259
1327
|
}
|
|
1260
|
-
log(
|
|
1328
|
+
log(
|
|
1329
|
+
`runCoordinatorTurn: done \u2014 delegated=${hasDelegated} response=${response.length}ch assigned=${state.actions.assigned.length} msgs=${state.actions.sentMessages.length}`
|
|
1330
|
+
);
|
|
1261
1331
|
return {
|
|
1262
1332
|
response,
|
|
1263
1333
|
usage,
|
|
@@ -1458,10 +1528,14 @@ async function processInbox(ctx) {
|
|
|
1458
1528
|
const agent = ctx.ensureCoordinatorAgent();
|
|
1459
1529
|
const sessionId = ctx.getCoordinatorSessionId();
|
|
1460
1530
|
let consecutiveTurns = 0;
|
|
1461
|
-
ctx.log(
|
|
1531
|
+
ctx.log(
|
|
1532
|
+
`processInbox: starting drain (inbox=${ctx.coordinatorInbox.length}, processing=${ctx.coordinatorProcessing})`
|
|
1533
|
+
);
|
|
1462
1534
|
while (ctx.started) {
|
|
1463
1535
|
if (consecutiveTurns >= MAX_CONSECUTIVE_TURNS) {
|
|
1464
|
-
ctx.log(
|
|
1536
|
+
ctx.log(
|
|
1537
|
+
`Inbox processor yielding after ${consecutiveTurns} consecutive turns`
|
|
1538
|
+
);
|
|
1465
1539
|
break;
|
|
1466
1540
|
}
|
|
1467
1541
|
const batch = ctx.coordinatorInbox.splice(0);
|
|
@@ -1471,7 +1545,9 @@ async function processInbox(ctx) {
|
|
|
1471
1545
|
if (batch.length === 0) {
|
|
1472
1546
|
const activeTaskCount = await ctx.getActiveTaskCount();
|
|
1473
1547
|
const nextPhase = activeTaskCount > 0 ? "waiting" : "ready";
|
|
1474
|
-
ctx.log(
|
|
1548
|
+
ctx.log(
|
|
1549
|
+
`processInbox: empty batch \u2192 phase=${nextPhase} (activeTasks=${activeTaskCount})`
|
|
1550
|
+
);
|
|
1475
1551
|
ctx.publishCoordinatorStatus({
|
|
1476
1552
|
phase: nextPhase,
|
|
1477
1553
|
message: activeTaskCount > 0 ? `Waiting on ${activeTaskCount} task${activeTaskCount === 1 ? "" : "s"}` : "Ready",
|
|
@@ -1479,9 +1555,13 @@ async function processInbox(ctx) {
|
|
|
1479
1555
|
});
|
|
1480
1556
|
break;
|
|
1481
1557
|
}
|
|
1482
|
-
ctx.log(
|
|
1558
|
+
ctx.log(
|
|
1559
|
+
`processInbox: batch of ${batch.length} items (${batch.map((i) => i.kind).join(", ")})`
|
|
1560
|
+
);
|
|
1483
1561
|
const hasUser = batch.some((item) => item.kind === "user-message");
|
|
1484
|
-
const hasNotification = batch.some(
|
|
1562
|
+
const hasNotification = batch.some(
|
|
1563
|
+
(item) => item.kind === "notification"
|
|
1564
|
+
);
|
|
1485
1565
|
let turnBatch;
|
|
1486
1566
|
if (hasUser && hasNotification) {
|
|
1487
1567
|
turnBatch = batch.filter((item) => item.kind === "user-message");
|
|
@@ -1521,7 +1601,9 @@ async function processInbox(ctx) {
|
|
|
1521
1601
|
pendingInboxCount: ctx.coordinatorInbox.length
|
|
1522
1602
|
});
|
|
1523
1603
|
const message = formatCoordinatorInboxBatch(turnBatch);
|
|
1524
|
-
ctx.log(
|
|
1604
|
+
ctx.log(
|
|
1605
|
+
`processInbox: running coordinator turn #${consecutiveTurns + 1} (autonomousTurns=${ctx.autonomousTurnCount})`
|
|
1606
|
+
);
|
|
1525
1607
|
let turnAssignedCount = 0;
|
|
1526
1608
|
let turnResponseLength = 0;
|
|
1527
1609
|
try {
|
|
@@ -1598,7 +1680,9 @@ async function processInbox(ctx) {
|
|
|
1598
1680
|
}
|
|
1599
1681
|
const activeTaskCountAfterTurn = await ctx.getActiveTaskCount();
|
|
1600
1682
|
const afterPhase = activeTaskCountAfterTurn > 0 ? "waiting" : "ready";
|
|
1601
|
-
ctx.log(
|
|
1683
|
+
ctx.log(
|
|
1684
|
+
`processInbox: turn done \u2014 assigned=${turnAssignedCount} response=${turnResponseLength > 0 ? turnResponseLength + "ch" : "(empty)"} \u2192 phase=${afterPhase} (activeTasks=${activeTaskCountAfterTurn}, inbox=${ctx.coordinatorInbox.length})`
|
|
1685
|
+
);
|
|
1602
1686
|
ctx.publishCoordinatorStatus({
|
|
1603
1687
|
phase: afterPhase,
|
|
1604
1688
|
message: activeTaskCountAfterTurn > 0 ? `Waiting on ${activeTaskCountAfterTurn} task${activeTaskCountAfterTurn === 1 ? "" : "s"}` : "Ready",
|
|
@@ -1606,14 +1690,18 @@ async function processInbox(ctx) {
|
|
|
1606
1690
|
pendingInboxCount: ctx.coordinatorInbox.length
|
|
1607
1691
|
});
|
|
1608
1692
|
if (ctx.coordinatorInbox.length === 0 && activeTaskCountAfterTurn > 0) {
|
|
1609
|
-
ctx.log(
|
|
1693
|
+
ctx.log(
|
|
1694
|
+
`processInbox: yielding \u2014 waiting on ${activeTaskCountAfterTurn} task(s)`
|
|
1695
|
+
);
|
|
1610
1696
|
break;
|
|
1611
1697
|
}
|
|
1612
1698
|
}
|
|
1613
1699
|
} finally {
|
|
1614
1700
|
ctx.coordinatorProcessing = false;
|
|
1615
1701
|
if (ctx.coordinatorReschedule || ctx.coordinatorInbox.length > 0) {
|
|
1616
|
-
ctx.log(
|
|
1702
|
+
ctx.log(
|
|
1703
|
+
`processInbox: rescheduling (reschedule=${ctx.coordinatorReschedule}, inbox=${ctx.coordinatorInbox.length})`
|
|
1704
|
+
);
|
|
1617
1705
|
ctx.coordinatorReschedule = false;
|
|
1618
1706
|
ctx.scheduleCoordinatorProcessing();
|
|
1619
1707
|
} else {
|
|
@@ -1650,7 +1738,10 @@ Respond with ONLY a JSON array (no markdown fences, no explanation):
|
|
|
1650
1738
|
]
|
|
1651
1739
|
|
|
1652
1740
|
You may omit teammates that are not needed for this task.`;
|
|
1653
|
-
const result = await coordinator.lead.send(
|
|
1741
|
+
const result = await coordinator.lead.send(
|
|
1742
|
+
coordinator.leadSessionId,
|
|
1743
|
+
planningPrompt
|
|
1744
|
+
);
|
|
1654
1745
|
const tasks = parsePlanResponse(result.response, prompt, targets);
|
|
1655
1746
|
await coordinator.emit({
|
|
1656
1747
|
type: "team-plan-created",
|
|
@@ -1775,7 +1866,10 @@ ${originalPrompt}
|
|
|
1775
1866
|
Teammate results:
|
|
1776
1867
|
|
|
1777
1868
|
` + sections.join("\n\n");
|
|
1778
|
-
const result = await coordinator.lead.send(
|
|
1869
|
+
const result = await coordinator.lead.send(
|
|
1870
|
+
coordinator.leadSessionId,
|
|
1871
|
+
synthesisPrompt
|
|
1872
|
+
);
|
|
1779
1873
|
await coordinator.emit({
|
|
1780
1874
|
type: "team-synthesis",
|
|
1781
1875
|
teamId: coordinator.teamId,
|
|
@@ -1972,7 +2066,9 @@ var TeamCoordinator = class {
|
|
|
1972
2066
|
message
|
|
1973
2067
|
});
|
|
1974
2068
|
});
|
|
1975
|
-
this.log(
|
|
2069
|
+
this.log(
|
|
2070
|
+
`Started (${this.members.size} member(s), ${this.roles.size} role(s))`
|
|
2071
|
+
);
|
|
1976
2072
|
this.publishCoordinatorStatus({
|
|
1977
2073
|
phase: "ready",
|
|
1978
2074
|
message: "Ready",
|
|
@@ -1984,7 +2080,9 @@ var TeamCoordinator = class {
|
|
|
1984
2080
|
if (!runtime.workLoop) {
|
|
1985
2081
|
runtime.stopRequested = false;
|
|
1986
2082
|
runtime.workLoop = runWorkLoop(this, runtime).catch((err) => {
|
|
1987
|
-
this.log(
|
|
2083
|
+
this.log(
|
|
2084
|
+
`[${runtime.member.id}] work-loop crashed: ${err instanceof Error ? err.message : String(err)}`
|
|
2085
|
+
);
|
|
1988
2086
|
});
|
|
1989
2087
|
}
|
|
1990
2088
|
}
|
|
@@ -2068,8 +2166,12 @@ var TeamCoordinator = class {
|
|
|
2068
2166
|
pattern: rule.pattern,
|
|
2069
2167
|
action: "allow"
|
|
2070
2168
|
}));
|
|
2071
|
-
const inheritedMiddleware = [
|
|
2072
|
-
|
|
2169
|
+
const inheritedMiddleware = [
|
|
2170
|
+
...this.lead.getMiddlewareRunner().getMiddleware()
|
|
2171
|
+
];
|
|
2172
|
+
const inheritedApprovalMiddleware = inheritedMiddleware.filter(
|
|
2173
|
+
(mw) => mw.name === "approval"
|
|
2174
|
+
);
|
|
2073
2175
|
const needsTeamPermissionPolicy = permissionRules.length > 0 || inheritedApprovalMiddleware.length > 0 || Boolean(this.onPermissionRequest);
|
|
2074
2176
|
const memberMiddleware = needsTeamPermissionPolicy ? [
|
|
2075
2177
|
...inheritedMiddleware.filter(
|
|
@@ -2081,12 +2183,21 @@ var TeamCoordinator = class {
|
|
|
2081
2183
|
rules: permissionRules,
|
|
2082
2184
|
forwardApproval: async (_memberId, tool, args, ctx) => {
|
|
2083
2185
|
if (this.onPermissionRequest) {
|
|
2084
|
-
return this.createPermissionForwarder(id)(
|
|
2186
|
+
return this.createPermissionForwarder(id)(
|
|
2187
|
+
_memberId,
|
|
2188
|
+
tool,
|
|
2189
|
+
args,
|
|
2190
|
+
ctx
|
|
2191
|
+
);
|
|
2085
2192
|
}
|
|
2086
2193
|
let currentArgs = args;
|
|
2087
2194
|
for (const approval of inheritedApprovalMiddleware) {
|
|
2088
2195
|
if (!approval.beforeToolCall) continue;
|
|
2089
|
-
const decision = await approval.beforeToolCall(
|
|
2196
|
+
const decision = await approval.beforeToolCall(
|
|
2197
|
+
tool,
|
|
2198
|
+
currentArgs,
|
|
2199
|
+
ctx
|
|
2200
|
+
);
|
|
2090
2201
|
if (decision.action === "deny") {
|
|
2091
2202
|
return decision;
|
|
2092
2203
|
}
|
|
@@ -2136,22 +2247,28 @@ var TeamCoordinator = class {
|
|
|
2136
2247
|
stopRequested: false,
|
|
2137
2248
|
stats
|
|
2138
2249
|
});
|
|
2139
|
-
this.log(
|
|
2250
|
+
this.log(
|
|
2251
|
+
`Registered member @${id} (role: ${role.name}${role.description ? `, ${role.description}` : ""})`
|
|
2252
|
+
);
|
|
2140
2253
|
await this.emit({
|
|
2141
2254
|
type: "team-member-registered",
|
|
2142
2255
|
teamId: this.teamId,
|
|
2143
2256
|
member: { ...member }
|
|
2144
2257
|
});
|
|
2145
2258
|
const runtime = this.members.get(id);
|
|
2146
|
-
agent.addTool(
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2259
|
+
agent.addTool(
|
|
2260
|
+
createAskCoordinatorTool({
|
|
2261
|
+
memberId: id,
|
|
2262
|
+
teamId: this.teamId,
|
|
2263
|
+
runtime,
|
|
2264
|
+
mailbox: this.mailbox
|
|
2265
|
+
})
|
|
2266
|
+
);
|
|
2152
2267
|
if (this.taskDispatchMode === "workloop" && this.started) {
|
|
2153
2268
|
runtime.workLoop = runWorkLoop(this, runtime).catch((err) => {
|
|
2154
|
-
this.log(
|
|
2269
|
+
this.log(
|
|
2270
|
+
`[${id}] work-loop crashed: ${err instanceof Error ? err.message : String(err)}`
|
|
2271
|
+
);
|
|
2155
2272
|
});
|
|
2156
2273
|
}
|
|
2157
2274
|
return { ...member };
|
|
@@ -2185,7 +2302,9 @@ var TeamCoordinator = class {
|
|
|
2185
2302
|
prompt: input.prompt,
|
|
2186
2303
|
dependsOn: input.dependsOn
|
|
2187
2304
|
});
|
|
2188
|
-
this.log(
|
|
2305
|
+
this.log(
|
|
2306
|
+
`Queued task "${input.title}" \u2192 ${input.memberId ?? "unassigned"}${input.dependsOn?.length ? ` (depends on ${input.dependsOn.length} task(s))` : ""}`
|
|
2307
|
+
);
|
|
2189
2308
|
await this.emitTransitions(transitions);
|
|
2190
2309
|
if (this.taskDispatchMode === "manual") {
|
|
2191
2310
|
await this.dispatchReady();
|
|
@@ -2246,7 +2365,10 @@ var TeamCoordinator = class {
|
|
|
2246
2365
|
* Cascades to dependent tasks.
|
|
2247
2366
|
*/
|
|
2248
2367
|
async cancel(taskId, reason) {
|
|
2249
|
-
const { task, transitions } = await this.taskBoard.cancelTask(
|
|
2368
|
+
const { task, transitions } = await this.taskBoard.cancelTask(
|
|
2369
|
+
taskId,
|
|
2370
|
+
reason
|
|
2371
|
+
);
|
|
2250
2372
|
await this.emitTransitions(transitions);
|
|
2251
2373
|
if (task.memberId) {
|
|
2252
2374
|
const runtime = this.members.get(task.memberId);
|
|
@@ -2272,7 +2394,10 @@ var TeamCoordinator = class {
|
|
|
2272
2394
|
if (task.status !== "running" && task.status !== "claimed") {
|
|
2273
2395
|
throw new Error(`Cannot abort task in "${task.status}" state`);
|
|
2274
2396
|
}
|
|
2275
|
-
const { task: aborted, transitions } = await this.taskBoard.abortTask(
|
|
2397
|
+
const { task: aborted, transitions } = await this.taskBoard.abortTask(
|
|
2398
|
+
taskId,
|
|
2399
|
+
reason
|
|
2400
|
+
);
|
|
2276
2401
|
await this.emitTransitions(transitions);
|
|
2277
2402
|
await this.emit({
|
|
2278
2403
|
type: "team-task-aborted",
|
|
@@ -2291,7 +2416,9 @@ var TeamCoordinator = class {
|
|
|
2291
2416
|
runtime,
|
|
2292
2417
|
reason
|
|
2293
2418
|
}).catch((err) => {
|
|
2294
|
-
this.log(
|
|
2419
|
+
this.log(
|
|
2420
|
+
`External abortTask failed for task ${taskId}: ${err instanceof Error ? err.message : String(err)}`
|
|
2421
|
+
);
|
|
2295
2422
|
});
|
|
2296
2423
|
} else if (runtime && runtime.member.activeTaskId === taskId && !runtime.activeRun) {
|
|
2297
2424
|
await this.finalizeExternallyTerminatedTask(runtime);
|
|
@@ -2327,24 +2454,32 @@ var TeamCoordinator = class {
|
|
|
2327
2454
|
runtime.lastDeliveredMessageId = record.id;
|
|
2328
2455
|
if (runtime.pendingAskResolve) {
|
|
2329
2456
|
runtime.pendingAskResolve(body);
|
|
2330
|
-
this.log(
|
|
2457
|
+
this.log(
|
|
2458
|
+
`Ask response \u2192 @${memberId}: "${body.length > 80 ? body.slice(0, 80) + "\u2026" : body}"`
|
|
2459
|
+
);
|
|
2331
2460
|
return;
|
|
2332
2461
|
}
|
|
2333
2462
|
if (runtime.taskAbort || this.taskDispatchMode !== "external") {
|
|
2334
2463
|
runtime.agent.intervene(body);
|
|
2335
2464
|
} else if (this.externalTaskControl?.guideTask) {
|
|
2336
|
-
const activeTask = await this.taskBoard.getTask(
|
|
2465
|
+
const activeTask = await this.taskBoard.getTask(
|
|
2466
|
+
runtime.member.activeTaskId
|
|
2467
|
+
);
|
|
2337
2468
|
if (activeTask) {
|
|
2338
2469
|
void this.externalTaskControl.guideTask({
|
|
2339
2470
|
task: activeTask,
|
|
2340
2471
|
runtime,
|
|
2341
2472
|
message: body
|
|
2342
2473
|
}).catch((err) => {
|
|
2343
|
-
this.log(
|
|
2474
|
+
this.log(
|
|
2475
|
+
`External guideTask failed for @${memberId}: ${err instanceof Error ? err.message : String(err)}`
|
|
2476
|
+
);
|
|
2344
2477
|
});
|
|
2345
2478
|
}
|
|
2346
2479
|
}
|
|
2347
|
-
this.log(
|
|
2480
|
+
this.log(
|
|
2481
|
+
`Intervention \u2192 @${memberId}: "${body.length > 80 ? body.slice(0, 80) + "\u2026" : body}"`
|
|
2482
|
+
);
|
|
2348
2483
|
return;
|
|
2349
2484
|
}
|
|
2350
2485
|
if (this.taskDispatchMode === "manual") {
|
|
@@ -2385,21 +2520,27 @@ var TeamCoordinator = class {
|
|
|
2385
2520
|
if (runtime.taskAbort || this.taskDispatchMode !== "external") {
|
|
2386
2521
|
runtime.agent.intervene(body);
|
|
2387
2522
|
} else if (this.externalTaskControl?.guideTask && runtime.member.activeTaskId) {
|
|
2388
|
-
const activeTask = await this.taskBoard.getTask(
|
|
2523
|
+
const activeTask = await this.taskBoard.getTask(
|
|
2524
|
+
runtime.member.activeTaskId
|
|
2525
|
+
);
|
|
2389
2526
|
if (activeTask) {
|
|
2390
2527
|
void this.externalTaskControl.guideTask({
|
|
2391
2528
|
task: activeTask,
|
|
2392
2529
|
runtime,
|
|
2393
2530
|
message: body
|
|
2394
2531
|
}).catch((err) => {
|
|
2395
|
-
this.log(
|
|
2532
|
+
this.log(
|
|
2533
|
+
`External guideTask (broadcast) failed for @${runtime.member.id}: ${err instanceof Error ? err.message : String(err)}`
|
|
2534
|
+
);
|
|
2396
2535
|
});
|
|
2397
2536
|
}
|
|
2398
2537
|
}
|
|
2399
2538
|
recipients.push(runtime.member.id);
|
|
2400
2539
|
}
|
|
2401
2540
|
}
|
|
2402
|
-
this.log(
|
|
2541
|
+
this.log(
|
|
2542
|
+
`Broadcast: "${body.length > 80 ? body.slice(0, 80) + "\u2026" : body}" \u2192 ${recipients.length > 0 ? recipients.join(", ") : "no active members"}`
|
|
2543
|
+
);
|
|
2403
2544
|
return record;
|
|
2404
2545
|
}
|
|
2405
2546
|
// ── Shutdown protocol ──────────────────────────────────────────────
|
|
@@ -2498,7 +2639,10 @@ var TeamCoordinator = class {
|
|
|
2498
2639
|
if (!this.coordinatorAgent) {
|
|
2499
2640
|
this.coordinatorAgent = createCoordinatorAgent(this);
|
|
2500
2641
|
this.coordinatorSessionId = `team:${this.teamId}:coordinator`;
|
|
2501
|
-
this.attachCoordinatorTurn(
|
|
2642
|
+
this.attachCoordinatorTurn(
|
|
2643
|
+
this.coordinatorAgent,
|
|
2644
|
+
this.coordinatorSessionId
|
|
2645
|
+
);
|
|
2502
2646
|
}
|
|
2503
2647
|
return this.coordinatorAgent;
|
|
2504
2648
|
}
|
|
@@ -2550,7 +2694,9 @@ var TeamCoordinator = class {
|
|
|
2550
2694
|
throw new Error("Coordinator message must not be empty");
|
|
2551
2695
|
}
|
|
2552
2696
|
const id = this.createId();
|
|
2553
|
-
this.log(
|
|
2697
|
+
this.log(
|
|
2698
|
+
`submitToCoordinator: id=${id} body="${body.length > 80 ? body.slice(0, 80) + "\u2026" : body}" (processing=${this.coordinatorProcessing}, inbox=${this.coordinatorInbox.length})`
|
|
2699
|
+
);
|
|
2554
2700
|
this.enqueueCoordinatorInboxItem({
|
|
2555
2701
|
id,
|
|
2556
2702
|
kind: "user-message",
|
|
@@ -2836,7 +2982,9 @@ var TeamCoordinator = class {
|
|
|
2836
2982
|
async emit(event) {
|
|
2837
2983
|
await this.dispatchEvent(event);
|
|
2838
2984
|
if (event.type === "team-notification") {
|
|
2839
|
-
this.log(
|
|
2985
|
+
this.log(
|
|
2986
|
+
`emit: team-notification kind=${event.notification.kind} member=${event.notification.memberId}`
|
|
2987
|
+
);
|
|
2840
2988
|
this.enqueueCoordinatorNotification(event.notification);
|
|
2841
2989
|
this.scheduleCoordinatorProcessing();
|
|
2842
2990
|
return;
|
|
@@ -2847,7 +2995,9 @@ var TeamCoordinator = class {
|
|
|
2847
2995
|
(taskId) => this.taskBoard.getTask(taskId)
|
|
2848
2996
|
);
|
|
2849
2997
|
if (notificationEvent) {
|
|
2850
|
-
this.log(
|
|
2998
|
+
this.log(
|
|
2999
|
+
`emit: ${event.type} \u2192 notification kind=${notificationEvent.notification.kind} member=${notificationEvent.notification.memberId}`
|
|
3000
|
+
);
|
|
2851
3001
|
await this.dispatchEvent(notificationEvent);
|
|
2852
3002
|
this.enqueueCoordinatorNotification(notificationEvent.notification);
|
|
2853
3003
|
this.scheduleCoordinatorProcessing();
|
|
@@ -2866,7 +3016,9 @@ var TeamCoordinator = class {
|
|
|
2866
3016
|
});
|
|
2867
3017
|
}
|
|
2868
3018
|
if (t.previous !== void 0) {
|
|
2869
|
-
this.log(
|
|
3019
|
+
this.log(
|
|
3020
|
+
`Task "${t.task.title}" (${t.task.memberId ?? "?"}): ${t.previous} \u2192 ${t.task.status}${t.reason ? ` (${t.reason})` : ""}`
|
|
3021
|
+
);
|
|
2870
3022
|
await this.emit({
|
|
2871
3023
|
type: "team-task-transition",
|
|
2872
3024
|
teamId: this.teamId,
|