@wrongstack/core 0.24.0 → 0.32.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/coordination/index.d.ts +3 -3
- package/dist/coordination/index.js +467 -72
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +2 -2
- package/dist/defaults/index.js +441 -58
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +47 -5
- package/dist/execution/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +688 -243
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.js +2 -1
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/{multi-agent-coordinator-Dcsc5t4-.d.ts → multi-agent-coordinator-DOXSgtom.d.ts} +4 -0
- package/dist/{null-fleet-bus-Cq4YKOiW.d.ts → null-fleet-bus-DLsUjOyB.d.ts} +77 -2
- package/dist/sdd/index.js +40 -3
- package/dist/sdd/index.js.map +1 -1
- package/dist/types/index.js +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +76 -1
- package/dist/utils/index.js +171 -5
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/multi-agent/SKILL.md +57 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID, randomBytes } from 'crypto';
|
|
2
|
-
import * as
|
|
2
|
+
import * as fsp6 from 'fs/promises';
|
|
3
3
|
import * as path4 from 'path';
|
|
4
|
+
import { isAbsolute, resolve } from 'path';
|
|
4
5
|
import { EventEmitter } from 'events';
|
|
5
6
|
|
|
6
7
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
@@ -11,16 +12,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
11
12
|
});
|
|
12
13
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
13
14
|
const dir = path4.dirname(targetPath);
|
|
14
|
-
await
|
|
15
|
+
await fsp6.mkdir(dir, { recursive: true });
|
|
15
16
|
const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
16
17
|
try {
|
|
17
18
|
if (typeof content === "string") {
|
|
18
|
-
await
|
|
19
|
+
await fsp6.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
19
20
|
} else {
|
|
20
|
-
await
|
|
21
|
+
await fsp6.writeFile(tmp, content, { flag: "wx" });
|
|
21
22
|
}
|
|
22
23
|
try {
|
|
23
|
-
const fh = await
|
|
24
|
+
const fh = await fsp6.open(tmp, "r+");
|
|
24
25
|
try {
|
|
25
26
|
await fh.sync();
|
|
26
27
|
} finally {
|
|
@@ -30,37 +31,37 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
30
31
|
}
|
|
31
32
|
let mode;
|
|
32
33
|
try {
|
|
33
|
-
const
|
|
34
|
-
mode =
|
|
34
|
+
const stat4 = await fsp6.stat(targetPath);
|
|
35
|
+
mode = stat4.mode & 511;
|
|
35
36
|
} catch {
|
|
36
37
|
mode = opts.mode;
|
|
37
38
|
}
|
|
38
39
|
if (mode !== void 0) {
|
|
39
|
-
await
|
|
40
|
+
await fsp6.chmod(tmp, mode);
|
|
40
41
|
}
|
|
41
42
|
await renameWithRetry(tmp, targetPath);
|
|
42
43
|
} catch (err) {
|
|
43
44
|
try {
|
|
44
|
-
await
|
|
45
|
+
await fsp6.unlink(tmp);
|
|
45
46
|
} catch {
|
|
46
47
|
}
|
|
47
48
|
throw err;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
async function ensureDir(dir) {
|
|
51
|
-
await
|
|
52
|
+
await fsp6.mkdir(dir, { recursive: true });
|
|
52
53
|
}
|
|
53
54
|
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
54
55
|
async function renameWithRetry(from, to) {
|
|
55
56
|
if (process.platform !== "win32") {
|
|
56
|
-
await
|
|
57
|
+
await fsp6.rename(from, to);
|
|
57
58
|
return;
|
|
58
59
|
}
|
|
59
60
|
const delays = [10, 25, 60, 120, 250];
|
|
60
61
|
let lastErr;
|
|
61
62
|
for (let i = 0; i <= delays.length; i++) {
|
|
62
63
|
try {
|
|
63
|
-
await
|
|
64
|
+
await fsp6.rename(from, to);
|
|
64
65
|
return;
|
|
65
66
|
} catch (err) {
|
|
66
67
|
lastErr = err;
|
|
@@ -68,15 +69,89 @@ async function renameWithRetry(from, to) {
|
|
|
68
69
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
69
70
|
throw err;
|
|
70
71
|
}
|
|
71
|
-
await new Promise((
|
|
72
|
+
await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
throw lastErr;
|
|
75
76
|
}
|
|
77
|
+
|
|
78
|
+
// src/coordination/large-answer-store.ts
|
|
79
|
+
var LargeAnswerStore = class {
|
|
80
|
+
/**
|
|
81
|
+
* Responses above this size (in characters) are stored out-of-context.
|
|
82
|
+
* Below this, the full answer is returned inline (no overhead).
|
|
83
|
+
* Default: 2000 chars ≈ 400-600 tokens.
|
|
84
|
+
*/
|
|
85
|
+
sizeThreshold;
|
|
86
|
+
store = /* @__PURE__ */ new Map();
|
|
87
|
+
constructor(sizeThreshold = 2e3) {
|
|
88
|
+
this.sizeThreshold = sizeThreshold;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Store a value, returning a summary + key for inline use.
|
|
92
|
+
* If the value is below sizeThreshold, returns it as-is (no store entry).
|
|
93
|
+
*/
|
|
94
|
+
storeAnswer(value) {
|
|
95
|
+
if (value === void 0 || value === null) {
|
|
96
|
+
return { summary: String(value), inline: true };
|
|
97
|
+
}
|
|
98
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
99
|
+
const size = serialized.length;
|
|
100
|
+
if (size <= this.sizeThreshold) {
|
|
101
|
+
return { summary: serialized.slice(0, 500), inline: true };
|
|
102
|
+
}
|
|
103
|
+
const key = `a-${hashStr(serialized)}`;
|
|
104
|
+
this.store.set(key, {
|
|
105
|
+
key,
|
|
106
|
+
value,
|
|
107
|
+
size,
|
|
108
|
+
storedAt: Date.now()
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
key,
|
|
112
|
+
summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
|
|
113
|
+
inline: false
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Retrieve a previously stored answer by its key.
|
|
118
|
+
* Returns undefined if the key is unknown or the store was cleared.
|
|
119
|
+
*/
|
|
120
|
+
retrieveAnswer(key) {
|
|
121
|
+
return this.store.get(key)?.value;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if a key exists in the store.
|
|
125
|
+
*/
|
|
126
|
+
hasAnswer(key) {
|
|
127
|
+
return this.store.has(key);
|
|
128
|
+
}
|
|
129
|
+
/** Number of stored entries. */
|
|
130
|
+
get size() {
|
|
131
|
+
return this.store.size;
|
|
132
|
+
}
|
|
133
|
+
/** Total characters stored. */
|
|
134
|
+
get totalChars() {
|
|
135
|
+
let total = 0;
|
|
136
|
+
for (const e of this.store.values()) total += e.size;
|
|
137
|
+
return total;
|
|
138
|
+
}
|
|
139
|
+
/** Clear all stored entries. Call at the end of a director run. */
|
|
140
|
+
clear() {
|
|
141
|
+
this.store.clear();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
function hashStr(s) {
|
|
145
|
+
let h = 5381;
|
|
146
|
+
for (let i = 0; i < s.length; i++) {
|
|
147
|
+
h = h * 33 ^ s.charCodeAt(i);
|
|
148
|
+
}
|
|
149
|
+
return (h >>> 0).toString(36);
|
|
150
|
+
}
|
|
76
151
|
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
77
152
|
let existing;
|
|
78
153
|
try {
|
|
79
|
-
existing = await
|
|
154
|
+
existing = await fsp6.readFile(lockPath, "utf8");
|
|
80
155
|
} catch {
|
|
81
156
|
}
|
|
82
157
|
if (existing) {
|
|
@@ -100,7 +175,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
100
175
|
}
|
|
101
176
|
async function releaseDirectorStateLock(lockPath) {
|
|
102
177
|
try {
|
|
103
|
-
await
|
|
178
|
+
await fsp6.unlink(lockPath);
|
|
104
179
|
} catch {
|
|
105
180
|
}
|
|
106
181
|
}
|
|
@@ -334,7 +409,7 @@ var InMemoryAgentBridge = class {
|
|
|
334
409
|
);
|
|
335
410
|
}
|
|
336
411
|
this.inflightGuards.add(correlationId);
|
|
337
|
-
return new Promise((
|
|
412
|
+
return new Promise((resolve2, reject) => {
|
|
338
413
|
const timer = setTimeout(() => {
|
|
339
414
|
this.inflightGuards.delete(correlationId);
|
|
340
415
|
this.pendingRequests.delete(correlationId);
|
|
@@ -346,7 +421,7 @@ var InMemoryAgentBridge = class {
|
|
|
346
421
|
return;
|
|
347
422
|
}
|
|
348
423
|
this.pendingRequests.set(correlationId, {
|
|
349
|
-
resolve,
|
|
424
|
+
resolve: resolve2,
|
|
350
425
|
reject,
|
|
351
426
|
timer
|
|
352
427
|
});
|
|
@@ -707,6 +782,10 @@ var NICKNAME_POOL = {
|
|
|
707
782
|
"pasteur": { name: "Pasteur", domain: "biology" },
|
|
708
783
|
"hawking": { name: "Hawking", domain: "cosmology" },
|
|
709
784
|
"sagan": { name: "Sagan", domain: "cosmology" },
|
|
785
|
+
// Exploration & navigation
|
|
786
|
+
"columbus": { name: "Columbus", domain: "exploration" },
|
|
787
|
+
"polo": { name: "Polo", domain: "exploration" },
|
|
788
|
+
"magellan": { name: "Magellan", domain: "exploration" },
|
|
710
789
|
// Chemistry / materials
|
|
711
790
|
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
712
791
|
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
@@ -1083,12 +1162,12 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
1083
1162
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
1084
1163
|
return Promise.resolve("stop");
|
|
1085
1164
|
}
|
|
1086
|
-
return new Promise((
|
|
1165
|
+
return new Promise((resolve2) => {
|
|
1087
1166
|
let resolved = false;
|
|
1088
1167
|
const respond = (d) => {
|
|
1089
1168
|
if (resolved) return;
|
|
1090
1169
|
resolved = true;
|
|
1091
|
-
|
|
1170
|
+
resolve2(d);
|
|
1092
1171
|
};
|
|
1093
1172
|
const fallback = setTimeout(
|
|
1094
1173
|
() => respond("stop"),
|
|
@@ -3686,7 +3765,7 @@ var FLEET_ROSTER_WITHACP = {
|
|
|
3686
3765
|
};
|
|
3687
3766
|
|
|
3688
3767
|
// src/coordination/multi-agent-coordinator.ts
|
|
3689
|
-
var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
3768
|
+
var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends EventEmitter {
|
|
3690
3769
|
coordinatorId;
|
|
3691
3770
|
config;
|
|
3692
3771
|
runner;
|
|
@@ -3701,8 +3780,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
3701
3780
|
* names) to memorable ones here — that's what surfaces in the fleet monitor.
|
|
3702
3781
|
*/
|
|
3703
3782
|
usedNicknames = /* @__PURE__ */ new Set();
|
|
3783
|
+
/** Maps subagentId → nickname key (e.g. 'einstein'). Used to free the slot on remove(). */
|
|
3784
|
+
subagentNicknames = /* @__PURE__ */ new Map();
|
|
3704
3785
|
pendingTasks = [];
|
|
3705
3786
|
completedResults = [];
|
|
3787
|
+
/** Prevents completedResults from growing unbounded in long-running coordinators. */
|
|
3788
|
+
static MAX_COMPLETED_RESULTS = 1e4;
|
|
3706
3789
|
totalIterations = 0;
|
|
3707
3790
|
inFlight = 0;
|
|
3708
3791
|
/**
|
|
@@ -3757,7 +3840,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
3757
3840
|
* Explicit, human-chosen names — including nicknames already assigned by
|
|
3758
3841
|
* `Director.spawn()` — are left untouched, so this never double-assigns.
|
|
3759
3842
|
*/
|
|
3760
|
-
withNickname(subagent) {
|
|
3843
|
+
withNickname(subagent, subagentId) {
|
|
3761
3844
|
const role = subagent.role ?? "subagent";
|
|
3762
3845
|
const name = subagent.name?.trim() ?? "";
|
|
3763
3846
|
const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
|
|
@@ -3765,11 +3848,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
3765
3848
|
const nickname = assignNickname(role, this.usedNicknames);
|
|
3766
3849
|
const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
3767
3850
|
this.usedNicknames.add(baseKey);
|
|
3851
|
+
this.subagentNicknames.set(subagentId, baseKey);
|
|
3768
3852
|
return { ...subagent, name: nickname };
|
|
3769
3853
|
}
|
|
3770
3854
|
async spawn(subagent) {
|
|
3771
|
-
subagent = this.withNickname(subagent);
|
|
3772
3855
|
const id = subagent.id || randomUUID();
|
|
3856
|
+
subagent = this.withNickname(subagent, id);
|
|
3773
3857
|
if (this.subagents.has(id)) {
|
|
3774
3858
|
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
3775
3859
|
}
|
|
@@ -3913,7 +3997,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
3913
3997
|
taskIds.map((id) => {
|
|
3914
3998
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
3915
3999
|
if (cached) return cached;
|
|
3916
|
-
return new Promise((
|
|
4000
|
+
return new Promise((resolve2, reject) => {
|
|
3917
4001
|
const timeout = setTimeout(() => {
|
|
3918
4002
|
this.off("task.completed", handler);
|
|
3919
4003
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -3922,7 +4006,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
3922
4006
|
if (result.taskId === id) {
|
|
3923
4007
|
clearTimeout(timeout);
|
|
3924
4008
|
this.off("task.completed", handler);
|
|
3925
|
-
|
|
4009
|
+
resolve2(result);
|
|
3926
4010
|
}
|
|
3927
4011
|
};
|
|
3928
4012
|
this.on("task.completed", handler);
|
|
@@ -4211,6 +4295,12 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4211
4295
|
}
|
|
4212
4296
|
recordCompletion(result) {
|
|
4213
4297
|
this.completedResults.push(result);
|
|
4298
|
+
if (this.completedResults.length > _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS) {
|
|
4299
|
+
this.completedResults.splice(
|
|
4300
|
+
0,
|
|
4301
|
+
this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
4214
4304
|
this.totalIterations += result.iterations;
|
|
4215
4305
|
if (this.inFlight > 0) {
|
|
4216
4306
|
this.inFlight--;
|
|
@@ -4280,7 +4370,29 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4280
4370
|
}
|
|
4281
4371
|
this.subagents.delete(subagentId);
|
|
4282
4372
|
this.terminating.delete(subagentId);
|
|
4373
|
+
const nicknameKey = this.subagentNicknames.get(subagentId);
|
|
4374
|
+
if (nicknameKey) {
|
|
4375
|
+
this.usedNicknames.delete(nicknameKey);
|
|
4376
|
+
this.subagentNicknames.delete(subagentId);
|
|
4377
|
+
}
|
|
4378
|
+
const orphaned = this.pendingTasks.filter((t) => t.subagentId === subagentId);
|
|
4283
4379
|
this.pendingTasks = this.pendingTasks.filter((t) => t.subagentId !== subagentId);
|
|
4380
|
+
for (const t of orphaned) {
|
|
4381
|
+
const synthetic = {
|
|
4382
|
+
subagentId,
|
|
4383
|
+
taskId: t.id,
|
|
4384
|
+
status: "stopped",
|
|
4385
|
+
error: {
|
|
4386
|
+
kind: "aborted_by_parent",
|
|
4387
|
+
message: `Subagent "${subagentId}" was removed while task "${t.id}" was pending`,
|
|
4388
|
+
retryable: false
|
|
4389
|
+
},
|
|
4390
|
+
iterations: 0,
|
|
4391
|
+
toolCalls: 0,
|
|
4392
|
+
durationMs: 0
|
|
4393
|
+
};
|
|
4394
|
+
this.recordCompletion(synthetic);
|
|
4395
|
+
}
|
|
4284
4396
|
this.fleetBus?.emit({
|
|
4285
4397
|
subagentId,
|
|
4286
4398
|
ts: Date.now(),
|
|
@@ -4528,9 +4640,12 @@ function makeSpawnTool(director, roster) {
|
|
|
4528
4640
|
provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
|
|
4529
4641
|
model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
|
|
4530
4642
|
systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
|
|
4531
|
-
maxIterations: { type: "number" },
|
|
4532
|
-
maxToolCalls: { type: "number" },
|
|
4533
|
-
maxCostUsd: { type: "number" }
|
|
4643
|
+
maxIterations: { type: "number", minimum: 1 },
|
|
4644
|
+
maxToolCalls: { type: "number", minimum: 1 },
|
|
4645
|
+
maxCostUsd: { type: "number", minimum: 0 },
|
|
4646
|
+
timeoutMs: { type: "number", minimum: 1, description: "Hard wall-clock cap in milliseconds. Defaults to none (idle timeout is the default reaper)." },
|
|
4647
|
+
idleTimeoutMs: { type: "number", minimum: 1, description: "Idle timeout in ms: reap the subagent after this long with no activity. Resets on every iteration/tool call. Default is role/coordinator-specific." },
|
|
4648
|
+
maxTokens: { type: "number", minimum: 1, description: "Maximum total tokens (input + output) the subagent may use." }
|
|
4534
4649
|
},
|
|
4535
4650
|
required: []
|
|
4536
4651
|
};
|
|
@@ -4576,6 +4691,9 @@ function makeSpawnTool(director, roster) {
|
|
|
4576
4691
|
if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
|
|
4577
4692
|
if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
|
|
4578
4693
|
if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
|
|
4694
|
+
if (typeof i.timeoutMs === "number") cfg.timeoutMs = i.timeoutMs;
|
|
4695
|
+
if (typeof i.idleTimeoutMs === "number") cfg.idleTimeoutMs = i.idleTimeoutMs;
|
|
4696
|
+
if (typeof i.maxTokens === "number") cfg.maxTokens = i.maxTokens;
|
|
4579
4697
|
try {
|
|
4580
4698
|
const subagentId = await director.spawn(cfg);
|
|
4581
4699
|
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name, role: cfg.role };
|
|
@@ -4603,10 +4721,10 @@ function makeAssignTool(director) {
|
|
|
4603
4721
|
const inputSchema = {
|
|
4604
4722
|
type: "object",
|
|
4605
4723
|
properties: {
|
|
4606
|
-
subagentId: { type: "string", description: "Target subagent id. Required." },
|
|
4607
|
-
description: { type: "string", description: "The task in natural language \u2014 what you want this subagent to do." },
|
|
4608
|
-
maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
|
|
4609
|
-
timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
|
|
4724
|
+
subagentId: { type: "string", minLength: 1, description: "Target subagent id. Required." },
|
|
4725
|
+
description: { type: "string", minLength: 1, description: "The task in natural language \u2014 what you want this subagent to do." },
|
|
4726
|
+
maxToolCalls: { type: "number", minimum: 1, description: "Optional per-task tool-call budget override." },
|
|
4727
|
+
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
4610
4728
|
},
|
|
4611
4729
|
required: ["subagentId", "description"]
|
|
4612
4730
|
};
|
|
@@ -4647,9 +4765,9 @@ function makeAskTool(director) {
|
|
|
4647
4765
|
inputSchema: {
|
|
4648
4766
|
type: "object",
|
|
4649
4767
|
properties: {
|
|
4650
|
-
subagentId: { type: "string", description: "Subagent to ask. Must be a previously spawned id." },
|
|
4651
|
-
question: { type: "string", description: "The question or instruction." },
|
|
4652
|
-
timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
|
|
4768
|
+
subagentId: { type: "string", minLength: 1, description: "Subagent to ask. Must be a previously spawned id." },
|
|
4769
|
+
question: { type: "string", minLength: 1, description: "The question or instruction." },
|
|
4770
|
+
timeoutMs: { type: "number", minimum: 1, description: "Optional timeout in ms (default 30s)." }
|
|
4653
4771
|
},
|
|
4654
4772
|
required: ["subagentId", "question"]
|
|
4655
4773
|
},
|
|
@@ -4657,13 +4775,49 @@ function makeAskTool(director) {
|
|
|
4657
4775
|
const i = input;
|
|
4658
4776
|
try {
|
|
4659
4777
|
const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
|
|
4660
|
-
|
|
4778
|
+
const stored = director.largeAnswerStore.storeAnswer(answer);
|
|
4779
|
+
if (stored.inline) {
|
|
4780
|
+
return { ok: true, answer: stored.summary };
|
|
4781
|
+
}
|
|
4782
|
+
return {
|
|
4783
|
+
ok: true,
|
|
4784
|
+
answer: stored.summary,
|
|
4785
|
+
_answerKey: stored.key,
|
|
4786
|
+
_hint: "Response was large and stored. Use ask_result with the key to retrieve it."
|
|
4787
|
+
};
|
|
4661
4788
|
} catch (err) {
|
|
4662
4789
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
4663
4790
|
}
|
|
4664
4791
|
}
|
|
4665
4792
|
};
|
|
4666
4793
|
}
|
|
4794
|
+
function makeAskResultTool(director) {
|
|
4795
|
+
return {
|
|
4796
|
+
name: "ask_result",
|
|
4797
|
+
description: "Retrieve a large `ask_subagent` response that was stored out-of-context (>2K chars). Returns the full stored value.",
|
|
4798
|
+
permission: "auto",
|
|
4799
|
+
mutating: false,
|
|
4800
|
+
inputSchema: {
|
|
4801
|
+
type: "object",
|
|
4802
|
+
properties: {
|
|
4803
|
+
key: {
|
|
4804
|
+
type: "string",
|
|
4805
|
+
minLength: 1,
|
|
4806
|
+
description: "The `_answerKey` returned by `ask_subagent` for a large response."
|
|
4807
|
+
}
|
|
4808
|
+
},
|
|
4809
|
+
required: ["key"]
|
|
4810
|
+
},
|
|
4811
|
+
async execute(input) {
|
|
4812
|
+
const i = input;
|
|
4813
|
+
const value = director.largeAnswerStore.retrieveAnswer(i.key);
|
|
4814
|
+
if (value === void 0) {
|
|
4815
|
+
return { ok: false, error: `No stored answer found for key "${i.key}" \u2014 it may have been cleared or the key is invalid.` };
|
|
4816
|
+
}
|
|
4817
|
+
return { ok: true, value };
|
|
4818
|
+
}
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4667
4821
|
function makeRollUpTool(director) {
|
|
4668
4822
|
return {
|
|
4669
4823
|
name: "roll_up",
|
|
@@ -4817,7 +4971,18 @@ function makeCollabDebugTool(director) {
|
|
|
4817
4971
|
},
|
|
4818
4972
|
timeoutMs: {
|
|
4819
4973
|
type: "number",
|
|
4974
|
+
minimum: 1,
|
|
4820
4975
|
description: "Timeout in ms. Default: 600000 (10 minutes)."
|
|
4976
|
+
},
|
|
4977
|
+
maxTargetFiles: {
|
|
4978
|
+
type: "number",
|
|
4979
|
+
minimum: 1,
|
|
4980
|
+
description: "Maximum number of files to include in the snapshot. If not set, the limit is computed dynamically from contextWindow or falls back to the default (30)."
|
|
4981
|
+
},
|
|
4982
|
+
contextWindow: {
|
|
4983
|
+
type: "number",
|
|
4984
|
+
minimum: 1,
|
|
4985
|
+
description: "Context window size (tokens) of the model. When provided and maxTargetFiles is not set, the file limit is computed dynamically as floor((contextWindow * 0.4) / 2000)."
|
|
4821
4986
|
}
|
|
4822
4987
|
},
|
|
4823
4988
|
required: ["targetPaths"]
|
|
@@ -4829,7 +4994,9 @@ function makeCollabDebugTool(director) {
|
|
|
4829
4994
|
}
|
|
4830
4995
|
const options = {
|
|
4831
4996
|
targetPaths: i.targetPaths,
|
|
4832
|
-
timeoutMs: i.timeoutMs
|
|
4997
|
+
timeoutMs: i.timeoutMs,
|
|
4998
|
+
maxTargetFiles: i.maxTargetFiles,
|
|
4999
|
+
contextWindow: i.contextWindow
|
|
4833
5000
|
};
|
|
4834
5001
|
try {
|
|
4835
5002
|
const report = await director.spawnCollab(options);
|
|
@@ -4896,6 +5063,123 @@ function makeWorkCompleteTool(director) {
|
|
|
4896
5063
|
}
|
|
4897
5064
|
};
|
|
4898
5065
|
}
|
|
5066
|
+
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
5067
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
5068
|
+
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
5069
|
+
function isGlob(p) {
|
|
5070
|
+
for (const c of p) {
|
|
5071
|
+
if (GLOB_CHARS.has(c)) return true;
|
|
5072
|
+
}
|
|
5073
|
+
return false;
|
|
5074
|
+
}
|
|
5075
|
+
function globToRegex(pat) {
|
|
5076
|
+
let i = 0, re = "^";
|
|
5077
|
+
while (i < pat.length) {
|
|
5078
|
+
const c = pat[i];
|
|
5079
|
+
if (c === "*") {
|
|
5080
|
+
if (pat[i + 1] === "*") {
|
|
5081
|
+
re += ".*";
|
|
5082
|
+
i += 2;
|
|
5083
|
+
if (pat[i] === "/") i++;
|
|
5084
|
+
} else {
|
|
5085
|
+
re += "[^/\\\\]*";
|
|
5086
|
+
i++;
|
|
5087
|
+
}
|
|
5088
|
+
} else if (c === "?") {
|
|
5089
|
+
re += "[^/\\\\]";
|
|
5090
|
+
i++;
|
|
5091
|
+
} else if (c === "[") {
|
|
5092
|
+
let cls = "[";
|
|
5093
|
+
i++;
|
|
5094
|
+
if (pat[i] === "!" || pat[i] === "^") {
|
|
5095
|
+
cls += "^";
|
|
5096
|
+
i++;
|
|
5097
|
+
}
|
|
5098
|
+
while (i < pat.length && pat[i] !== "]") {
|
|
5099
|
+
const ch = pat[i] ?? "";
|
|
5100
|
+
if (ch === "\\") cls += "\\\\";
|
|
5101
|
+
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
5102
|
+
else cls += ch;
|
|
5103
|
+
i++;
|
|
5104
|
+
}
|
|
5105
|
+
cls += "]";
|
|
5106
|
+
re += cls;
|
|
5107
|
+
i++;
|
|
5108
|
+
} else {
|
|
5109
|
+
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
5110
|
+
i++;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
return new RegExp(re + "$");
|
|
5114
|
+
}
|
|
5115
|
+
function baseDir(pat) {
|
|
5116
|
+
let i = pat.length - 1;
|
|
5117
|
+
while (i >= 0 && !GLOB_CHARS.has(pat[i]) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
5118
|
+
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
5119
|
+
return cut < 0 ? "." : pat.slice(0, cut);
|
|
5120
|
+
}
|
|
5121
|
+
async function expandGlob(pattern) {
|
|
5122
|
+
if (!isGlob(pattern)) return [pattern];
|
|
5123
|
+
const results = /* @__PURE__ */ new Set();
|
|
5124
|
+
const abs = isAbsolute(pattern);
|
|
5125
|
+
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
5126
|
+
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
5127
|
+
async function walk(dir, pat) {
|
|
5128
|
+
let entries;
|
|
5129
|
+
try {
|
|
5130
|
+
entries = await fsp6.readdir(dir);
|
|
5131
|
+
} catch {
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
const firstGlob = pat.search(/[*?[\[]/);
|
|
5135
|
+
if (firstGlob < 0) {
|
|
5136
|
+
const re = globToRegex(pat);
|
|
5137
|
+
for (const e of entries) {
|
|
5138
|
+
if (re.test(e)) {
|
|
5139
|
+
const full = `${dir}${SEP}${e}`;
|
|
5140
|
+
results.add(abs ? resolve(full) : full);
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
return;
|
|
5144
|
+
}
|
|
5145
|
+
const before = pat.slice(0, firstGlob);
|
|
5146
|
+
const rest = pat.slice(firstGlob);
|
|
5147
|
+
if (before.endsWith("**")) {
|
|
5148
|
+
await walk(dir, rest);
|
|
5149
|
+
for (const e of entries) {
|
|
5150
|
+
const full = `${dir}${SEP}${e}`;
|
|
5151
|
+
try {
|
|
5152
|
+
const stat4 = await fsp6.stat(full);
|
|
5153
|
+
if (stat4.isDirectory()) await walk(full, rest);
|
|
5154
|
+
} catch {
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
} else if (before === "") {
|
|
5158
|
+
const re = globToRegex(rest);
|
|
5159
|
+
for (const e of entries) {
|
|
5160
|
+
if (re.test(e)) {
|
|
5161
|
+
const full = `${dir}${SEP}${e}`;
|
|
5162
|
+
results.add(abs ? resolve(full) : full);
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
} else {
|
|
5166
|
+
const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
|
|
5167
|
+
if (entries.includes(seg)) {
|
|
5168
|
+
const full = `${dir}${SEP}${seg}`;
|
|
5169
|
+
try {
|
|
5170
|
+
const stat4 = await fsp6.stat(full);
|
|
5171
|
+
if (stat4.isDirectory()) await walk(full, rest);
|
|
5172
|
+
} catch {
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
await walk(base === "." ? "." : base, relPat);
|
|
5178
|
+
return [...results];
|
|
5179
|
+
}
|
|
5180
|
+
|
|
5181
|
+
// src/coordination/collab-debug.ts
|
|
5182
|
+
var DEFAULT_MAX_TARGET_FILES = 30;
|
|
4899
5183
|
var DirectorAlertLevel = /* @__PURE__ */ ((DirectorAlertLevel2) => {
|
|
4900
5184
|
DirectorAlertLevel2["WARNING"] = "warning";
|
|
4901
5185
|
DirectorAlertLevel2["CRITICAL"] = "critical";
|
|
@@ -4958,11 +5242,36 @@ var CollabSession = class extends EventEmitter {
|
|
|
4958
5242
|
getSubagentIds() {
|
|
4959
5243
|
return new Map(this.subagentIds);
|
|
4960
5244
|
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Returns the effective file limit for this session.
|
|
5247
|
+
* Priority: explicit `maxTargetFiles` > dynamic from `contextWindow` > `DEFAULT_MAX_TARGET_FILES`.
|
|
5248
|
+
*/
|
|
5249
|
+
effectiveFileLimit() {
|
|
5250
|
+
if (this.options.maxTargetFiles !== void 0) {
|
|
5251
|
+
return this.options.maxTargetFiles;
|
|
5252
|
+
}
|
|
5253
|
+
if (this.options.contextWindow !== void 0) {
|
|
5254
|
+
return Math.max(5, Math.floor(this.options.contextWindow * 0.4 / 2e3));
|
|
5255
|
+
}
|
|
5256
|
+
return DEFAULT_MAX_TARGET_FILES;
|
|
5257
|
+
}
|
|
4961
5258
|
async buildSnapshot() {
|
|
4962
5259
|
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
4963
|
-
|
|
5260
|
+
const allFiles = [];
|
|
5261
|
+
for (const pattern of this.options.targetPaths) {
|
|
5262
|
+
const expanded = await expandGlob(pattern);
|
|
5263
|
+
allFiles.push(...expanded);
|
|
5264
|
+
}
|
|
5265
|
+
const limit = this.effectiveFileLimit();
|
|
5266
|
+
if (allFiles.length > limit) {
|
|
5267
|
+
const hint = this.options.contextWindow ? `contextWindow=${this.options.contextWindow} \u2192 calculated limit=${limit}` : `default limit=${DEFAULT_MAX_TARGET_FILES}`;
|
|
5268
|
+
throw new Error(
|
|
5269
|
+
`[collab_debug] Target has ${allFiles.length} files, which exceeds the limit (${hint}). Narrow the target or pass maxTargetFiles / contextWindow to override. For large codebases, run package-by-package or module-by-module sessions instead of targeting the entire repo.`
|
|
5270
|
+
);
|
|
5271
|
+
}
|
|
5272
|
+
for (const filePath of allFiles) {
|
|
4964
5273
|
try {
|
|
4965
|
-
const content = await
|
|
5274
|
+
const content = await fsp6.readFile(filePath, "utf8");
|
|
4966
5275
|
const ext = filePath.split(".").pop() ?? "";
|
|
4967
5276
|
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
4968
5277
|
this.snapshot.files.push({ path: filePath, content, language });
|
|
@@ -5016,7 +5325,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
5016
5325
|
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
5017
5326
|
}, this.timeoutMs);
|
|
5018
5327
|
});
|
|
5019
|
-
let results;
|
|
5328
|
+
let results = null;
|
|
5020
5329
|
try {
|
|
5021
5330
|
results = await Promise.race([
|
|
5022
5331
|
Promise.all([
|
|
@@ -5447,7 +5756,7 @@ var FleetContextOverflowError = class extends Error {
|
|
|
5447
5756
|
this.observed = observed;
|
|
5448
5757
|
}
|
|
5449
5758
|
};
|
|
5450
|
-
var Director = class {
|
|
5759
|
+
var Director = class _Director {
|
|
5451
5760
|
/** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
|
|
5452
5761
|
get coordinatorId() {
|
|
5453
5762
|
return this.id;
|
|
@@ -5505,6 +5814,8 @@ var Director = class {
|
|
|
5505
5814
|
* coordinator already fired the event — `awaitTasks(['t-1'])` after
|
|
5506
5815
|
* t-1 finished should resolve immediately, not hang. */
|
|
5507
5816
|
completed = /* @__PURE__ */ new Map();
|
|
5817
|
+
/** Prevents the completed Map from growing unbounded in long-running directors. */
|
|
5818
|
+
static MAX_COMPLETED = 1e4;
|
|
5508
5819
|
/** Per-subagent provider/model metadata, captured at spawn time so the
|
|
5509
5820
|
* FleetUsageAggregator's metaLookup can surface readable rows. */
|
|
5510
5821
|
subagentMeta = /* @__PURE__ */ new Map();
|
|
@@ -5584,6 +5895,8 @@ var Director = class {
|
|
|
5584
5895
|
_leaderBtwNotes = [];
|
|
5585
5896
|
/** Active collab sessions tracked by sessionId (see spawnCollab). */
|
|
5586
5897
|
_activeCollabSessions = /* @__PURE__ */ new Map();
|
|
5898
|
+
/** Prevents large `ask_subagent` answers from bloating the leader's context window. */
|
|
5899
|
+
largeAnswerStore;
|
|
5587
5900
|
constructor(opts) {
|
|
5588
5901
|
this.id = opts.config.coordinatorId || randomUUID();
|
|
5589
5902
|
this.manifestPath = opts.manifestPath;
|
|
@@ -5612,7 +5925,7 @@ var Director = class {
|
|
|
5612
5925
|
}, opts.checkpointDebounceMs ?? 250) : null;
|
|
5613
5926
|
this.fleetManager = opts.fleetManager;
|
|
5614
5927
|
if (this.sharedScratchpadPath) {
|
|
5615
|
-
void
|
|
5928
|
+
void fsp6.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
5616
5929
|
(err) => this.logShutdownError("shared_scratchpad_mkdir", err)
|
|
5617
5930
|
);
|
|
5618
5931
|
}
|
|
@@ -5644,6 +5957,11 @@ var Director = class {
|
|
|
5644
5957
|
this.taskCompletedListener = (payload) => {
|
|
5645
5958
|
const r = payload.result;
|
|
5646
5959
|
this.completed.set(r.taskId, r);
|
|
5960
|
+
if (this.completed.size > _Director.MAX_COMPLETED) {
|
|
5961
|
+
const toDelete = this.completed.size - _Director.MAX_COMPLETED;
|
|
5962
|
+
const keys = [...this.completed.keys()].slice(0, toDelete);
|
|
5963
|
+
for (const k of keys) this.completed.delete(k);
|
|
5964
|
+
}
|
|
5647
5965
|
const waiter = this.taskWaiters.get(r.taskId);
|
|
5648
5966
|
if (waiter) {
|
|
5649
5967
|
waiter.resolve(r);
|
|
@@ -5750,6 +6068,7 @@ var Director = class {
|
|
|
5750
6068
|
payload.extend(extra);
|
|
5751
6069
|
});
|
|
5752
6070
|
});
|
|
6071
|
+
this.largeAnswerStore = new LargeAnswerStore(2e3);
|
|
5753
6072
|
}
|
|
5754
6073
|
/**
|
|
5755
6074
|
* Record a granted budget extension and broadcast it on the FleetBus so
|
|
@@ -5976,6 +6295,20 @@ var Director = class {
|
|
|
5976
6295
|
);
|
|
5977
6296
|
this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
|
|
5978
6297
|
this.subagentBridges.set(result.subagentId, subagentBridge);
|
|
6298
|
+
this.fleet.emit({
|
|
6299
|
+
subagentId: result.subagentId,
|
|
6300
|
+
ts: Date.now(),
|
|
6301
|
+
type: "subagent.spawned",
|
|
6302
|
+
payload: {
|
|
6303
|
+
subagentId: result.subagentId,
|
|
6304
|
+
taskId: "",
|
|
6305
|
+
// taskId will be set when assign() is called
|
|
6306
|
+
name: config.name,
|
|
6307
|
+
role: config.role,
|
|
6308
|
+
provider: config.provider,
|
|
6309
|
+
model: config.model
|
|
6310
|
+
}
|
|
6311
|
+
});
|
|
5979
6312
|
if (!this.fleetManager) {
|
|
5980
6313
|
this.manifestEntries.set(result.subagentId, {
|
|
5981
6314
|
subagentId: result.subagentId,
|
|
@@ -6115,7 +6448,7 @@ var Director = class {
|
|
|
6115
6448
|
})),
|
|
6116
6449
|
usage: this.usage.snapshot()
|
|
6117
6450
|
};
|
|
6118
|
-
await
|
|
6451
|
+
await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
6119
6452
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
6120
6453
|
return this.manifestPath;
|
|
6121
6454
|
}
|
|
@@ -6230,11 +6563,11 @@ var Director = class {
|
|
|
6230
6563
|
if (cached) return cached;
|
|
6231
6564
|
const existing = this.taskWaiters.get(id);
|
|
6232
6565
|
if (existing) return existing.promise;
|
|
6233
|
-
let
|
|
6566
|
+
let resolve2;
|
|
6234
6567
|
const promise = new Promise((res) => {
|
|
6235
|
-
|
|
6568
|
+
resolve2 = res;
|
|
6236
6569
|
});
|
|
6237
|
-
this.taskWaiters.set(id, { promise, resolve });
|
|
6570
|
+
this.taskWaiters.set(id, { promise, resolve: resolve2 });
|
|
6238
6571
|
return promise;
|
|
6239
6572
|
})
|
|
6240
6573
|
);
|
|
@@ -6247,7 +6580,24 @@ var Director = class {
|
|
|
6247
6580
|
}
|
|
6248
6581
|
async remove(subagentId) {
|
|
6249
6582
|
await this.coordinator.remove(subagentId);
|
|
6583
|
+
const bridge = this.subagentBridges.get(subagentId);
|
|
6584
|
+
if (bridge) {
|
|
6585
|
+
await bridge.stop();
|
|
6586
|
+
this.subagentBridges.delete(subagentId);
|
|
6587
|
+
}
|
|
6250
6588
|
this.usage.removeSubagent(subagentId);
|
|
6589
|
+
if (this.fleetManager) {
|
|
6590
|
+
this.fleetManager.removeSubagent(subagentId);
|
|
6591
|
+
} else {
|
|
6592
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
6593
|
+
if (entry?.name) {
|
|
6594
|
+
const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
6595
|
+
this._usedNicknames.delete(nicknameKey);
|
|
6596
|
+
}
|
|
6597
|
+
}
|
|
6598
|
+
this.manifestEntries.delete(subagentId);
|
|
6599
|
+
this.taskOwners.delete(subagentId);
|
|
6600
|
+
this.taskDescriptions.delete(subagentId);
|
|
6251
6601
|
}
|
|
6252
6602
|
status() {
|
|
6253
6603
|
const base = this.coordinator.getStatus();
|
|
@@ -6303,7 +6653,7 @@ var Director = class {
|
|
|
6303
6653
|
const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
6304
6654
|
let raw;
|
|
6305
6655
|
try {
|
|
6306
|
-
raw = await
|
|
6656
|
+
raw = await fsp6.readFile(filePath, "utf8");
|
|
6307
6657
|
} catch {
|
|
6308
6658
|
return null;
|
|
6309
6659
|
}
|
|
@@ -6409,6 +6759,7 @@ var Director = class {
|
|
|
6409
6759
|
makeAssignTool(this),
|
|
6410
6760
|
makeAwaitTasksTool(this),
|
|
6411
6761
|
makeAskTool(this),
|
|
6762
|
+
makeAskResultTool(this),
|
|
6412
6763
|
makeRollUpTool(this),
|
|
6413
6764
|
makeTerminateTool(this),
|
|
6414
6765
|
makeTerminateAllTool(this),
|
|
@@ -6491,15 +6842,33 @@ function createDelegateTool(opts) {
|
|
|
6491
6842
|
},
|
|
6492
6843
|
timeoutMs: {
|
|
6493
6844
|
type: "number",
|
|
6845
|
+
minimum: 1,
|
|
6494
6846
|
description: `Wall-clock budget for this delegate in milliseconds. No hard cap \u2014 set as high as the task realistically needs (a monorepo audit can take hours, a single-file lint takes seconds). Default ${Math.round(defaultTimeoutMs / 1e3 / 60)} minutes.`
|
|
6495
6847
|
},
|
|
6496
6848
|
maxIterations: {
|
|
6497
6849
|
type: "number",
|
|
6850
|
+
minimum: 1,
|
|
6498
6851
|
description: "Maximum LLM iterations the subagent may take. Unset = use the role/coordinator default. Raise this for tasks with many tool-think-tool cycles (deep code analysis, multi-file refactors)."
|
|
6499
6852
|
},
|
|
6500
6853
|
maxToolCalls: {
|
|
6501
6854
|
type: "number",
|
|
6855
|
+
minimum: 1,
|
|
6502
6856
|
description: "Maximum number of tool invocations the subagent may make. Unset = use the role/coordinator default. Raise this for tasks that touch many files (large grep + read + report)."
|
|
6857
|
+
},
|
|
6858
|
+
idleTimeoutMs: {
|
|
6859
|
+
type: "number",
|
|
6860
|
+
minimum: 1,
|
|
6861
|
+
description: "Idle timeout in ms: reap the subagent after this long with no activity. Resets on every iteration/tool call. Unset = use the role/coordinator default."
|
|
6862
|
+
},
|
|
6863
|
+
maxTokens: {
|
|
6864
|
+
type: "number",
|
|
6865
|
+
minimum: 1,
|
|
6866
|
+
description: "Maximum total tokens (input + output) the subagent may use. Unset = use the role/coordinator default."
|
|
6867
|
+
},
|
|
6868
|
+
maxCostUsd: {
|
|
6869
|
+
type: "number",
|
|
6870
|
+
minimum: 0,
|
|
6871
|
+
description: "Maximum estimated USD cost the subagent may incur. Unset = use the role/coordinator default."
|
|
6503
6872
|
}
|
|
6504
6873
|
},
|
|
6505
6874
|
required: ["task"]
|
|
@@ -6557,12 +6926,21 @@ function createDelegateTool(opts) {
|
|
|
6557
6926
|
};
|
|
6558
6927
|
cfg = applyRosterBudget({ ...cfg, name: i.name });
|
|
6559
6928
|
}
|
|
6560
|
-
if (typeof i.maxIterations === "number"
|
|
6929
|
+
if (typeof i.maxIterations === "number") {
|
|
6561
6930
|
cfg.maxIterations = i.maxIterations;
|
|
6562
6931
|
}
|
|
6563
|
-
if (typeof i.maxToolCalls === "number"
|
|
6932
|
+
if (typeof i.maxToolCalls === "number") {
|
|
6564
6933
|
cfg.maxToolCalls = i.maxToolCalls;
|
|
6565
6934
|
}
|
|
6935
|
+
if (typeof i.idleTimeoutMs === "number") {
|
|
6936
|
+
cfg.idleTimeoutMs = i.idleTimeoutMs;
|
|
6937
|
+
}
|
|
6938
|
+
if (typeof i.maxTokens === "number") {
|
|
6939
|
+
cfg.maxTokens = i.maxTokens;
|
|
6940
|
+
}
|
|
6941
|
+
if (typeof i.maxCostUsd === "number") {
|
|
6942
|
+
cfg.maxCostUsd = i.maxCostUsd;
|
|
6943
|
+
}
|
|
6566
6944
|
const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ?? 6e4;
|
|
6567
6945
|
if (!cfg.timeoutMs) {
|
|
6568
6946
|
cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
@@ -6574,7 +6952,7 @@ function createDelegateTool(opts) {
|
|
|
6574
6952
|
subagentId
|
|
6575
6953
|
});
|
|
6576
6954
|
const dir = director;
|
|
6577
|
-
const result = await new Promise((
|
|
6955
|
+
const result = await new Promise((resolve2) => {
|
|
6578
6956
|
let settled = false;
|
|
6579
6957
|
let timer;
|
|
6580
6958
|
const finish = (value) => {
|
|
@@ -6584,7 +6962,7 @@ function createDelegateTool(opts) {
|
|
|
6584
6962
|
offTool();
|
|
6585
6963
|
offIter();
|
|
6586
6964
|
offProgress();
|
|
6587
|
-
|
|
6965
|
+
resolve2(value);
|
|
6588
6966
|
};
|
|
6589
6967
|
const arm = () => {
|
|
6590
6968
|
if (timer) clearTimeout(timer);
|
|
@@ -6731,7 +7109,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
6731
7109
|
candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
6732
7110
|
} else {
|
|
6733
7111
|
try {
|
|
6734
|
-
const entries = await
|
|
7112
|
+
const entries = await fsp6.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
6735
7113
|
for (const entry of entries) {
|
|
6736
7114
|
if (entry.isDirectory()) {
|
|
6737
7115
|
candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
@@ -6744,7 +7122,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
6744
7122
|
for (const file of candidates) {
|
|
6745
7123
|
let raw;
|
|
6746
7124
|
try {
|
|
6747
|
-
raw = await
|
|
7125
|
+
raw = await fsp6.readFile(file, "utf8");
|
|
6748
7126
|
} catch {
|
|
6749
7127
|
continue;
|
|
6750
7128
|
}
|
|
@@ -7063,7 +7441,7 @@ var DefaultSessionStore = class {
|
|
|
7063
7441
|
const file = path4.join(shardDir, `${id}.jsonl`);
|
|
7064
7442
|
let handle;
|
|
7065
7443
|
try {
|
|
7066
|
-
handle = await
|
|
7444
|
+
handle = await fsp6.open(file, "a", 384);
|
|
7067
7445
|
} catch (err) {
|
|
7068
7446
|
throw new Error(
|
|
7069
7447
|
`Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -7087,7 +7465,7 @@ var DefaultSessionStore = class {
|
|
|
7087
7465
|
const data = await this.load(id);
|
|
7088
7466
|
let handle;
|
|
7089
7467
|
try {
|
|
7090
|
-
handle = await
|
|
7468
|
+
handle = await fsp6.open(file, "a", 384);
|
|
7091
7469
|
} catch (err) {
|
|
7092
7470
|
throw new Error(
|
|
7093
7471
|
`Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -7116,7 +7494,7 @@ var DefaultSessionStore = class {
|
|
|
7116
7494
|
}
|
|
7117
7495
|
async load(id) {
|
|
7118
7496
|
const file = this.sessionPath(id, ".jsonl");
|
|
7119
|
-
const raw = await
|
|
7497
|
+
const raw = await fsp6.readFile(file, "utf8");
|
|
7120
7498
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
7121
7499
|
const events = [];
|
|
7122
7500
|
for (const line of lines) {
|
|
@@ -7151,7 +7529,7 @@ var DefaultSessionStore = class {
|
|
|
7151
7529
|
/** Recursively collect all session IDs from shard subdirectories. */
|
|
7152
7530
|
async collectSessionIds(dir) {
|
|
7153
7531
|
const ids = [];
|
|
7154
|
-
const entries = await
|
|
7532
|
+
const entries = await fsp6.readdir(dir, { withFileTypes: true });
|
|
7155
7533
|
for (const entry of entries) {
|
|
7156
7534
|
const full = path4.join(dir, entry.name);
|
|
7157
7535
|
if (entry.isDirectory()) {
|
|
@@ -7165,12 +7543,12 @@ var DefaultSessionStore = class {
|
|
|
7165
7543
|
async summaryFor(id) {
|
|
7166
7544
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
7167
7545
|
try {
|
|
7168
|
-
const raw = await
|
|
7546
|
+
const raw = await fsp6.readFile(manifest, "utf8");
|
|
7169
7547
|
return JSON.parse(raw);
|
|
7170
7548
|
} catch {
|
|
7171
7549
|
const full = this.sessionPath(id, ".jsonl");
|
|
7172
|
-
const
|
|
7173
|
-
const summary = await this.summarize(id,
|
|
7550
|
+
const stat4 = await fsp6.stat(full);
|
|
7551
|
+
const summary = await this.summarize(id, stat4.mtime.toISOString());
|
|
7174
7552
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
7175
7553
|
console.warn(
|
|
7176
7554
|
`[session-store] Failed to write manifest for "${id}":`,
|
|
@@ -7181,8 +7559,8 @@ var DefaultSessionStore = class {
|
|
|
7181
7559
|
}
|
|
7182
7560
|
}
|
|
7183
7561
|
async delete(id) {
|
|
7184
|
-
await
|
|
7185
|
-
await
|
|
7562
|
+
await fsp6.unlink(this.sessionPath(id, ".jsonl"));
|
|
7563
|
+
await fsp6.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
|
|
7186
7564
|
}
|
|
7187
7565
|
async clearHistory(id) {
|
|
7188
7566
|
await this.ensureShardDir(id);
|
|
@@ -7196,8 +7574,8 @@ var DefaultSessionStore = class {
|
|
|
7196
7574
|
provider: "unknown"
|
|
7197
7575
|
})}
|
|
7198
7576
|
`;
|
|
7199
|
-
await
|
|
7200
|
-
await
|
|
7577
|
+
await fsp6.writeFile(file, record, "utf8");
|
|
7578
|
+
await fsp6.unlink(meta).catch(() => void 0);
|
|
7201
7579
|
}
|
|
7202
7580
|
async summarize(id, mtime) {
|
|
7203
7581
|
try {
|
|
@@ -7383,7 +7761,7 @@ var FileSessionWriter = class {
|
|
|
7383
7761
|
`;
|
|
7384
7762
|
try {
|
|
7385
7763
|
if (this.filePath) {
|
|
7386
|
-
await
|
|
7764
|
+
await fsp6.writeFile(this.filePath, record, { flag: "a", mode: 384 });
|
|
7387
7765
|
}
|
|
7388
7766
|
} catch {
|
|
7389
7767
|
}
|
|
@@ -7476,7 +7854,7 @@ var FileSessionWriter = class {
|
|
|
7476
7854
|
}
|
|
7477
7855
|
async truncateToCheckpoint(targetPromptIndex) {
|
|
7478
7856
|
if (!this.filePath) return 0;
|
|
7479
|
-
const raw = await
|
|
7857
|
+
const raw = await fsp6.readFile(this.filePath, "utf8");
|
|
7480
7858
|
const lines = raw.split("\n");
|
|
7481
7859
|
const kept = [];
|
|
7482
7860
|
let removedCount = 0;
|
|
@@ -7514,13 +7892,13 @@ var FileSessionWriter = class {
|
|
|
7514
7892
|
}
|
|
7515
7893
|
const truncated = kept.join("\n");
|
|
7516
7894
|
const tmpPath = `${this.filePath}.rewind.tmp`;
|
|
7517
|
-
await
|
|
7895
|
+
await fsp6.writeFile(tmpPath, truncated + "\n", "utf8");
|
|
7518
7896
|
try {
|
|
7519
7897
|
await this.handle.close();
|
|
7520
|
-
await
|
|
7521
|
-
this.handle = await
|
|
7898
|
+
await fsp6.rename(tmpPath, this.filePath);
|
|
7899
|
+
this.handle = await fsp6.open(this.filePath, "a", 384);
|
|
7522
7900
|
} catch (err) {
|
|
7523
|
-
await
|
|
7901
|
+
await fsp6.unlink(tmpPath).catch(() => void 0);
|
|
7524
7902
|
throw err;
|
|
7525
7903
|
}
|
|
7526
7904
|
await this.append({
|
|
@@ -7546,7 +7924,7 @@ var FileSessionWriter = class {
|
|
|
7546
7924
|
provider: this.meta.provider ?? "unknown"
|
|
7547
7925
|
})}
|
|
7548
7926
|
`;
|
|
7549
|
-
await
|
|
7927
|
+
await fsp6.writeFile(this.filePath, record, "utf8");
|
|
7550
7928
|
}
|
|
7551
7929
|
/**
|
|
7552
7930
|
* Idea #1 — write an in-flight marker. The agent loop should call
|
|
@@ -7892,7 +8270,7 @@ var FleetManager = class {
|
|
|
7892
8270
|
})),
|
|
7893
8271
|
usage: this.usage.snapshot()
|
|
7894
8272
|
};
|
|
7895
|
-
await
|
|
8273
|
+
await fsp6.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
7896
8274
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
7897
8275
|
return this.manifestPath;
|
|
7898
8276
|
}
|
|
@@ -8002,6 +8380,23 @@ var FleetManager = class {
|
|
|
8002
8380
|
}));
|
|
8003
8381
|
return { pending, live: [] };
|
|
8004
8382
|
}
|
|
8383
|
+
/**
|
|
8384
|
+
* Clean up all fleet-manager state associated with a removed subagent:
|
|
8385
|
+
* - Frees the nickname slot so the same name can be reused
|
|
8386
|
+
* - Removes any pending tasks for this subagent
|
|
8387
|
+
*/
|
|
8388
|
+
removeSubagent(subagentId) {
|
|
8389
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
8390
|
+
if (entry?.name) {
|
|
8391
|
+
const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
8392
|
+
this._usedNicknames.delete(nicknameKey);
|
|
8393
|
+
}
|
|
8394
|
+
for (const [taskId, task] of this.pendingTasks) {
|
|
8395
|
+
if (task.subagentId === subagentId) {
|
|
8396
|
+
this.pendingTasks.delete(taskId);
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
}
|
|
8005
8400
|
/** Release all resources: clear the manifest debounce timer and dispose the usage aggregator. */
|
|
8006
8401
|
dispose() {
|
|
8007
8402
|
if (this.manifestTimer) {
|
|
@@ -8012,6 +8407,6 @@ var FleetManager = class {
|
|
|
8012
8407
|
}
|
|
8013
8408
|
};
|
|
8014
8409
|
|
|
8015
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, CollabSession, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, rosterSummaryFromConfigs, scoreAgents };
|
|
8410
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, CollabSession, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, rosterSummaryFromConfigs, scoreAgents };
|
|
8016
8411
|
//# sourceMappingURL=index.js.map
|
|
8017
8412
|
//# sourceMappingURL=index.js.map
|