@wrongstack/core 0.275.1 → 0.276.2
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/{agent-bridge-D9JkPvJ0.d.ts → agent-bridge-D7A-eu3C.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CArSFKFl.d.ts → agent-subagent-runner-CEuw4ATz.d.ts} +16 -10
- package/dist/{brain-DCkB5_e7.d.ts → brain-BLOyN5ZP.d.ts} +127 -1
- package/dist/{compactor-CzSvxM1g.d.ts → compactor-DcBpaJsI.d.ts} +1 -1
- package/dist/{config-BzFRKkg7.d.ts → config-Bf5mj-ad.d.ts} +20 -2
- package/dist/{context-BrLe8pJy.d.ts → context-CLnUMW5g.d.ts} +40 -2
- package/dist/coordination/index.d.ts +43 -24
- package/dist/coordination/index.js +849 -648
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +1636 -845
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +218 -49
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +7 -7
- package/dist/extension/index.js.map +1 -1
- package/dist/{global-mailbox-CXkugtNQ.d.ts → global-mailbox-Iqfkgmwu.d.ts} +3 -3
- package/dist/{goal-store-DUwdbdoY.d.ts → goal-store-DGb6b5Ed.d.ts} +1 -1
- package/dist/hq/index.d.ts +6 -6
- package/dist/hq/index.js +178 -75
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-CtlizLTK.d.ts → index-Cn0NOshr.d.ts} +10 -5
- package/dist/{index-neOCEy6q.d.ts → index-L4RZN9jJ.d.ts} +2 -2
- package/dist/index.d.ts +56 -48
- package/dist/index.js +2789 -1546
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +26 -7
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +20 -12
- package/dist/kernel/index.js +55 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mailbox-types-_7gaY0Rl.d.ts → mailbox-types-DTl7bRH3.d.ts} +3 -1
- package/dist/{mcp-servers-MLL6bMlv.d.ts → mcp-servers-CuZGf9fI.d.ts} +4 -4
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +223 -139
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-CrkcxQ-g.d.ts → models-registry-8XOdxWQu.d.ts} +16 -1
- package/dist/{multi-agent-coordinator-Dc_HuG9p.d.ts → multi-agent-coordinator-CiRtKVTk.d.ts} +8 -1
- package/dist/{null-fleet-bus-BMZwMin7.d.ts → null-fleet-bus-d9G-bVy9.d.ts} +26 -22
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-uVK4BatM.d.ts → path-resolver-BhIb6mtd.d.ts} +8 -3
- package/dist/{permission-CJR1qfOi.d.ts → permission-BCbQDR2s.d.ts} +1 -1
- package/dist/{permission-policy-DLVKKk4w.d.ts → permission-policy-C0ikndX_.d.ts} +2 -18
- package/dist/{pipeline-BYR-Vdau.d.ts → pipeline-Dl6XbfE7.d.ts} +10 -6
- package/dist/{provider-model-resolve-iREK_1lG.d.ts → provider-model-resolve-B70epO19.d.ts} +3 -3
- package/dist/{provider-runner-i7SQXZuC.d.ts → provider-runner-DZ808MSM.d.ts} +3 -3
- package/dist/{retry-policy-BmY5ooh3.d.ts → retry-policy-Dt3_z8Aj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +19 -10
- package/dist/sdd/index.js +411 -240
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-C9leEMzr.d.ts → secret-vault-BUJ2d1gB.d.ts} +1 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +30 -6
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-qjpee9BF.d.ts → selector-BCkWgdwy.d.ts} +1 -1
- package/dist/{session-event-bridge-m7y--I-H.d.ts → session-event-bridge-CMvIO59_.d.ts} +1 -1
- package/dist/{session-reader-BjLH4V9n.d.ts → session-reader-C8aiChUu.d.ts} +1 -1
- package/dist/skills/index.js +1 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +68 -30
- package/dist/storage/index.js +839 -528
- package/dist/storage/index.js.map +1 -1
- package/dist/{strategy-compactor-C2bmlWYg.d.ts → strategy-compactor-DI1OHVbB.d.ts} +10 -10
- package/dist/{todos-checkpoint-oDS9IBNS.d.ts → todos-checkpoint-Ddd2CGr0.d.ts} +56 -9
- package/dist/{tool-executor-D4YdaJ-M.d.ts → tool-executor-Bmd5Ygoo.d.ts} +45 -10
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -20
- package/dist/types/index.js +331 -98
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +16 -3
- package/dist/utils/index.js +159 -83
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-A1Efnvs0.d.ts → worktree-manager-DBdl_5rs.d.ts} +4 -1
- package/instructions/agents/shadow-agent.md +3 -3
- package/instructions/coordination/director-preamble.md +3 -3
- package/instructions/modes/research-web.md +4 -4
- package/package.json +1 -1
- package/skills/research-web/SKILL.md +26 -26
- package/skills/research-web/SKILL.save.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
2
2
|
import * as fsp7 from 'fs/promises';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path9 from 'path';
|
|
4
4
|
import { isAbsolute, resolve } from 'path';
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import { hostname } from 'os';
|
|
@@ -19,10 +19,10 @@ var ObservableBrainArbiter = class {
|
|
|
19
19
|
inner;
|
|
20
20
|
events;
|
|
21
21
|
async decide(request) {
|
|
22
|
-
this.events.emit("brain.decision_requested", { request, at: Date.now() });
|
|
22
|
+
this.events.emit("brain.decision_requested", { sessionId: request.sessionId, request, at: Date.now() });
|
|
23
23
|
const decision = await this.inner.decide(request);
|
|
24
24
|
const event = decision.type === "ask_human" ? "brain.decision_ask_human" : decision.type === "deny" ? "brain.decision_denied" : "brain.decision_answered";
|
|
25
|
-
this.events.emit(event, { request, decision, at: Date.now() });
|
|
25
|
+
this.events.emit(event, { sessionId: request.sessionId, request, decision, at: Date.now() });
|
|
26
26
|
return decision;
|
|
27
27
|
}
|
|
28
28
|
};
|
|
@@ -59,17 +59,17 @@ var BrainDecisionQueue = class {
|
|
|
59
59
|
options: request.options,
|
|
60
60
|
rationale: "Decision escalated to human authority."
|
|
61
61
|
};
|
|
62
|
-
const pending = new Promise((
|
|
63
|
-
const entry = { request, resolve:
|
|
62
|
+
const pending = new Promise((resolve6) => {
|
|
63
|
+
const entry = { request, resolve: resolve6 };
|
|
64
64
|
if (this.opts.timeoutMs && this.opts.timeoutMs > 0) {
|
|
65
65
|
entry.timer = setTimeout(() => {
|
|
66
66
|
this.pending.delete(request.id);
|
|
67
|
-
|
|
67
|
+
resolve6({ type: "deny", reason: "Brain human decision timed out." });
|
|
68
68
|
}, this.opts.timeoutMs);
|
|
69
69
|
}
|
|
70
70
|
this.pending.set(request.id, entry);
|
|
71
71
|
});
|
|
72
|
-
this.events.emit("brain.decision_ask_human", { request, decision: ask, at: Date.now() });
|
|
72
|
+
this.events.emit("brain.decision_ask_human", { sessionId: request.sessionId, request, decision: ask, at: Date.now() });
|
|
73
73
|
return pending;
|
|
74
74
|
}
|
|
75
75
|
dispose() {
|
|
@@ -155,308 +155,10 @@ function formatHumanPrompt(request) {
|
|
|
155
155
|
lines.push("", `Risk: ${request.risk}`);
|
|
156
156
|
return lines.join("\n");
|
|
157
157
|
}
|
|
158
|
-
async function atomicWrite(targetPath, content, opts = {}) {
|
|
159
|
-
const dir = path8.dirname(targetPath);
|
|
160
|
-
await fsp7.mkdir(dir, { recursive: true });
|
|
161
|
-
const tmp = path8.join(dir, `.${path8.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
162
|
-
try {
|
|
163
|
-
if (typeof content === "string") {
|
|
164
|
-
await fsp7.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
165
|
-
} else {
|
|
166
|
-
await fsp7.writeFile(tmp, content, { flag: "wx" });
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
const fh = await fsp7.open(tmp, "r+");
|
|
170
|
-
try {
|
|
171
|
-
await fh.sync();
|
|
172
|
-
} finally {
|
|
173
|
-
await fh.close();
|
|
174
|
-
}
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
let mode;
|
|
178
|
-
try {
|
|
179
|
-
const stat7 = await fsp7.stat(targetPath);
|
|
180
|
-
mode = stat7.mode & 511;
|
|
181
|
-
} catch {
|
|
182
|
-
mode = opts.mode;
|
|
183
|
-
}
|
|
184
|
-
if (mode !== void 0) {
|
|
185
|
-
await fsp7.chmod(tmp, mode);
|
|
186
|
-
}
|
|
187
|
-
await renameWithRetry(tmp, targetPath);
|
|
188
|
-
if (mode !== void 0 && process.platform === "win32") {
|
|
189
|
-
try {
|
|
190
|
-
await fsp7.chmod(targetPath, mode);
|
|
191
|
-
} catch {
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} catch (err) {
|
|
195
|
-
try {
|
|
196
|
-
await fsp7.unlink(tmp);
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
throw err;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async function ensureDir(dir) {
|
|
203
|
-
await fsp7.mkdir(dir, { recursive: true });
|
|
204
|
-
}
|
|
205
|
-
async function withFileLock(targetPath, fn, opts = {}) {
|
|
206
|
-
const dir = path8.dirname(targetPath);
|
|
207
|
-
await fsp7.mkdir(dir, { recursive: true });
|
|
208
|
-
const lockPath = path8.join(dir, `.${path8.basename(targetPath)}.lock`);
|
|
209
|
-
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
210
|
-
const staleMs = opts.staleMs ?? 3e4;
|
|
211
|
-
const started = Date.now();
|
|
212
|
-
let handle;
|
|
213
|
-
for (; ; ) {
|
|
214
|
-
try {
|
|
215
|
-
handle = await fsp7.open(lockPath, "wx");
|
|
216
|
-
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
217
|
-
break;
|
|
218
|
-
} catch (err) {
|
|
219
|
-
const code = err.code;
|
|
220
|
-
if (code === "ENOENT") {
|
|
221
|
-
await fsp7.mkdir(dir, { recursive: true });
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (code !== "EEXIST") throw err;
|
|
225
|
-
try {
|
|
226
|
-
const stat7 = await fsp7.stat(lockPath);
|
|
227
|
-
if (Date.now() - stat7.mtimeMs > staleMs) {
|
|
228
|
-
await fsp7.unlink(lockPath);
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (Date.now() - started >= timeoutMs) {
|
|
235
|
-
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
236
|
-
}
|
|
237
|
-
await new Promise((resolve5) => setTimeout(resolve5, 25));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
try {
|
|
241
|
-
return await fn();
|
|
242
|
-
} finally {
|
|
243
|
-
try {
|
|
244
|
-
await handle?.close();
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
try {
|
|
248
|
-
await fsp7.unlink(lockPath);
|
|
249
|
-
} catch {
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
254
|
-
async function renameWithRetry(from, to) {
|
|
255
|
-
if (process.platform !== "win32") {
|
|
256
|
-
await fsp7.rename(from, to);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const delays = [10, 25, 60, 120, 250];
|
|
260
|
-
let lastErr;
|
|
261
|
-
for (let i = 0; i <= delays.length; i++) {
|
|
262
|
-
try {
|
|
263
|
-
await fsp7.rename(from, to);
|
|
264
|
-
return;
|
|
265
|
-
} catch (err) {
|
|
266
|
-
lastErr = err;
|
|
267
|
-
const code = err?.code;
|
|
268
|
-
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
269
|
-
throw err;
|
|
270
|
-
}
|
|
271
|
-
await new Promise((resolve5) => setTimeout(resolve5, delays[i]));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
throw lastErr;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/utils/error.ts
|
|
278
|
-
function toErrorMessage(err) {
|
|
279
|
-
return err instanceof Error ? err.message : String(err);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// src/storage/director-state.ts
|
|
283
|
-
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
284
|
-
let existing;
|
|
285
|
-
try {
|
|
286
|
-
existing = await fsp7.readFile(lockPath, "utf8");
|
|
287
|
-
} catch {
|
|
288
|
-
}
|
|
289
|
-
if (existing) {
|
|
290
|
-
try {
|
|
291
|
-
const lock2 = JSON.parse(existing);
|
|
292
|
-
try {
|
|
293
|
-
process.kill(lock2.pid, 0);
|
|
294
|
-
return false;
|
|
295
|
-
} catch {
|
|
296
|
-
}
|
|
297
|
-
} catch {
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
const lock = {
|
|
301
|
-
pid: processId,
|
|
302
|
-
hostname: hostname(),
|
|
303
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
|
-
};
|
|
305
|
-
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
async function releaseDirectorStateLock(lockPath) {
|
|
309
|
-
try {
|
|
310
|
-
await fsp7.unlink(lockPath);
|
|
311
|
-
} catch {
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
var DirectorStateCheckpoint = class {
|
|
315
|
-
snapshot;
|
|
316
|
-
filePath;
|
|
317
|
-
lockPath;
|
|
318
|
-
timer = null;
|
|
319
|
-
debounceMs;
|
|
320
|
-
writing = false;
|
|
321
|
-
rewriteRequested = false;
|
|
322
|
-
constructor(filePath, init, debounceMs = 250) {
|
|
323
|
-
this.filePath = filePath;
|
|
324
|
-
this.lockPath = `${filePath}.lock`;
|
|
325
|
-
this.debounceMs = debounceMs;
|
|
326
|
-
this.snapshot = {
|
|
327
|
-
version: 1,
|
|
328
|
-
directorRunId: init.directorRunId,
|
|
329
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
330
|
-
spawnCount: 0,
|
|
331
|
-
maxSpawns: init.maxSpawns,
|
|
332
|
-
spawnDepth: init.spawnDepth,
|
|
333
|
-
maxSpawnDepth: init.maxSpawnDepth,
|
|
334
|
-
directorBudget: init.directorBudget,
|
|
335
|
-
subagents: [],
|
|
336
|
-
tasks: []
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Attempt to acquire the lock for this checkpoint. Call this before
|
|
341
|
-
* resuming a crashed director run. If it returns false, another
|
|
342
|
-
* director process is still running this fleet — do not resume.
|
|
343
|
-
*/
|
|
344
|
-
async acquireLock() {
|
|
345
|
-
return acquireDirectorStateLock(this.lockPath);
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Release the lock on graceful shutdown. Call `flush()` first to ensure
|
|
349
|
-
* the final checkpoint state is on disk before removing the lock.
|
|
350
|
-
* Without this, the next resume will see a stale-lock and refuse.
|
|
351
|
-
*/
|
|
352
|
-
async releaseLock() {
|
|
353
|
-
return releaseDirectorStateLock(this.lockPath);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Resume from a snapshot previously loaded via `loadDirectorState()`.
|
|
357
|
-
* Use this when `--resume <runId>` is triggered — the snapshot has
|
|
358
|
-
* the full fleet state (subagents, tasks) from before the crash; the
|
|
359
|
-
* checkpoint continues from there.
|
|
360
|
-
*/
|
|
361
|
-
resume(snapshot) {
|
|
362
|
-
this.snapshot = snapshot;
|
|
363
|
-
}
|
|
364
|
-
current() {
|
|
365
|
-
return this.snapshot;
|
|
366
|
-
}
|
|
367
|
-
recordSpawn(sub, spawnCount) {
|
|
368
|
-
this.snapshot = {
|
|
369
|
-
...this.snapshot,
|
|
370
|
-
spawnCount,
|
|
371
|
-
subagents: [...this.snapshot.subagents.filter((s) => s.id !== sub.id), sub]
|
|
372
|
-
};
|
|
373
|
-
this.bumpUpdatedAt();
|
|
374
|
-
this.schedule();
|
|
375
|
-
}
|
|
376
|
-
recordTaskAssigned(task) {
|
|
377
|
-
const exists = this.snapshot.tasks.some((t) => t.taskId === task.taskId);
|
|
378
|
-
this.snapshot = {
|
|
379
|
-
...this.snapshot,
|
|
380
|
-
tasks: exists ? this.snapshot.tasks.map((t) => t.taskId === task.taskId ? { ...t, ...task } : t) : [...this.snapshot.tasks, task]
|
|
381
|
-
};
|
|
382
|
-
this.bumpUpdatedAt();
|
|
383
|
-
this.schedule();
|
|
384
|
-
}
|
|
385
|
-
recordTaskStatus(taskId, patch) {
|
|
386
|
-
this.snapshot = {
|
|
387
|
-
...this.snapshot,
|
|
388
|
-
tasks: this.snapshot.tasks.map(
|
|
389
|
-
(t) => t.taskId === taskId ? { ...t, ...patch } : t
|
|
390
|
-
)
|
|
391
|
-
};
|
|
392
|
-
this.bumpUpdatedAt();
|
|
393
|
-
this.schedule();
|
|
394
|
-
}
|
|
395
|
-
setUsage(usage) {
|
|
396
|
-
this.snapshot = { ...this.snapshot, usage };
|
|
397
|
-
this.bumpUpdatedAt();
|
|
398
|
-
this.schedule();
|
|
399
|
-
}
|
|
400
|
-
/** Force a synchronous flush — used by Director.shutdown(). */
|
|
401
|
-
async flush() {
|
|
402
|
-
if (this.timer) {
|
|
403
|
-
clearTimeout(this.timer);
|
|
404
|
-
this.timer = null;
|
|
405
|
-
}
|
|
406
|
-
await this.persist();
|
|
407
|
-
while (this.rewriteRequested) {
|
|
408
|
-
this.rewriteRequested = false;
|
|
409
|
-
await this.persist();
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
bumpUpdatedAt() {
|
|
413
|
-
this.snapshot = { ...this.snapshot, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
414
|
-
}
|
|
415
|
-
schedule() {
|
|
416
|
-
if (this.timer) return;
|
|
417
|
-
this.timer = setTimeout(() => {
|
|
418
|
-
this.timer = null;
|
|
419
|
-
void this.persist();
|
|
420
|
-
}, this.debounceMs);
|
|
421
|
-
}
|
|
422
|
-
async persist() {
|
|
423
|
-
if (this.writing) {
|
|
424
|
-
this.rewriteRequested = true;
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
this.writing = true;
|
|
428
|
-
try {
|
|
429
|
-
await atomicWrite(this.filePath, JSON.stringify(this.snapshot, null, 2), {
|
|
430
|
-
mode: 384
|
|
431
|
-
});
|
|
432
|
-
} catch (err) {
|
|
433
|
-
console.warn(
|
|
434
|
-
"[director-state] checkpoint write failed:",
|
|
435
|
-
toErrorMessage(err)
|
|
436
|
-
);
|
|
437
|
-
} finally {
|
|
438
|
-
this.writing = false;
|
|
439
|
-
if (this.rewriteRequested) {
|
|
440
|
-
this.rewriteRequested = false;
|
|
441
|
-
this.schedule();
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
158
|
|
|
447
|
-
// src/utils/
|
|
448
|
-
function
|
|
449
|
-
|
|
450
|
-
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
451
|
-
}
|
|
452
|
-
try {
|
|
453
|
-
return { ok: true, value: JSON.parse(input) };
|
|
454
|
-
} catch (err) {
|
|
455
|
-
return {
|
|
456
|
-
ok: false,
|
|
457
|
-
error: toErrorMessage(err)
|
|
458
|
-
};
|
|
459
|
-
}
|
|
159
|
+
// src/utils/error.ts
|
|
160
|
+
function toErrorMessage(err) {
|
|
161
|
+
return err instanceof Error ? err.message : String(err);
|
|
460
162
|
}
|
|
461
163
|
|
|
462
164
|
// src/utils/expect-defined.ts
|
|
@@ -676,13 +378,72 @@ function isEmptyMessage(msg) {
|
|
|
676
378
|
return msg.content.length === 0;
|
|
677
379
|
}
|
|
678
380
|
|
|
381
|
+
// src/utils/safe-json.ts
|
|
382
|
+
function safeParse(input, maxBytes = 5e6) {
|
|
383
|
+
if (input.length > maxBytes) {
|
|
384
|
+
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
return { ok: true, value: JSON.parse(input) };
|
|
388
|
+
} catch (err) {
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
error: toErrorMessage(err)
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function sessionScopedPath(dir, sessionId, suffix) {
|
|
396
|
+
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
397
|
+
throw invalid(sessionId);
|
|
398
|
+
}
|
|
399
|
+
const resolved = path9.resolve(dir, `${sessionId}${suffix}`);
|
|
400
|
+
const rel = path9.relative(path9.resolve(dir), resolved);
|
|
401
|
+
if (rel.startsWith("..") || path9.isAbsolute(rel)) {
|
|
402
|
+
throw invalid(sessionId);
|
|
403
|
+
}
|
|
404
|
+
return resolved;
|
|
405
|
+
}
|
|
406
|
+
function invalid(sessionId) {
|
|
407
|
+
return new FsError({
|
|
408
|
+
message: `Invalid sessionId: ${sessionId}`,
|
|
409
|
+
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
410
|
+
path: sessionId,
|
|
411
|
+
context: { reason: "path_traversal" }
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
415
|
+
var ENCODING_LEN = ENCODING.length;
|
|
416
|
+
var TIME_LEN = 10;
|
|
417
|
+
var RANDOM_LEN = 16;
|
|
418
|
+
function encodeTime(now, len) {
|
|
419
|
+
let mod;
|
|
420
|
+
let str = "";
|
|
421
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
422
|
+
mod = now % ENCODING_LEN;
|
|
423
|
+
str = ENCODING[mod] + str;
|
|
424
|
+
now = (now - mod) / ENCODING_LEN;
|
|
425
|
+
}
|
|
426
|
+
return str;
|
|
427
|
+
}
|
|
428
|
+
function encodeRandom(len) {
|
|
429
|
+
const bytes = randomBytes(len);
|
|
430
|
+
let str = "";
|
|
431
|
+
for (let i = 0; i < len; i++) {
|
|
432
|
+
str += ENCODING[bytes[i] % ENCODING_LEN];
|
|
433
|
+
}
|
|
434
|
+
return str;
|
|
435
|
+
}
|
|
436
|
+
function ulid(seedTime = Date.now()) {
|
|
437
|
+
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);
|
|
438
|
+
}
|
|
439
|
+
|
|
679
440
|
// src/utils/string.ts
|
|
680
441
|
function truncate(s, max) {
|
|
681
442
|
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
682
443
|
}
|
|
683
444
|
function projectSlug(absRoot) {
|
|
684
|
-
const base = slugify(
|
|
685
|
-
const hash = createHash("sha256").update(
|
|
445
|
+
const base = slugify(path9.basename(absRoot));
|
|
446
|
+
const hash = createHash("sha256").update(path9.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
686
447
|
return `${base}-${hash}`;
|
|
687
448
|
}
|
|
688
449
|
function slugify(name) {
|
|
@@ -690,8 +451,8 @@ function slugify(name) {
|
|
|
690
451
|
}
|
|
691
452
|
function wstackGlobalRoot() {
|
|
692
453
|
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
693
|
-
if (fromEnv && fromEnv.trim().length > 0) return
|
|
694
|
-
return
|
|
454
|
+
if (fromEnv && fromEnv.trim().length > 0) return path9.resolve(fromEnv);
|
|
455
|
+
return path9.join(os.homedir(), ".wrongstack");
|
|
695
456
|
}
|
|
696
457
|
|
|
697
458
|
// src/types/errors.ts
|
|
@@ -706,7 +467,10 @@ var ERROR_CODES = {
|
|
|
706
467
|
// Agent
|
|
707
468
|
AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
|
|
708
469
|
AGENT_ABORTED: "AGENT_ABORTED",
|
|
709
|
-
AGENT_RUN_FAILED: "AGENT_RUN_FAILED"
|
|
470
|
+
AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
|
|
471
|
+
// File system
|
|
472
|
+
FS_READ_FAILED: "FS_READ_FAILED",
|
|
473
|
+
FS_DELETE_FAILED: "FS_DELETE_FAILED"};
|
|
710
474
|
var WrongStackError = class extends Error {
|
|
711
475
|
code;
|
|
712
476
|
subsystem;
|
|
@@ -723,30 +487,336 @@ var WrongStackError = class extends Error {
|
|
|
723
487
|
this.context = opts.context;
|
|
724
488
|
}
|
|
725
489
|
/**
|
|
726
|
-
* Render a one-line user-facing description.
|
|
727
|
-
* Subclasses should override for domain-specific formatting.
|
|
490
|
+
* Render a one-line user-facing description.
|
|
491
|
+
* Subclasses should override for domain-specific formatting.
|
|
492
|
+
*/
|
|
493
|
+
describe() {
|
|
494
|
+
const ctx = this.context ? ` ${formatContext(this.context)}` : "";
|
|
495
|
+
return `${this.code}: ${this.message}${ctx}`;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
function formatContext(ctx) {
|
|
499
|
+
const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
|
|
500
|
+
return parts.length > 0 ? `[${parts.join(" ")}]` : "";
|
|
501
|
+
}
|
|
502
|
+
var AgentError = class extends WrongStackError {
|
|
503
|
+
constructor(opts) {
|
|
504
|
+
super({
|
|
505
|
+
message: opts.message,
|
|
506
|
+
code: opts.code,
|
|
507
|
+
subsystem: "agent",
|
|
508
|
+
severity: opts.code === ERROR_CODES.AGENT_ABORTED ? "warning" : "error",
|
|
509
|
+
recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,
|
|
510
|
+
context: opts.context,
|
|
511
|
+
cause: opts.cause
|
|
512
|
+
});
|
|
513
|
+
this.name = "AgentError";
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
var FsError = class extends WrongStackError {
|
|
517
|
+
path;
|
|
518
|
+
constructor(opts) {
|
|
519
|
+
super({
|
|
520
|
+
message: opts.message,
|
|
521
|
+
code: opts.code,
|
|
522
|
+
subsystem: "fs",
|
|
523
|
+
severity: "error",
|
|
524
|
+
recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
|
|
525
|
+
context: { path: opts.path, ...opts.context },
|
|
526
|
+
cause: opts.cause
|
|
527
|
+
});
|
|
528
|
+
this.name = "FsError";
|
|
529
|
+
this.path = opts.path;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/utils/atomic-write.ts
|
|
534
|
+
async function atomicWrite(targetPath, content, opts = {}) {
|
|
535
|
+
const dir = path9.dirname(targetPath);
|
|
536
|
+
await fsp7.mkdir(dir, { recursive: true });
|
|
537
|
+
const tmp = path9.join(dir, `.${path9.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
538
|
+
try {
|
|
539
|
+
if (typeof content === "string") {
|
|
540
|
+
await fsp7.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
541
|
+
} else {
|
|
542
|
+
await fsp7.writeFile(tmp, content, { flag: "wx" });
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
const fh = await fsp7.open(tmp, "r+");
|
|
546
|
+
try {
|
|
547
|
+
await fh.sync();
|
|
548
|
+
} finally {
|
|
549
|
+
await fh.close();
|
|
550
|
+
}
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
let mode;
|
|
554
|
+
try {
|
|
555
|
+
const stat7 = await fsp7.stat(targetPath);
|
|
556
|
+
mode = stat7.mode & 511;
|
|
557
|
+
} catch {
|
|
558
|
+
mode = opts.mode;
|
|
559
|
+
}
|
|
560
|
+
if (mode !== void 0) {
|
|
561
|
+
await fsp7.chmod(tmp, mode);
|
|
562
|
+
}
|
|
563
|
+
await renameWithRetry(tmp, targetPath);
|
|
564
|
+
if (mode !== void 0 && process.platform === "win32") {
|
|
565
|
+
try {
|
|
566
|
+
await fsp7.chmod(targetPath, mode);
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
} catch (err) {
|
|
571
|
+
try {
|
|
572
|
+
await fsp7.unlink(tmp);
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
throw err;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async function ensureDir(dir) {
|
|
579
|
+
await fsp7.mkdir(dir, { recursive: true });
|
|
580
|
+
}
|
|
581
|
+
async function withFileLock(targetPath, fn, opts = {}) {
|
|
582
|
+
const dir = path9.dirname(targetPath);
|
|
583
|
+
await fsp7.mkdir(dir, { recursive: true });
|
|
584
|
+
const lockPath = path9.join(dir, `.${path9.basename(targetPath)}.lock`);
|
|
585
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
586
|
+
const staleMs = opts.staleMs ?? 3e4;
|
|
587
|
+
const started = Date.now();
|
|
588
|
+
let handle;
|
|
589
|
+
for (; ; ) {
|
|
590
|
+
try {
|
|
591
|
+
handle = await fsp7.open(lockPath, "wx");
|
|
592
|
+
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
593
|
+
break;
|
|
594
|
+
} catch (err) {
|
|
595
|
+
const code = err.code;
|
|
596
|
+
if (code === "ENOENT") {
|
|
597
|
+
await fsp7.mkdir(dir, { recursive: true });
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (code !== "EEXIST") throw err;
|
|
601
|
+
try {
|
|
602
|
+
const stat7 = await fsp7.stat(lockPath);
|
|
603
|
+
if (Date.now() - stat7.mtimeMs > staleMs) {
|
|
604
|
+
await fsp7.unlink(lockPath);
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
} catch {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (Date.now() - started >= timeoutMs) {
|
|
611
|
+
throw new FsError({
|
|
612
|
+
message: `Timed out waiting for file lock: ${targetPath}`,
|
|
613
|
+
code: "FS_ATOMIC_WRITE_FAILED",
|
|
614
|
+
path: targetPath,
|
|
615
|
+
context: { timeoutMs }
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
await new Promise((resolve6) => setTimeout(resolve6, 25));
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
try {
|
|
622
|
+
return await fn();
|
|
623
|
+
} finally {
|
|
624
|
+
try {
|
|
625
|
+
await handle?.close();
|
|
626
|
+
} catch {
|
|
627
|
+
}
|
|
628
|
+
try {
|
|
629
|
+
await fsp7.unlink(lockPath);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
635
|
+
async function renameWithRetry(from, to) {
|
|
636
|
+
if (process.platform !== "win32") {
|
|
637
|
+
await fsp7.rename(from, to);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const delays = [10, 25, 60, 120, 250];
|
|
641
|
+
let lastErr;
|
|
642
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
643
|
+
try {
|
|
644
|
+
await fsp7.rename(from, to);
|
|
645
|
+
return;
|
|
646
|
+
} catch (err) {
|
|
647
|
+
lastErr = err;
|
|
648
|
+
const code = err?.code;
|
|
649
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
650
|
+
throw err;
|
|
651
|
+
}
|
|
652
|
+
await new Promise((resolve6) => setTimeout(resolve6, delays[i]));
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
throw lastErr;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/storage/director-state.ts
|
|
659
|
+
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
660
|
+
let existing;
|
|
661
|
+
try {
|
|
662
|
+
existing = await fsp7.readFile(lockPath, "utf8");
|
|
663
|
+
} catch {
|
|
664
|
+
}
|
|
665
|
+
if (existing) {
|
|
666
|
+
try {
|
|
667
|
+
const lock2 = JSON.parse(existing);
|
|
668
|
+
try {
|
|
669
|
+
process.kill(lock2.pid, 0);
|
|
670
|
+
return false;
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const lock = {
|
|
677
|
+
pid: processId,
|
|
678
|
+
hostname: hostname(),
|
|
679
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
680
|
+
};
|
|
681
|
+
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
async function releaseDirectorStateLock(lockPath) {
|
|
685
|
+
try {
|
|
686
|
+
await fsp7.unlink(lockPath);
|
|
687
|
+
} catch {
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
var DirectorStateCheckpoint = class {
|
|
691
|
+
snapshot;
|
|
692
|
+
filePath;
|
|
693
|
+
lockPath;
|
|
694
|
+
timer = null;
|
|
695
|
+
debounceMs;
|
|
696
|
+
writing = false;
|
|
697
|
+
rewriteRequested = false;
|
|
698
|
+
constructor(filePath, init, debounceMs = 250) {
|
|
699
|
+
this.filePath = filePath;
|
|
700
|
+
this.lockPath = `${filePath}.lock`;
|
|
701
|
+
this.debounceMs = debounceMs;
|
|
702
|
+
this.snapshot = {
|
|
703
|
+
version: 1,
|
|
704
|
+
directorRunId: init.directorRunId,
|
|
705
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
706
|
+
spawnCount: 0,
|
|
707
|
+
maxSpawns: init.maxSpawns,
|
|
708
|
+
spawnDepth: init.spawnDepth,
|
|
709
|
+
maxSpawnDepth: init.maxSpawnDepth,
|
|
710
|
+
directorBudget: init.directorBudget,
|
|
711
|
+
subagents: [],
|
|
712
|
+
tasks: []
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Attempt to acquire the lock for this checkpoint. Call this before
|
|
717
|
+
* resuming a crashed director run. If it returns false, another
|
|
718
|
+
* director process is still running this fleet — do not resume.
|
|
719
|
+
*/
|
|
720
|
+
async acquireLock() {
|
|
721
|
+
return acquireDirectorStateLock(this.lockPath);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Release the lock on graceful shutdown. Call `flush()` first to ensure
|
|
725
|
+
* the final checkpoint state is on disk before removing the lock.
|
|
726
|
+
* Without this, the next resume will see a stale-lock and refuse.
|
|
728
727
|
*/
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
return `${this.code}: ${this.message}${ctx}`;
|
|
728
|
+
async releaseLock() {
|
|
729
|
+
return releaseDirectorStateLock(this.lockPath);
|
|
732
730
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
731
|
+
/**
|
|
732
|
+
* Resume from a snapshot previously loaded via `loadDirectorState()`.
|
|
733
|
+
* Use this when `--resume <runId>` is triggered — the snapshot has
|
|
734
|
+
* the full fleet state (subagents, tasks) from before the crash; the
|
|
735
|
+
* checkpoint continues from there.
|
|
736
|
+
*/
|
|
737
|
+
resume(snapshot) {
|
|
738
|
+
this.snapshot = snapshot;
|
|
739
|
+
}
|
|
740
|
+
current() {
|
|
741
|
+
return this.snapshot;
|
|
742
|
+
}
|
|
743
|
+
recordSpawn(sub, spawnCount) {
|
|
744
|
+
this.snapshot = {
|
|
745
|
+
...this.snapshot,
|
|
746
|
+
spawnCount,
|
|
747
|
+
subagents: [...this.snapshot.subagents.filter((s) => s.id !== sub.id), sub]
|
|
748
|
+
};
|
|
749
|
+
this.bumpUpdatedAt();
|
|
750
|
+
this.schedule();
|
|
751
|
+
}
|
|
752
|
+
recordTaskAssigned(task) {
|
|
753
|
+
const exists = this.snapshot.tasks.some((t) => t.taskId === task.taskId);
|
|
754
|
+
this.snapshot = {
|
|
755
|
+
...this.snapshot,
|
|
756
|
+
tasks: exists ? this.snapshot.tasks.map((t) => t.taskId === task.taskId ? { ...t, ...task } : t) : [...this.snapshot.tasks, task]
|
|
757
|
+
};
|
|
758
|
+
this.bumpUpdatedAt();
|
|
759
|
+
this.schedule();
|
|
760
|
+
}
|
|
761
|
+
recordTaskStatus(taskId, patch) {
|
|
762
|
+
this.snapshot = {
|
|
763
|
+
...this.snapshot,
|
|
764
|
+
tasks: this.snapshot.tasks.map(
|
|
765
|
+
(t) => t.taskId === taskId ? { ...t, ...patch } : t
|
|
766
|
+
)
|
|
767
|
+
};
|
|
768
|
+
this.bumpUpdatedAt();
|
|
769
|
+
this.schedule();
|
|
770
|
+
}
|
|
771
|
+
setUsage(usage) {
|
|
772
|
+
this.snapshot = { ...this.snapshot, usage };
|
|
773
|
+
this.bumpUpdatedAt();
|
|
774
|
+
this.schedule();
|
|
775
|
+
}
|
|
776
|
+
/** Force a synchronous flush — used by Director.shutdown(). */
|
|
777
|
+
async flush() {
|
|
778
|
+
if (this.timer) {
|
|
779
|
+
clearTimeout(this.timer);
|
|
780
|
+
this.timer = null;
|
|
781
|
+
}
|
|
782
|
+
await this.persist();
|
|
783
|
+
while (this.rewriteRequested) {
|
|
784
|
+
this.rewriteRequested = false;
|
|
785
|
+
await this.persist();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
bumpUpdatedAt() {
|
|
789
|
+
this.snapshot = { ...this.snapshot, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
790
|
+
}
|
|
791
|
+
schedule() {
|
|
792
|
+
if (this.timer) return;
|
|
793
|
+
this.timer = setTimeout(() => {
|
|
794
|
+
this.timer = null;
|
|
795
|
+
void this.persist();
|
|
796
|
+
}, this.debounceMs);
|
|
797
|
+
}
|
|
798
|
+
async persist() {
|
|
799
|
+
if (this.writing) {
|
|
800
|
+
this.rewriteRequested = true;
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
this.writing = true;
|
|
804
|
+
try {
|
|
805
|
+
await atomicWrite(this.filePath, JSON.stringify(this.snapshot, null, 2), {
|
|
806
|
+
mode: 384
|
|
807
|
+
});
|
|
808
|
+
} catch (err) {
|
|
809
|
+
console.warn(
|
|
810
|
+
"[director-state] checkpoint write failed:",
|
|
811
|
+
toErrorMessage(err)
|
|
812
|
+
);
|
|
813
|
+
} finally {
|
|
814
|
+
this.writing = false;
|
|
815
|
+
if (this.rewriteRequested) {
|
|
816
|
+
this.rewriteRequested = false;
|
|
817
|
+
this.schedule();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
750
820
|
}
|
|
751
821
|
};
|
|
752
822
|
|
|
@@ -852,7 +922,7 @@ var InMemoryAgentBridge = class {
|
|
|
852
922
|
});
|
|
853
923
|
}
|
|
854
924
|
this.inflightGuards.add(correlationId);
|
|
855
|
-
return new Promise((
|
|
925
|
+
return new Promise((resolve6, reject) => {
|
|
856
926
|
const timer = setTimeout(() => {
|
|
857
927
|
this.inflightGuards.delete(correlationId);
|
|
858
928
|
this.pendingRequests.delete(correlationId);
|
|
@@ -871,7 +941,7 @@ var InMemoryAgentBridge = class {
|
|
|
871
941
|
return;
|
|
872
942
|
}
|
|
873
943
|
this.pendingRequests.set(correlationId, {
|
|
874
|
-
resolve:
|
|
944
|
+
resolve: resolve6,
|
|
875
945
|
reject,
|
|
876
946
|
timer
|
|
877
947
|
});
|
|
@@ -1173,7 +1243,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
1173
1243
|
id: `${role}-${this.sessionId}`,
|
|
1174
1244
|
name: role,
|
|
1175
1245
|
role,
|
|
1176
|
-
tools: ["fleet_emit", "
|
|
1246
|
+
tools: ["fleet_emit", "fleet", "read", "grep", "glob", "bash", "write"],
|
|
1177
1247
|
maxIterations: budget.maxIterations,
|
|
1178
1248
|
maxToolCalls: budget.maxToolCalls,
|
|
1179
1249
|
timeoutMs: budget.timeoutMs
|
|
@@ -1509,7 +1579,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
1509
1579
|
function readBundledInstructionText(relativePath) {
|
|
1510
1580
|
for (const root of instructionRootCandidates()) {
|
|
1511
1581
|
try {
|
|
1512
|
-
return readFileSync(
|
|
1582
|
+
return readFileSync(path9.join(root, relativePath), "utf8").trimEnd();
|
|
1513
1583
|
} catch {
|
|
1514
1584
|
}
|
|
1515
1585
|
}
|
|
@@ -1522,11 +1592,11 @@ function renderInstructionTemplate(template, values) {
|
|
|
1522
1592
|
);
|
|
1523
1593
|
}
|
|
1524
1594
|
function instructionRootCandidates() {
|
|
1525
|
-
const here =
|
|
1595
|
+
const here = path9.dirname(fileURLToPath(import.meta.url));
|
|
1526
1596
|
const candidates = [
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1597
|
+
path9.resolve(here, "../../instructions"),
|
|
1598
|
+
path9.resolve(here, "../instructions"),
|
|
1599
|
+
path9.resolve(here, "instructions")
|
|
1530
1600
|
];
|
|
1531
1601
|
return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
|
|
1532
1602
|
}
|
|
@@ -1655,24 +1725,24 @@ function agentPrompt(id) {
|
|
|
1655
1725
|
const fileName = `${id}.md`;
|
|
1656
1726
|
for (const dir of agentPromptDirCandidates()) {
|
|
1657
1727
|
try {
|
|
1658
|
-
return readFileSync(
|
|
1728
|
+
return readFileSync(path9.join(dir, fileName), "utf8").trimEnd();
|
|
1659
1729
|
} catch {
|
|
1660
1730
|
}
|
|
1661
1731
|
}
|
|
1662
1732
|
return "";
|
|
1663
1733
|
}
|
|
1664
1734
|
function agentPromptDirCandidates() {
|
|
1665
|
-
const here =
|
|
1735
|
+
const here = path9.dirname(fileURLToPath(import.meta.url));
|
|
1666
1736
|
const explicitDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"];
|
|
1667
|
-
const globalRoot = process.env["WRONGSTACK_HOME"] ||
|
|
1737
|
+
const globalRoot = process.env["WRONGSTACK_HOME"] || path9.join(os.homedir(), ".wrongstack");
|
|
1668
1738
|
const candidates = [
|
|
1669
|
-
...explicitDir ? [
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1739
|
+
...explicitDir ? [path9.resolve(explicitDir)] : [],
|
|
1740
|
+
path9.join(globalRoot, "instructions", "agents"),
|
|
1741
|
+
path9.resolve(here, "../../../../instructions/agents"),
|
|
1742
|
+
path9.resolve(here, "../../../instructions/agents"),
|
|
1743
|
+
path9.resolve(here, "../../instructions/agents"),
|
|
1744
|
+
path9.resolve(here, "../instructions/agents"),
|
|
1745
|
+
path9.resolve(here, "instructions/agents")
|
|
1676
1746
|
];
|
|
1677
1747
|
return candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
|
|
1678
1748
|
}
|
|
@@ -3491,96 +3561,91 @@ function makeTerminateAllTool(director) {
|
|
|
3491
3561
|
}
|
|
3492
3562
|
};
|
|
3493
3563
|
}
|
|
3494
|
-
function
|
|
3495
|
-
return {
|
|
3496
|
-
name: "fleet_status",
|
|
3497
|
-
description: "Snapshot of the fleet \u2014 every subagent's current status, coordinator counts (total/running/idle/stopped), pending task descriptions, and usage rollup.",
|
|
3498
|
-
permission: "auto",
|
|
3499
|
-
mutating: false,
|
|
3500
|
-
capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
|
|
3501
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
3502
|
-
async execute() {
|
|
3503
|
-
const base = director.status();
|
|
3504
|
-
const fm = director.fleetManager;
|
|
3505
|
-
const stats = fm?.getFleetStats();
|
|
3506
|
-
const fleetStatus = fm?.getFleetStatus();
|
|
3507
|
-
return {
|
|
3508
|
-
subagents: base.subagents,
|
|
3509
|
-
coordinatorStats: stats ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
|
|
3510
|
-
pending: fleetStatus?.pending ?? [],
|
|
3511
|
-
usage: fm?.snapshot()
|
|
3512
|
-
};
|
|
3513
|
-
}
|
|
3514
|
-
};
|
|
3515
|
-
}
|
|
3516
|
-
function makeFleetUsageTool(director) {
|
|
3517
|
-
return {
|
|
3518
|
-
name: "fleet_usage",
|
|
3519
|
-
description: "Token + cost breakdown across the fleet, per-subagent and totals.",
|
|
3520
|
-
permission: "auto",
|
|
3521
|
-
mutating: false,
|
|
3522
|
-
capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
|
|
3523
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
3524
|
-
async execute() {
|
|
3525
|
-
return director.snapshot();
|
|
3526
|
-
}
|
|
3527
|
-
};
|
|
3528
|
-
}
|
|
3529
|
-
function makeFleetSessionTool(director) {
|
|
3564
|
+
function makeFleetTool(director) {
|
|
3530
3565
|
return {
|
|
3531
|
-
name: "
|
|
3532
|
-
description: "
|
|
3566
|
+
name: "fleet",
|
|
3567
|
+
description: 'Fleet observation tool. Use `action` to select what you need: "status" \u2014 snapshot of all subagents + coordinator counts + pending tasks; "usage" \u2014 token + cost breakdown per subagent and totals; "health" \u2014 per-subagent budget pressure, last activity, and status; "session" \u2014 read a subagent\'s JSONL transcript (requires subagentId).',
|
|
3568
|
+
usageHint: 'action: "status" (default) | "usage" | "health" | "session".\nFor "session", pass subagentId (required) and optional tail (trailing JSONL lines).',
|
|
3533
3569
|
permission: "auto",
|
|
3534
3570
|
mutating: false,
|
|
3535
3571
|
capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
|
|
3536
3572
|
inputSchema: {
|
|
3537
3573
|
type: "object",
|
|
3538
3574
|
properties: {
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3575
|
+
action: {
|
|
3576
|
+
type: "string",
|
|
3577
|
+
enum: ["status", "usage", "health", "session"],
|
|
3578
|
+
description: "Observation to retrieve (default: status)."
|
|
3579
|
+
},
|
|
3580
|
+
subagentId: {
|
|
3581
|
+
type: "string",
|
|
3582
|
+
description: 'Subagent id (required for action: "session").'
|
|
3583
|
+
},
|
|
3584
|
+
tail: {
|
|
3585
|
+
type: "number",
|
|
3586
|
+
description: 'Number of trailing JSONL lines (action: "session" only). Omit for the full transcript.'
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3543
3589
|
},
|
|
3544
3590
|
async execute(input) {
|
|
3545
|
-
const i = input;
|
|
3546
|
-
const
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
}
|
|
3554
|
-
};
|
|
3555
|
-
}
|
|
3556
|
-
function makeFleetHealthTool(director) {
|
|
3557
|
-
return {
|
|
3558
|
-
name: "fleet_health",
|
|
3559
|
-
description: "Per-subagent health report: budget pressure (pct of limits consumed), last activity timestamp, and current status. Use to decide whether to assign more work to a subagent or spawn a fresh one.",
|
|
3560
|
-
permission: "auto",
|
|
3561
|
-
mutating: false,
|
|
3562
|
-
capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
|
|
3563
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
3564
|
-
async execute() {
|
|
3565
|
-
const status = director.status();
|
|
3566
|
-
const snapshot = director.snapshot();
|
|
3567
|
-
const subagents = status.subagents ?? [];
|
|
3568
|
-
const perSubagent = snapshot.perSubagent ?? {};
|
|
3569
|
-
return {
|
|
3570
|
-
subagents: subagents.map((s) => {
|
|
3571
|
-
const usage = perSubagent[s.id];
|
|
3591
|
+
const i = input ?? {};
|
|
3592
|
+
const action = i.action ?? "status";
|
|
3593
|
+
switch (action) {
|
|
3594
|
+
case "status": {
|
|
3595
|
+
const base = director.status();
|
|
3596
|
+
const fm = director.fleetManager;
|
|
3597
|
+
const stats = fm?.getFleetStats();
|
|
3598
|
+
const fleetStatus = fm?.getFleetStatus();
|
|
3572
3599
|
return {
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
toolCalls: usage?.toolCalls,
|
|
3579
|
-
costUsd: usage?.cost
|
|
3580
|
-
}
|
|
3600
|
+
action: "status",
|
|
3601
|
+
subagents: base.subagents,
|
|
3602
|
+
coordinatorStats: stats ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
|
|
3603
|
+
pending: fleetStatus?.pending ?? [],
|
|
3604
|
+
usage: fm?.snapshot()
|
|
3581
3605
|
};
|
|
3582
|
-
}
|
|
3583
|
-
|
|
3606
|
+
}
|
|
3607
|
+
case "usage": {
|
|
3608
|
+
return { action: "usage", ...director.snapshot() };
|
|
3609
|
+
}
|
|
3610
|
+
case "health": {
|
|
3611
|
+
const status = director.status();
|
|
3612
|
+
const snapshot = director.snapshot();
|
|
3613
|
+
const subagents = status.subagents ?? [];
|
|
3614
|
+
const perSubagent = snapshot.perSubagent ?? {};
|
|
3615
|
+
return {
|
|
3616
|
+
action: "health",
|
|
3617
|
+
subagents: subagents.map((s) => {
|
|
3618
|
+
const usage = perSubagent[s.id];
|
|
3619
|
+
return {
|
|
3620
|
+
id: s.id,
|
|
3621
|
+
status: s.status,
|
|
3622
|
+
lastEventAt: usage?.lastEventAt,
|
|
3623
|
+
budgetPressure: {
|
|
3624
|
+
iterations: usage?.iterations,
|
|
3625
|
+
toolCalls: usage?.toolCalls,
|
|
3626
|
+
costUsd: usage?.cost
|
|
3627
|
+
}
|
|
3628
|
+
};
|
|
3629
|
+
})
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
case "session": {
|
|
3633
|
+
const subagentId = i.subagentId;
|
|
3634
|
+
if (!subagentId) {
|
|
3635
|
+
return { action: "session", error: 'fleet: subagentId is required for action: "session"' };
|
|
3636
|
+
}
|
|
3637
|
+
const result = await director.readSession(subagentId, i.tail);
|
|
3638
|
+
if (!result) {
|
|
3639
|
+
return {
|
|
3640
|
+
action: "session",
|
|
3641
|
+
error: `fleet: transcript unavailable for "${subagentId}". Is sessionsRoot configured?`
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
return { action: "session", ...result };
|
|
3645
|
+
}
|
|
3646
|
+
default:
|
|
3647
|
+
return { error: `fleet: unknown action "${action}". Valid: status, usage, health, session.` };
|
|
3648
|
+
}
|
|
3584
3649
|
}
|
|
3585
3650
|
};
|
|
3586
3651
|
}
|
|
@@ -4116,6 +4181,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4116
4181
|
*/
|
|
4117
4182
|
lastActivityTime = null;
|
|
4118
4183
|
_onThreshold;
|
|
4184
|
+
_sessionId;
|
|
4119
4185
|
/**
|
|
4120
4186
|
* Hard cap on how long `_negotiateExtension` waits for the coordinator to
|
|
4121
4187
|
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
@@ -4184,10 +4250,15 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4184
4250
|
get mode() {
|
|
4185
4251
|
return this._mode;
|
|
4186
4252
|
}
|
|
4187
|
-
constructor(limits = {}, mode = "auto") {
|
|
4253
|
+
constructor(limits = {}, mode = "auto", options = {}) {
|
|
4188
4254
|
this._mode = mode;
|
|
4255
|
+
this._sessionId = options.sessionId;
|
|
4189
4256
|
this.limits = { ...limits };
|
|
4190
4257
|
}
|
|
4258
|
+
currentSessionId() {
|
|
4259
|
+
const value = typeof this._sessionId === "function" ? this._sessionId() : this._sessionId;
|
|
4260
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4261
|
+
}
|
|
4191
4262
|
start() {
|
|
4192
4263
|
this.startTime = Date.now();
|
|
4193
4264
|
this.lastActivityTime = this.startTime;
|
|
@@ -4346,16 +4417,18 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
4346
4417
|
if (!bus?.hasListenerFor("budget.threshold_reached")) {
|
|
4347
4418
|
return Promise.resolve("stop");
|
|
4348
4419
|
}
|
|
4349
|
-
return new Promise((
|
|
4420
|
+
return new Promise((resolve6) => {
|
|
4350
4421
|
let resolved = false;
|
|
4351
4422
|
const respond = (d) => {
|
|
4352
4423
|
if (resolved) return;
|
|
4353
4424
|
resolved = true;
|
|
4354
4425
|
clearTimeout(fallback);
|
|
4355
|
-
|
|
4426
|
+
resolve6(d);
|
|
4356
4427
|
};
|
|
4357
4428
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
4429
|
+
const sessionId = this.currentSessionId();
|
|
4358
4430
|
bus.emit("budget.threshold_reached", {
|
|
4431
|
+
...sessionId ? { sessionId } : {},
|
|
4359
4432
|
kind: entry.kind,
|
|
4360
4433
|
used: entry.used,
|
|
4361
4434
|
limit: entry.limit,
|
|
@@ -4825,6 +4898,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
4825
4898
|
coordinatorId;
|
|
4826
4899
|
config;
|
|
4827
4900
|
runner;
|
|
4901
|
+
sessionId;
|
|
4828
4902
|
fleetBus;
|
|
4829
4903
|
subagents = /* @__PURE__ */ new Map();
|
|
4830
4904
|
/**
|
|
@@ -4859,6 +4933,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
4859
4933
|
this.coordinatorId = config.coordinatorId;
|
|
4860
4934
|
this.config = config;
|
|
4861
4935
|
this.runner = options.runner;
|
|
4936
|
+
this.sessionId = options.sessionId;
|
|
4937
|
+
}
|
|
4938
|
+
currentSessionId() {
|
|
4939
|
+
const value = typeof this.sessionId === "function" ? this.sessionId() : this.sessionId;
|
|
4940
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4862
4941
|
}
|
|
4863
4942
|
/**
|
|
4864
4943
|
* Replace the runner after construction. Used when the runner depends
|
|
@@ -5017,11 +5096,16 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5017
5096
|
status: s.status,
|
|
5018
5097
|
assigned: s.context.parentBridge !== null
|
|
5019
5098
|
}));
|
|
5099
|
+
const sessionId = this.currentSessionId();
|
|
5020
5100
|
this.fleetBus?.emit({
|
|
5021
5101
|
subagentId: this.coordinatorId,
|
|
5022
5102
|
ts: Date.now(),
|
|
5023
5103
|
type: "coordinator.stats",
|
|
5024
|
-
payload: {
|
|
5104
|
+
payload: {
|
|
5105
|
+
...sessionId ? { sessionId } : {},
|
|
5106
|
+
...stats,
|
|
5107
|
+
subagentStatuses
|
|
5108
|
+
}
|
|
5025
5109
|
});
|
|
5026
5110
|
}
|
|
5027
5111
|
getStatus() {
|
|
@@ -5053,7 +5137,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5053
5137
|
taskIds.map((id) => {
|
|
5054
5138
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
5055
5139
|
if (cached) return cached;
|
|
5056
|
-
return new Promise((
|
|
5140
|
+
return new Promise((resolve6, reject) => {
|
|
5057
5141
|
const timeout = setTimeout(() => {
|
|
5058
5142
|
this.off("task.completed", handler);
|
|
5059
5143
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -5062,7 +5146,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5062
5146
|
if (result.taskId === id) {
|
|
5063
5147
|
clearTimeout(timeout);
|
|
5064
5148
|
this.off("task.completed", handler);
|
|
5065
|
-
|
|
5149
|
+
resolve6(result);
|
|
5066
5150
|
}
|
|
5067
5151
|
};
|
|
5068
5152
|
this.on("task.completed", handler);
|
|
@@ -5224,16 +5308,20 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5224
5308
|
const rawTimeoutMs = subagent.config.timeoutMs;
|
|
5225
5309
|
const rawIdleTimeoutMs = subagent.config.idleTimeoutMs;
|
|
5226
5310
|
const configWithRosterDefaults = applyRosterBudget(subagent.config);
|
|
5227
|
-
const budget = new SubagentBudget(
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5311
|
+
const budget = new SubagentBudget(
|
|
5312
|
+
{
|
|
5313
|
+
maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
|
|
5314
|
+
maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
|
|
5315
|
+
maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
|
|
5316
|
+
maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
|
|
5317
|
+
// Wall-clock cap is opt-in (explicit config / defaultBudget only); the
|
|
5318
|
+
// roster no longer supplies one. Idle is the default reaper.
|
|
5319
|
+
timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs,
|
|
5320
|
+
idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
|
|
5321
|
+
},
|
|
5322
|
+
"auto",
|
|
5323
|
+
{ sessionId: () => this.currentSessionId() }
|
|
5324
|
+
);
|
|
5237
5325
|
subagent.activeBudget = budget;
|
|
5238
5326
|
if (!this.runner) {
|
|
5239
5327
|
return;
|
|
@@ -5330,13 +5418,15 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5330
5418
|
}
|
|
5331
5419
|
return new Promise((resolveDecision) => {
|
|
5332
5420
|
let settled = false;
|
|
5333
|
-
const
|
|
5421
|
+
const resolve6 = (d) => {
|
|
5334
5422
|
if (settled) return;
|
|
5335
5423
|
settled = true;
|
|
5336
5424
|
resolveDecision(d);
|
|
5337
5425
|
};
|
|
5338
|
-
const fallback = setTimeout(() =>
|
|
5426
|
+
const fallback = setTimeout(() => resolve6("stop"), DECISION_TIMEOUT_MS);
|
|
5427
|
+
const sessionId = this.currentSessionId();
|
|
5339
5428
|
budget._events?.emit("budget.threshold_reached", {
|
|
5429
|
+
...sessionId ? { sessionId } : {},
|
|
5340
5430
|
kind: "timeout",
|
|
5341
5431
|
used,
|
|
5342
5432
|
limit,
|
|
@@ -5351,11 +5441,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5351
5441
|
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
5352
5442
|
extend: (extra) => {
|
|
5353
5443
|
clearTimeout(fallback);
|
|
5354
|
-
queueMicrotask(() =>
|
|
5444
|
+
queueMicrotask(() => resolve6({ extend: extra }));
|
|
5355
5445
|
},
|
|
5356
5446
|
deny: () => {
|
|
5357
5447
|
clearTimeout(fallback);
|
|
5358
|
-
|
|
5448
|
+
resolve6("stop");
|
|
5359
5449
|
}
|
|
5360
5450
|
});
|
|
5361
5451
|
});
|
|
@@ -5370,7 +5460,9 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
5370
5460
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
5371
5461
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
5372
5462
|
if (idleExceeded && !wallExceeded) {
|
|
5463
|
+
const sessionId = this.currentSessionId();
|
|
5373
5464
|
budget._events?.emit("budget.threshold_reached", {
|
|
5465
|
+
...sessionId ? { sessionId } : {},
|
|
5374
5466
|
kind: "idle_timeout",
|
|
5375
5467
|
used: budget.idleMs(),
|
|
5376
5468
|
limit: idleLimit ?? 0,
|
|
@@ -5661,6 +5753,10 @@ var Director = class _Director {
|
|
|
5661
5753
|
const resolved = typeof this.maxContext === "function" ? this.maxContext() : this.maxContext;
|
|
5662
5754
|
return resolved && resolved > 0 ? resolved : 128e3;
|
|
5663
5755
|
}
|
|
5756
|
+
currentSessionId() {
|
|
5757
|
+
const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
|
|
5758
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
5759
|
+
}
|
|
5664
5760
|
/** Optional Brain arbiter for director-level policy decisions. */
|
|
5665
5761
|
brain;
|
|
5666
5762
|
/**
|
|
@@ -5720,6 +5816,7 @@ var Director = class _Director {
|
|
|
5720
5816
|
stateCheckpoint;
|
|
5721
5817
|
/** Optional session writer for emitting task_* / agent_* lifecycle events. */
|
|
5722
5818
|
sessionWriter;
|
|
5819
|
+
sessionIdSource;
|
|
5723
5820
|
/** Debounce timer for periodic manifest writes. */
|
|
5724
5821
|
manifestTimer = null;
|
|
5725
5822
|
manifestDebounceMs;
|
|
@@ -5727,7 +5824,7 @@ var Director = class _Director {
|
|
|
5727
5824
|
maxFleetCostUsd;
|
|
5728
5825
|
/** Max auto-extensions per subagent per budget kind before denying. */
|
|
5729
5826
|
maxBudgetExtensions;
|
|
5730
|
-
/** Sessions root for direct subagent JSONL reads (
|
|
5827
|
+
/** Sessions root for direct subagent JSONL reads (fleet tool, action: session). */
|
|
5731
5828
|
sessionsRoot;
|
|
5732
5829
|
/** Director run id for JSONL path resolution. */
|
|
5733
5830
|
directorRunId;
|
|
@@ -5803,6 +5900,7 @@ var Director = class _Director {
|
|
|
5803
5900
|
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
5804
5901
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
5805
5902
|
this.sessionWriter = opts.sessionWriter ?? null;
|
|
5903
|
+
this.sessionIdSource = opts.sessionId ?? (() => opts.sessionWriter?.id);
|
|
5806
5904
|
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
5807
5905
|
this.dispatchClassifier = opts.dispatchClassifier;
|
|
5808
5906
|
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
@@ -5849,7 +5947,7 @@ var Director = class _Director {
|
|
|
5849
5947
|
}
|
|
5850
5948
|
this.coordinator = new DefaultMultiAgentCoordinator(
|
|
5851
5949
|
{ ...opts.config, coordinatorId: this.id },
|
|
5852
|
-
{ runner: opts.runner }
|
|
5950
|
+
{ runner: opts.runner, sessionId: () => this.currentSessionId() }
|
|
5853
5951
|
);
|
|
5854
5952
|
this.coordinator.setFleetBus(this.fleet);
|
|
5855
5953
|
this.fleetManager?.setCoordinator(this.coordinator);
|
|
@@ -5977,6 +6075,7 @@ var Director = class _Director {
|
|
|
5977
6075
|
if (this.brain) {
|
|
5978
6076
|
void this.brain.decide({
|
|
5979
6077
|
id: `director-budget-${e.subagentId}-${payload.kind}`,
|
|
6078
|
+
sessionId: this.currentSessionId(),
|
|
5980
6079
|
source: "director",
|
|
5981
6080
|
question: `Should the director extend the ${payload.kind} budget for subagent ${e.subagentId}?`,
|
|
5982
6081
|
context: [
|
|
@@ -6426,7 +6525,7 @@ var Director = class _Director {
|
|
|
6426
6525
|
})),
|
|
6427
6526
|
usage: this.usage.snapshot()
|
|
6428
6527
|
};
|
|
6429
|
-
await fsp7.mkdir(
|
|
6528
|
+
await fsp7.mkdir(path9.dirname(this.manifestPath), { recursive: true });
|
|
6430
6529
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
6431
6530
|
return this.manifestPath;
|
|
6432
6531
|
}
|
|
@@ -6570,11 +6669,11 @@ var Director = class _Director {
|
|
|
6570
6669
|
if (cached) return cached;
|
|
6571
6670
|
const existing = this.taskWaiters.get(id);
|
|
6572
6671
|
if (existing) return existing.promise;
|
|
6573
|
-
let
|
|
6672
|
+
let resolve6;
|
|
6574
6673
|
const promise = new Promise((res) => {
|
|
6575
|
-
|
|
6674
|
+
resolve6 = res;
|
|
6576
6675
|
});
|
|
6577
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
6676
|
+
this.taskWaiters.set(id, { promise, resolve: resolve6 });
|
|
6578
6677
|
return promise;
|
|
6579
6678
|
})
|
|
6580
6679
|
);
|
|
@@ -6657,7 +6756,7 @@ var Director = class _Director {
|
|
|
6657
6756
|
*/
|
|
6658
6757
|
async readSession(subagentId, tail) {
|
|
6659
6758
|
if (!this.sessionsRoot) return null;
|
|
6660
|
-
const filePath =
|
|
6759
|
+
const filePath = path9.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
6661
6760
|
let raw;
|
|
6662
6761
|
try {
|
|
6663
6762
|
raw = await fsp7.readFile(filePath, "utf8");
|
|
@@ -6773,10 +6872,7 @@ var Director = class _Director {
|
|
|
6773
6872
|
makeRollUpTool(this),
|
|
6774
6873
|
makeTerminateTool(this),
|
|
6775
6874
|
makeTerminateAllTool(this),
|
|
6776
|
-
|
|
6777
|
-
makeFleetUsageTool(this),
|
|
6778
|
-
makeFleetSessionTool(this),
|
|
6779
|
-
makeFleetHealthTool(this),
|
|
6875
|
+
makeFleetTool(this),
|
|
6780
6876
|
makeCollabDebugTool(this),
|
|
6781
6877
|
makeFleetEmitTool(this),
|
|
6782
6878
|
makeWorkCompleteTool(this)
|
|
@@ -6897,6 +6993,7 @@ function createDelegateTool(opts) {
|
|
|
6897
6993
|
mutating: false,
|
|
6898
6994
|
inputSchema,
|
|
6899
6995
|
async execute(input) {
|
|
6996
|
+
const sessionId = opts.directorRunId;
|
|
6900
6997
|
const i = input ?? {};
|
|
6901
6998
|
if (typeof i.task !== "string" || !i.task.trim()) {
|
|
6902
6999
|
return { ok: false, error: "`task` is required." };
|
|
@@ -6962,7 +7059,7 @@ function createDelegateTool(opts) {
|
|
|
6962
7059
|
if (!cfg.timeoutMs) {
|
|
6963
7060
|
cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
6964
7061
|
}
|
|
6965
|
-
opts.events?.emit("delegate.started", { target, task: i.task });
|
|
7062
|
+
opts.events?.emit("delegate.started", { sessionId, target, task: i.task });
|
|
6966
7063
|
const subagentId = await director.spawn(cfg);
|
|
6967
7064
|
const taskId = await director.assign({
|
|
6968
7065
|
id: `${randomUUID()}`,
|
|
@@ -6970,7 +7067,7 @@ function createDelegateTool(opts) {
|
|
|
6970
7067
|
subagentId
|
|
6971
7068
|
});
|
|
6972
7069
|
const dir = director;
|
|
6973
|
-
const result = await new Promise((
|
|
7070
|
+
const result = await new Promise((resolve6) => {
|
|
6974
7071
|
let settled = false;
|
|
6975
7072
|
let timer;
|
|
6976
7073
|
const finish = (value) => {
|
|
@@ -6980,7 +7077,7 @@ function createDelegateTool(opts) {
|
|
|
6980
7077
|
offTool();
|
|
6981
7078
|
offIter();
|
|
6982
7079
|
offProgress();
|
|
6983
|
-
|
|
7080
|
+
resolve6(value);
|
|
6984
7081
|
};
|
|
6985
7082
|
const arm = () => {
|
|
6986
7083
|
if (timer) clearTimeout(timer);
|
|
@@ -6998,6 +7095,7 @@ function createDelegateTool(opts) {
|
|
|
6998
7095
|
if ("__timeout" in result) {
|
|
6999
7096
|
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
7000
7097
|
opts.events?.emit("delegate.completed", {
|
|
7098
|
+
sessionId,
|
|
7001
7099
|
target,
|
|
7002
7100
|
task: i.task,
|
|
7003
7101
|
ok: false,
|
|
@@ -7021,6 +7119,7 @@ function createDelegateTool(opts) {
|
|
|
7021
7119
|
if ("__emptyResult" in result) {
|
|
7022
7120
|
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
7023
7121
|
opts.events?.emit("delegate.completed", {
|
|
7122
|
+
sessionId,
|
|
7024
7123
|
target,
|
|
7025
7124
|
task: i.task,
|
|
7026
7125
|
ok: false,
|
|
@@ -7054,6 +7153,7 @@ function createDelegateTool(opts) {
|
|
|
7054
7153
|
costUsd = void 0;
|
|
7055
7154
|
}
|
|
7056
7155
|
opts.events?.emit("delegate.completed", {
|
|
7156
|
+
sessionId,
|
|
7057
7157
|
target,
|
|
7058
7158
|
task: i.task,
|
|
7059
7159
|
ok: result.status === "success",
|
|
@@ -7088,6 +7188,7 @@ function createDelegateTool(opts) {
|
|
|
7088
7188
|
} catch (err) {
|
|
7089
7189
|
const message = toErrorMessage(err);
|
|
7090
7190
|
opts.events?.emit("delegate.completed", {
|
|
7191
|
+
sessionId,
|
|
7091
7192
|
target,
|
|
7092
7193
|
task: i.task,
|
|
7093
7194
|
ok: false,
|
|
@@ -7187,13 +7288,13 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
7187
7288
|
if (!opts.sessionsRoot) return void 0;
|
|
7188
7289
|
const candidates = [];
|
|
7189
7290
|
if (opts.directorRunId) {
|
|
7190
|
-
candidates.push(
|
|
7291
|
+
candidates.push(path9.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
7191
7292
|
} else {
|
|
7192
7293
|
try {
|
|
7193
7294
|
const entries = await fsp7.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
7194
7295
|
for (const entry of entries) {
|
|
7195
7296
|
if (entry.isDirectory()) {
|
|
7196
|
-
candidates.push(
|
|
7297
|
+
candidates.push(path9.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
7197
7298
|
}
|
|
7198
7299
|
}
|
|
7199
7300
|
} catch {
|
|
@@ -7465,7 +7566,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
7465
7566
|
this.meta = meta;
|
|
7466
7567
|
this.events = events;
|
|
7467
7568
|
this.resumed = opts.resumed ?? false;
|
|
7468
|
-
this.manifestFile = opts.dir ?
|
|
7569
|
+
this.manifestFile = opts.dir ? path9.join(opts.dir, `${path9.basename(id)}.summary.json`) : "";
|
|
7469
7570
|
this.filePath = opts.filePath ?? "";
|
|
7470
7571
|
this.secretScrubber = opts.secretScrubber;
|
|
7471
7572
|
this.onCloseCb = opts.onClose;
|
|
@@ -7834,6 +7935,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
7834
7935
|
promptPreview
|
|
7835
7936
|
});
|
|
7836
7937
|
this.events?.emit("checkpoint.written", {
|
|
7938
|
+
sessionId: this.id,
|
|
7837
7939
|
promptIndex,
|
|
7838
7940
|
promptPreview,
|
|
7839
7941
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8003,6 +8105,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8003
8105
|
revertedFiles: []
|
|
8004
8106
|
});
|
|
8005
8107
|
this.events?.emit("session.rewound", {
|
|
8108
|
+
sessionId: this.id,
|
|
8006
8109
|
toPromptIndex: targetPromptIndex,
|
|
8007
8110
|
revertedFiles: [],
|
|
8008
8111
|
removedEvents: removedCount
|
|
@@ -8042,7 +8145,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8042
8145
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8043
8146
|
context
|
|
8044
8147
|
});
|
|
8045
|
-
this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8148
|
+
this.events?.emit("in_flight.started", { sessionId: this.id, context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8046
8149
|
}
|
|
8047
8150
|
/**
|
|
8048
8151
|
* Close the in-flight marker. Idempotent in spirit
|
|
@@ -8057,18 +8160,15 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8057
8160
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8058
8161
|
reason
|
|
8059
8162
|
});
|
|
8060
|
-
this.events?.emit("in_flight.ended", { reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8163
|
+
this.events?.emit("in_flight.ended", { sessionId: this.id, reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8061
8164
|
}
|
|
8062
8165
|
};
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
function generateSessionId(startedAt, model) {
|
|
8166
|
+
|
|
8167
|
+
// src/storage/session-id.ts
|
|
8168
|
+
function generateSessionId(startedAt, _model) {
|
|
8067
8169
|
const date = startedAt.slice(0, 10);
|
|
8068
|
-
const
|
|
8069
|
-
|
|
8070
|
-
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
8071
|
-
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
8170
|
+
const seedTime = Number.isNaN(Date.parse(startedAt)) ? Date.now() : Date.parse(startedAt);
|
|
8171
|
+
return `${date}/sess_${ulid(seedTime)}`;
|
|
8072
8172
|
}
|
|
8073
8173
|
|
|
8074
8174
|
// src/storage/session-summary.ts
|
|
@@ -8199,17 +8299,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8199
8299
|
}
|
|
8200
8300
|
/** Absolute path to the session index file. */
|
|
8201
8301
|
get indexFile() {
|
|
8202
|
-
return
|
|
8302
|
+
return path9.join(this.dir, "_index.jsonl");
|
|
8203
8303
|
}
|
|
8204
8304
|
/** Join session ID to its absolute path within the store directory. */
|
|
8205
8305
|
sessionPath(id, ext) {
|
|
8206
|
-
return
|
|
8306
|
+
return sessionScopedPath(this.dir, id, ext);
|
|
8207
8307
|
}
|
|
8208
8308
|
shardManifestPath(shardKey) {
|
|
8209
|
-
return shardKey ?
|
|
8309
|
+
return shardKey ? path9.join(this.dir, shardKey, "_manifest.json") : path9.join(this.dir, "_manifest.json");
|
|
8210
8310
|
}
|
|
8211
8311
|
shardKeyForSessionId(id) {
|
|
8212
|
-
const dirName =
|
|
8312
|
+
const dirName = path9.dirname(id);
|
|
8213
8313
|
return dirName === "." ? "" : dirName;
|
|
8214
8314
|
}
|
|
8215
8315
|
invalidateShardManifestBySessionId(id) {
|
|
@@ -8221,15 +8321,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8221
8321
|
* subdirectory so sessions group naturally by day.
|
|
8222
8322
|
*/
|
|
8223
8323
|
async ensureShardDir(id) {
|
|
8224
|
-
const dirPath =
|
|
8324
|
+
const dirPath = path9.dirname(sessionScopedPath(this.dir, id, ""));
|
|
8225
8325
|
await ensureDir(dirPath);
|
|
8226
8326
|
return dirPath;
|
|
8227
8327
|
}
|
|
8228
8328
|
async create(meta) {
|
|
8229
8329
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8230
|
-
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt
|
|
8330
|
+
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt);
|
|
8231
8331
|
const shardDir = await this.ensureShardDir(id);
|
|
8232
|
-
const file =
|
|
8332
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
8233
8333
|
const t0 = Date.now();
|
|
8234
8334
|
let handle;
|
|
8235
8335
|
try {
|
|
@@ -8291,7 +8391,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8291
8391
|
// Shard directory (sessions/<date>/) — must match create() so the
|
|
8292
8392
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
8293
8393
|
// sessions root (where summaryFor() would never find it).
|
|
8294
|
-
dir:
|
|
8394
|
+
dir: path9.dirname(file),
|
|
8295
8395
|
filePath: file,
|
|
8296
8396
|
secretScrubber: this.secretScrubber,
|
|
8297
8397
|
onClose: (s) => this.appendToIndex(s)
|
|
@@ -8311,6 +8411,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8311
8411
|
}
|
|
8312
8412
|
}
|
|
8313
8413
|
async load(id) {
|
|
8414
|
+
return this.loadInternal(id, { full: true });
|
|
8415
|
+
}
|
|
8416
|
+
/**
|
|
8417
|
+
* Fast-path loader that skips message reconstruction and adjacency repair.
|
|
8418
|
+
*
|
|
8419
|
+
* Use this for callers that only need the raw event stream + session
|
|
8420
|
+
* metadata — e.g. session listers, analytics, audit, and the TUI's
|
|
8421
|
+
* "events only" views. It avoids the message array build and
|
|
8422
|
+
* repairToolUseAdjacency cost on large session files (a long agent
|
|
8423
|
+
* run can have 50k+ events; rebuilding messages is O(events) and
|
|
8424
|
+
* allocates per-block, so skipping it is a meaningful win).
|
|
8425
|
+
*
|
|
8426
|
+
* The returned data.messages is an empty array; data.toolCallEnds
|
|
8427
|
+
* is computed from the raw events. usage is the sum across all
|
|
8428
|
+
* llm_response events — same as full load().
|
|
8429
|
+
*/
|
|
8430
|
+
async loadEventsOnly(id) {
|
|
8431
|
+
return this.loadInternal(id, { full: false });
|
|
8432
|
+
}
|
|
8433
|
+
async loadInternal(id, mode) {
|
|
8314
8434
|
const file = this.sessionPath(id, ".jsonl");
|
|
8315
8435
|
const t0 = Date.now();
|
|
8316
8436
|
let outcome = "success";
|
|
@@ -8324,87 +8444,107 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8324
8444
|
cacheHit = true;
|
|
8325
8445
|
this._loadCache.delete(id);
|
|
8326
8446
|
this._loadCache.set(id, cached);
|
|
8327
|
-
return cached.data;
|
|
8447
|
+
if (mode.full) return cached.data;
|
|
8448
|
+
return { ...cached.data, messages: [] };
|
|
8328
8449
|
}
|
|
8329
|
-
const raw = await fsp7.readFile(file, "utf8");
|
|
8330
|
-
const lines = raw.split("\n").filter((l) => l.trim());
|
|
8331
8450
|
const events = [];
|
|
8332
8451
|
let sessionStartEvent;
|
|
8333
8452
|
let sessionEndEvent;
|
|
8334
8453
|
let sessionModel;
|
|
8335
8454
|
let sessionProvider;
|
|
8336
8455
|
let sessionPendingToolUses;
|
|
8337
|
-
const messages = [];
|
|
8338
|
-
const openToolUses = /* @__PURE__ */ new Set();
|
|
8456
|
+
const messages = mode.full ? [] : void 0;
|
|
8457
|
+
const openToolUses = mode.full ? /* @__PURE__ */ new Set() : void 0;
|
|
8339
8458
|
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
}
|
|
8355
|
-
if (ev.type === "user_input") {
|
|
8356
|
-
openToolUses.clear();
|
|
8357
|
-
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
8358
|
-
} else if (ev.type === "llm_response") {
|
|
8359
|
-
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
8360
|
-
for (const b of ev.content) {
|
|
8361
|
-
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
8459
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
8460
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
8461
|
+
try {
|
|
8462
|
+
for await (const line of rl) {
|
|
8463
|
+
if (!line.trim()) continue;
|
|
8464
|
+
try {
|
|
8465
|
+
const parsed = JSON.parse(line);
|
|
8466
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
8467
|
+
const ev = parsed;
|
|
8468
|
+
events.push(ev);
|
|
8469
|
+
if (ev.type === "session_start" && !sessionStartEvent) {
|
|
8470
|
+
sessionStartEvent = ev;
|
|
8471
|
+
sessionModel = ev.model;
|
|
8472
|
+
sessionProvider = ev.provider;
|
|
8362
8473
|
}
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
8367
|
-
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
8368
|
-
};
|
|
8369
|
-
} else if (ev.type === "tool_result") {
|
|
8370
|
-
if (!openToolUses.has(ev.id)) {
|
|
8371
|
-
this.events?.emit("session.damaged", {
|
|
8372
|
-
sessionId: id,
|
|
8373
|
-
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
8374
|
-
});
|
|
8375
|
-
continue;
|
|
8474
|
+
if (ev.type === "session_end") {
|
|
8475
|
+
sessionEndEvent = ev;
|
|
8476
|
+
sessionPendingToolUses = ev.pendingToolUses;
|
|
8376
8477
|
}
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8478
|
+
if (mode.full && messages !== void 0 && openToolUses !== void 0) {
|
|
8479
|
+
if (ev.type === "user_input") {
|
|
8480
|
+
openToolUses.clear();
|
|
8481
|
+
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
8482
|
+
} else if (ev.type === "llm_response") {
|
|
8483
|
+
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
8484
|
+
for (const b of ev.content) {
|
|
8485
|
+
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
8486
|
+
}
|
|
8487
|
+
usage = {
|
|
8488
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
8489
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
8490
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
8491
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
8492
|
+
};
|
|
8493
|
+
} else if (ev.type === "tool_result") {
|
|
8494
|
+
if (!openToolUses.has(ev.id)) {
|
|
8495
|
+
this.events?.emit("session.damaged", {
|
|
8496
|
+
sessionId: id,
|
|
8497
|
+
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
8498
|
+
});
|
|
8499
|
+
continue;
|
|
8500
|
+
}
|
|
8501
|
+
openToolUses.delete(ev.id);
|
|
8502
|
+
const resultBlock = {
|
|
8503
|
+
type: "tool_result",
|
|
8504
|
+
tool_use_id: ev.id,
|
|
8505
|
+
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
8506
|
+
is_error: ev.isError
|
|
8507
|
+
};
|
|
8508
|
+
const last = messages[messages.length - 1];
|
|
8509
|
+
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
8510
|
+
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
8511
|
+
last.content.push(resultBlock);
|
|
8512
|
+
} else {
|
|
8513
|
+
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8516
|
+
} else if (ev.type === "llm_response") {
|
|
8517
|
+
usage = {
|
|
8518
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
8519
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
8520
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
8521
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
8522
|
+
};
|
|
8390
8523
|
}
|
|
8391
8524
|
}
|
|
8525
|
+
} catch {
|
|
8392
8526
|
}
|
|
8393
|
-
} catch {
|
|
8394
8527
|
}
|
|
8528
|
+
} finally {
|
|
8529
|
+
rl.close();
|
|
8530
|
+
stream.close();
|
|
8395
8531
|
}
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8532
|
+
let finalMessages = [];
|
|
8533
|
+
if (mode.full && messages !== void 0 && openToolUses !== void 0) {
|
|
8534
|
+
if (openToolUses.size > 0) {
|
|
8535
|
+
this.events?.emit("session.damaged", {
|
|
8536
|
+
sessionId: id,
|
|
8537
|
+
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
8538
|
+
});
|
|
8539
|
+
}
|
|
8540
|
+
const repaired = repairToolUseAdjacency(messages);
|
|
8541
|
+
if (repaired.report.changed) {
|
|
8542
|
+
this.events?.emit("session.damaged", {
|
|
8543
|
+
sessionId: id,
|
|
8544
|
+
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
8545
|
+
});
|
|
8546
|
+
}
|
|
8547
|
+
finalMessages = repaired.messages;
|
|
8408
8548
|
}
|
|
8409
8549
|
const meta = {
|
|
8410
8550
|
id,
|
|
@@ -8415,27 +8555,29 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8415
8555
|
pendingToolUses: sessionPendingToolUses
|
|
8416
8556
|
};
|
|
8417
8557
|
const toolCallEnds = extractToolCallEnds(events);
|
|
8418
|
-
const data = { metadata: meta, events, messages:
|
|
8419
|
-
if (
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8558
|
+
const data = { metadata: meta, events, messages: finalMessages, usage, toolCallEnds };
|
|
8559
|
+
if (mode.full) {
|
|
8560
|
+
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
8561
|
+
const oldest = this._loadCache.keys().next().value;
|
|
8562
|
+
if (oldest !== void 0) {
|
|
8563
|
+
this._loadCache.delete(oldest);
|
|
8564
|
+
}
|
|
8423
8565
|
}
|
|
8566
|
+
this._loadCache.set(id, { mtimeMs: stat7.mtimeMs, size: stat7.size, data });
|
|
8424
8567
|
}
|
|
8425
|
-
this._loadCache.set(id, { mtimeMs: stat7.mtimeMs, size: stat7.size, data });
|
|
8426
8568
|
return data;
|
|
8427
8569
|
} catch (err) {
|
|
8428
8570
|
outcome = "failure";
|
|
8429
8571
|
errorMsg = toErrorMessage(err);
|
|
8430
8572
|
throw err;
|
|
8431
8573
|
} finally {
|
|
8432
|
-
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
8574
|
+
this.emitRead(id, file, mode.full ? "load" : "load_events_only", outcome, Date.now() - t0, errorMsg);
|
|
8433
8575
|
if (cacheHit) {
|
|
8434
8576
|
this.events?.emit("storage.cache_hit", {
|
|
8435
8577
|
sessionId: id,
|
|
8436
8578
|
store: "session",
|
|
8437
8579
|
filePath: file,
|
|
8438
|
-
operation: "load",
|
|
8580
|
+
operation: mode.full ? "load" : "load_events_only",
|
|
8439
8581
|
durationMs: Date.now() - t0
|
|
8440
8582
|
});
|
|
8441
8583
|
}
|
|
@@ -8583,14 +8725,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8583
8725
|
async appendToIndex(summary) {
|
|
8584
8726
|
try {
|
|
8585
8727
|
await ensureDir(this.dir);
|
|
8586
|
-
|
|
8587
|
-
await
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
this.indexAppendCount
|
|
8728
|
+
let shouldCompact = false;
|
|
8729
|
+
await withFileLock(this.indexFile, async () => {
|
|
8730
|
+
const line = JSON.stringify(summary) + "\n";
|
|
8731
|
+
await fsp7.appendFile(this.indexFile, line, "utf8");
|
|
8732
|
+
this._indexCache = null;
|
|
8733
|
+
this.invalidateShardManifestBySessionId(summary.id);
|
|
8734
|
+
this.indexAppendCount++;
|
|
8735
|
+
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
8736
|
+
shouldCompact = true;
|
|
8737
|
+
this.indexAppendCount = 0;
|
|
8738
|
+
}
|
|
8739
|
+
});
|
|
8740
|
+
if (shouldCompact) {
|
|
8741
|
+
await withFileLock(this.indexFile, () => this.compactIndexInner());
|
|
8594
8742
|
}
|
|
8595
8743
|
} catch {
|
|
8596
8744
|
}
|
|
@@ -8599,30 +8747,28 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8599
8747
|
async writeTombstone(id) {
|
|
8600
8748
|
try {
|
|
8601
8749
|
await ensureDir(this.dir);
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8750
|
+
await withFileLock(this.indexFile, async () => {
|
|
8751
|
+
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
8752
|
+
await fsp7.appendFile(this.indexFile, line, "utf8");
|
|
8753
|
+
this._indexCache = null;
|
|
8754
|
+
this.invalidateShardManifestBySessionId(id);
|
|
8755
|
+
this.indexAppendCount++;
|
|
8756
|
+
});
|
|
8607
8757
|
} catch {
|
|
8608
8758
|
}
|
|
8609
8759
|
}
|
|
8610
8760
|
/**
|
|
8611
8761
|
* Compact the index: read all entries, drop tombstones, deduplicate
|
|
8612
|
-
* (keep latest per session), and rewrite.
|
|
8762
|
+
* (keep latest per session), and rewrite atomically. Acquires the index
|
|
8763
|
+
* file lock so a concurrent append (this process or another wstack in the
|
|
8764
|
+
* same project) can't be overwritten by the rewrite.
|
|
8613
8765
|
*/
|
|
8614
8766
|
async compactIndex() {
|
|
8615
8767
|
const t0 = Date.now();
|
|
8616
8768
|
let outcome = "success";
|
|
8617
8769
|
let errorMsg;
|
|
8618
8770
|
try {
|
|
8619
|
-
|
|
8620
|
-
if (entries.length === 0) return;
|
|
8621
|
-
const tmp = `${this.indexFile}.compact.tmp`;
|
|
8622
|
-
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
8623
|
-
await fsp7.writeFile(tmp, lines, "utf8");
|
|
8624
|
-
await fsp7.rename(tmp, this.indexFile);
|
|
8625
|
-
this._indexCache = null;
|
|
8771
|
+
await withFileLock(this.indexFile, () => this.compactIndexInner());
|
|
8626
8772
|
} catch (err) {
|
|
8627
8773
|
outcome = "failure";
|
|
8628
8774
|
errorMsg = toErrorMessage(err);
|
|
@@ -8630,6 +8776,19 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8630
8776
|
this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
|
|
8631
8777
|
}
|
|
8632
8778
|
}
|
|
8779
|
+
/**
|
|
8780
|
+
* Lock-free compaction body. The caller MUST already hold the index file
|
|
8781
|
+
* lock (via withFileLock(this.indexFile, ...)). Uses atomicWrite for the
|
|
8782
|
+
* rewrite so the temp file gets a random suffix (no collision between two
|
|
8783
|
+
* compactions) and the Windows transient-EPERM rename retry.
|
|
8784
|
+
*/
|
|
8785
|
+
async compactIndexInner() {
|
|
8786
|
+
const entries = await this.readIndex();
|
|
8787
|
+
if (entries.length === 0) return;
|
|
8788
|
+
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
8789
|
+
await atomicWrite(this.indexFile, lines, { mode: 384 });
|
|
8790
|
+
this._indexCache = null;
|
|
8791
|
+
}
|
|
8633
8792
|
/**
|
|
8634
8793
|
* Read the index file and return deduplicated session summaries.
|
|
8635
8794
|
* Entries with a matching tombstone are filtered out.
|
|
@@ -8688,11 +8847,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8688
8847
|
const ids = await this.collectSessionIds(this.dir);
|
|
8689
8848
|
const summaries = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
8690
8849
|
const valid = summaries.filter((s) => s !== null);
|
|
8691
|
-
const tmp = `${this.indexFile}.tmp`;
|
|
8692
8850
|
const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
8693
|
-
await
|
|
8694
|
-
|
|
8695
|
-
|
|
8851
|
+
await withFileLock(this.indexFile, async () => {
|
|
8852
|
+
await atomicWrite(this.indexFile, lines, { mode: 384 });
|
|
8853
|
+
this._indexCache = null;
|
|
8854
|
+
});
|
|
8696
8855
|
return valid.length;
|
|
8697
8856
|
}
|
|
8698
8857
|
async listFromDirectoryScan(limit) {
|
|
@@ -8768,7 +8927,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8768
8927
|
return entry;
|
|
8769
8928
|
}
|
|
8770
8929
|
async collectSessionFilesInShard(shardKey) {
|
|
8771
|
-
const dir = shardKey ?
|
|
8930
|
+
const dir = shardKey ? path9.join(this.dir, shardKey) : this.dir;
|
|
8772
8931
|
const entries = await this.collectSessionFiles(dir, shardKey);
|
|
8773
8932
|
return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
|
|
8774
8933
|
}
|
|
@@ -8791,13 +8950,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8791
8950
|
if (entry.name === "_index.jsonl") continue;
|
|
8792
8951
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
8793
8952
|
const id = prefix ? `${prefix}/${base}` : base;
|
|
8794
|
-
files.push({ id, filePath:
|
|
8953
|
+
files.push({ id, filePath: path9.join(dir, entry.name) });
|
|
8795
8954
|
}
|
|
8796
8955
|
}
|
|
8797
8956
|
const childFileArrays = await Promise.all(
|
|
8798
8957
|
dirEntries.map((entry) => {
|
|
8799
8958
|
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
8800
|
-
return this.collectSessionFiles(
|
|
8959
|
+
return this.collectSessionFiles(path9.join(dir, entry.name), childPrefix, depth + 1);
|
|
8801
8960
|
})
|
|
8802
8961
|
);
|
|
8803
8962
|
return [...childFileArrays.flat(), ...files];
|
|
@@ -8830,7 +8989,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8830
8989
|
const childIdArrays = await Promise.all(
|
|
8831
8990
|
dirEntries.map((entry) => {
|
|
8832
8991
|
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
8833
|
-
return this.collectSessionIds(
|
|
8992
|
+
return this.collectSessionIds(path9.join(dir, entry.name), childPrefix, depth + 1);
|
|
8834
8993
|
})
|
|
8835
8994
|
);
|
|
8836
8995
|
return [...childIdArrays.flat(), ...fileIds];
|
|
@@ -8947,14 +9106,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8947
9106
|
async deleteSession(id) {
|
|
8948
9107
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
8949
9108
|
const summaryPath = this.sessionPath(id, ".summary.json");
|
|
8950
|
-
const shardDir =
|
|
8951
|
-
const base =
|
|
8952
|
-
const sessDir =
|
|
9109
|
+
const shardDir = path9.dirname(jsonlPath);
|
|
9110
|
+
const base = path9.basename(id);
|
|
9111
|
+
const sessDir = path9.join(shardDir, base);
|
|
8953
9112
|
const deletions = [
|
|
8954
9113
|
fsp7.unlink(jsonlPath),
|
|
8955
9114
|
fsp7.unlink(summaryPath),
|
|
8956
|
-
fsp7.unlink(
|
|
8957
|
-
fsp7.unlink(
|
|
9115
|
+
fsp7.unlink(sessionScopedPath(this.dir, id, ".plan.json")),
|
|
9116
|
+
fsp7.unlink(sessionScopedPath(this.dir, id, ".tasks.json")),
|
|
9117
|
+
fsp7.unlink(sessionScopedPath(this.dir, id, ".todos.json"))
|
|
8958
9118
|
];
|
|
8959
9119
|
const results = await Promise.allSettled(deletions);
|
|
8960
9120
|
for (const r of results) {
|
|
@@ -8990,14 +9150,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8990
9150
|
let deleted = 0;
|
|
8991
9151
|
let activeSessionId = null;
|
|
8992
9152
|
try {
|
|
8993
|
-
const raw = await fsp7.readFile(
|
|
9153
|
+
const raw = await fsp7.readFile(path9.join(this.dir, "active.json"), "utf8");
|
|
8994
9154
|
const active = JSON.parse(raw);
|
|
8995
9155
|
activeSessionId = active.sessionId ?? null;
|
|
8996
9156
|
} catch {
|
|
8997
9157
|
}
|
|
8998
9158
|
const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
8999
9159
|
const pruneFile = async (dir, name, prefix) => {
|
|
9000
|
-
const jsonlPath =
|
|
9160
|
+
const jsonlPath = path9.join(dir, name);
|
|
9001
9161
|
try {
|
|
9002
9162
|
const stat7 = await fsp7.stat(jsonlPath);
|
|
9003
9163
|
if (stat7.mtimeMs >= cutoff) return;
|
|
@@ -9017,7 +9177,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9017
9177
|
continue;
|
|
9018
9178
|
}
|
|
9019
9179
|
if (!entry.isDirectory()) continue;
|
|
9020
|
-
const dateDir =
|
|
9180
|
+
const dateDir = path9.join(this.dir, entry.name);
|
|
9021
9181
|
const files = await fsp7.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
9022
9182
|
for (const file of files) {
|
|
9023
9183
|
if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
|
|
@@ -9029,7 +9189,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
9029
9189
|
}
|
|
9030
9190
|
for (const entry of entries) {
|
|
9031
9191
|
if (!entry.isDirectory()) continue;
|
|
9032
|
-
const dateDir =
|
|
9192
|
+
const dateDir = path9.join(this.dir, entry.name);
|
|
9033
9193
|
try {
|
|
9034
9194
|
const remaining = await fsp7.readdir(dateDir);
|
|
9035
9195
|
if (remaining.length === 0) {
|
|
@@ -9160,9 +9320,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
9160
9320
|
let dir;
|
|
9161
9321
|
if (opts.store) {
|
|
9162
9322
|
store = opts.store;
|
|
9163
|
-
dir = opts.sessionsRoot ?
|
|
9323
|
+
dir = opts.sessionsRoot ? path9.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
9164
9324
|
} else if (opts.sessionsRoot) {
|
|
9165
|
-
dir =
|
|
9325
|
+
dir = path9.join(opts.sessionsRoot, runId);
|
|
9166
9326
|
store = new DefaultSessionStore({ dir });
|
|
9167
9327
|
} else {
|
|
9168
9328
|
throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
|
|
@@ -9474,7 +9634,7 @@ var FleetManager = class {
|
|
|
9474
9634
|
})),
|
|
9475
9635
|
usage: this.usage.snapshot()
|
|
9476
9636
|
};
|
|
9477
|
-
await fsp7.mkdir(
|
|
9637
|
+
await fsp7.mkdir(path9.dirname(this.manifestPath), { recursive: true });
|
|
9478
9638
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
9479
9639
|
return this.manifestPath;
|
|
9480
9640
|
}
|
|
@@ -9632,7 +9792,7 @@ var DefaultMailbox = class {
|
|
|
9632
9792
|
/** Counts malformed JSONL lines skipped during parsing for observability. */
|
|
9633
9793
|
_corruptionCount = 0;
|
|
9634
9794
|
constructor(sessionDir) {
|
|
9635
|
-
this.filePath =
|
|
9795
|
+
this.filePath = path9.join(sessionDir, MAILBOX_FILE);
|
|
9636
9796
|
}
|
|
9637
9797
|
get mailboxPath() {
|
|
9638
9798
|
return this.filePath;
|
|
@@ -9661,7 +9821,7 @@ var DefaultMailbox = class {
|
|
|
9661
9821
|
taskContext: input.taskContext
|
|
9662
9822
|
};
|
|
9663
9823
|
const line = JSON.stringify(msg) + LINE_SEPARATOR;
|
|
9664
|
-
await fsp7.mkdir(
|
|
9824
|
+
await fsp7.mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
9665
9825
|
await withFileLock(this.filePath, async () => {
|
|
9666
9826
|
await fsp7.appendFile(this.filePath, line, "utf8");
|
|
9667
9827
|
this._pushToCache(msg);
|
|
@@ -10103,6 +10263,7 @@ var BrainMonitor = class {
|
|
|
10103
10263
|
try {
|
|
10104
10264
|
const request = {
|
|
10105
10265
|
id: `brainmon-${randomUUID()}`,
|
|
10266
|
+
sessionId: this.opts.sessionId?.(),
|
|
10106
10267
|
source: "system",
|
|
10107
10268
|
question: input.question,
|
|
10108
10269
|
context: input.context,
|
|
@@ -10128,6 +10289,7 @@ var BrainMonitor = class {
|
|
|
10128
10289
|
const decision = await this.opts.brain.decide(request);
|
|
10129
10290
|
const intervened = await this.maybeIntervene(kind, request, decision);
|
|
10130
10291
|
this.opts.events.emit("brain.intervention", {
|
|
10292
|
+
sessionId: request.sessionId,
|
|
10131
10293
|
kind,
|
|
10132
10294
|
request,
|
|
10133
10295
|
decision,
|
|
@@ -10171,7 +10333,7 @@ var REGISTRY_CACHE_TTL_MS = 2e3;
|
|
|
10171
10333
|
var LINE_SEPARATOR2 = "\n";
|
|
10172
10334
|
var MESSAGE_CACHE_MAX_ENTRIES2 = 1e4;
|
|
10173
10335
|
function resolveProjectDir(projectRoot, globalRoot) {
|
|
10174
|
-
return
|
|
10336
|
+
return path9.join(globalRoot, "projects", projectSlug(projectRoot));
|
|
10175
10337
|
}
|
|
10176
10338
|
var GlobalMailbox = class {
|
|
10177
10339
|
/** Path to the JSONL message file. */
|
|
@@ -10224,14 +10386,14 @@ var GlobalMailbox = class {
|
|
|
10224
10386
|
* @param hqPublisher — optional HQ publisher, or getter, for cross-project telemetry
|
|
10225
10387
|
*/
|
|
10226
10388
|
constructor(projectDir, events, hqPublisher) {
|
|
10227
|
-
this.messagePath =
|
|
10228
|
-
this.registryPath =
|
|
10229
|
-
this.clientRegistryPath =
|
|
10389
|
+
this.messagePath = path9.join(projectDir, MAILBOX_FILE2);
|
|
10390
|
+
this.registryPath = path9.join(projectDir, "_mailbox.registry.json");
|
|
10391
|
+
this.clientRegistryPath = path9.join(projectDir, CLIENT_REGISTRY_FILE);
|
|
10230
10392
|
this._events = events;
|
|
10231
10393
|
this._hqPublisher = hqPublisher;
|
|
10232
10394
|
}
|
|
10233
10395
|
get hqMailboxId() {
|
|
10234
|
-
return `${
|
|
10396
|
+
return `${path9.basename(path9.dirname(this.messagePath))}:mailbox`;
|
|
10235
10397
|
}
|
|
10236
10398
|
get hqPublisher() {
|
|
10237
10399
|
return typeof this._hqPublisher === "function" ? this._hqPublisher() : this._hqPublisher;
|
|
@@ -10268,7 +10430,7 @@ var GlobalMailbox = class {
|
|
|
10268
10430
|
taskContext: input.taskContext
|
|
10269
10431
|
};
|
|
10270
10432
|
const line = JSON.stringify(msg) + LINE_SEPARATOR2;
|
|
10271
|
-
await fsp7.mkdir(
|
|
10433
|
+
await fsp7.mkdir(path9.dirname(this.messagePath), { recursive: true });
|
|
10272
10434
|
await withFileLock(this.messagePath, async () => {
|
|
10273
10435
|
await fsp7.appendFile(this.messagePath, line, "utf8");
|
|
10274
10436
|
this._pushToCache(msg);
|
|
@@ -10519,13 +10681,17 @@ var GlobalMailbox = class {
|
|
|
10519
10681
|
const client = registry.get(input.clientId);
|
|
10520
10682
|
if (client) {
|
|
10521
10683
|
client.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10684
|
+
if (typeof input.sessionId === "string" && input.sessionId.length > 0) {
|
|
10685
|
+
client.sessionId = input.sessionId;
|
|
10686
|
+
}
|
|
10522
10687
|
}
|
|
10523
10688
|
this._clientRegistryCache = registry;
|
|
10524
10689
|
this._clientRegistryCacheAt = Date.now();
|
|
10525
10690
|
await this._writeClientRegistry(registry);
|
|
10526
10691
|
});
|
|
10527
10692
|
this._events?.emitCustom("mailbox.client_heartbeat", {
|
|
10528
|
-
clientId: input.clientId
|
|
10693
|
+
clientId: input.clientId,
|
|
10694
|
+
...input.sessionId ? { sessionId: input.sessionId } : {}
|
|
10529
10695
|
});
|
|
10530
10696
|
this.publishHqMailboxSnapshot();
|
|
10531
10697
|
}
|
|
@@ -10763,7 +10929,7 @@ var GlobalMailbox = class {
|
|
|
10763
10929
|
this._messageCache.push(msg);
|
|
10764
10930
|
}
|
|
10765
10931
|
async _ensureRegistry() {
|
|
10766
|
-
await fsp7.mkdir(
|
|
10932
|
+
await fsp7.mkdir(path9.dirname(this.registryPath), { recursive: true });
|
|
10767
10933
|
}
|
|
10768
10934
|
async _readRegistry(opts) {
|
|
10769
10935
|
if (!opts?.fresh && this._registryCache && Date.now() - this._registryCacheAt < REGISTRY_CACHE_TTL_MS) {
|
|
@@ -10808,7 +10974,7 @@ var GlobalMailbox = class {
|
|
|
10808
10974
|
}
|
|
10809
10975
|
// ── Client registry internals ───────────────────────────────────────────
|
|
10810
10976
|
async _ensureClientRegistry() {
|
|
10811
|
-
await fsp7.mkdir(
|
|
10977
|
+
await fsp7.mkdir(path9.dirname(this.clientRegistryPath), { recursive: true });
|
|
10812
10978
|
}
|
|
10813
10979
|
async _readClientRegistry(opts) {
|
|
10814
10980
|
if (!opts?.fresh && this._clientRegistryCache && Date.now() - this._clientRegistryCacheAt < REGISTRY_CACHE_TTL_MS) {
|
|
@@ -10855,8 +11021,8 @@ var GlobalMailbox = class {
|
|
|
10855
11021
|
var MAILBOX_BRIDGE_LOCK_FILENAME = ".mailbox-bridge.lock";
|
|
10856
11022
|
var MAILBOX_BRIDGE_TOKEN_FILENAME = ".mailbox.token";
|
|
10857
11023
|
async function acquireOrJoin(opts) {
|
|
10858
|
-
const lockPath =
|
|
10859
|
-
const tokenPath =
|
|
11024
|
+
const lockPath = path9.join(opts.projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
11025
|
+
const tokenPath = path9.join(opts.projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
10860
11026
|
const inspected = await readLockForInspection(lockPath);
|
|
10861
11027
|
if (inspected.kind === "live") {
|
|
10862
11028
|
const existing = inspected.lock;
|
|
@@ -10884,8 +11050,8 @@ async function acquireOrJoin(opts) {
|
|
|
10884
11050
|
return { kind: "acquired", lock: tentative, tokenPath };
|
|
10885
11051
|
}
|
|
10886
11052
|
async function finalize(projectDir, tentative, boundPort) {
|
|
10887
|
-
const lockPath =
|
|
10888
|
-
const tokenPath =
|
|
11053
|
+
const lockPath = path9.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
11054
|
+
const tokenPath = path9.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
10889
11055
|
const finalized = {
|
|
10890
11056
|
...tentative,
|
|
10891
11057
|
port: boundPort,
|
|
@@ -10896,8 +11062,8 @@ async function finalize(projectDir, tentative, boundPort) {
|
|
|
10896
11062
|
return finalized;
|
|
10897
11063
|
}
|
|
10898
11064
|
async function release(projectDir, generation) {
|
|
10899
|
-
const lockPath =
|
|
10900
|
-
const tokenPath =
|
|
11065
|
+
const lockPath = path9.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
11066
|
+
const tokenPath = path9.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
|
|
10901
11067
|
try {
|
|
10902
11068
|
const raw = await fsp7.readFile(lockPath, "utf-8");
|
|
10903
11069
|
const parsed = JSON.parse(raw);
|
|
@@ -10931,7 +11097,7 @@ async function readLockForInspection(lockPath) {
|
|
|
10931
11097
|
return { kind: "live", lock: parsed };
|
|
10932
11098
|
}
|
|
10933
11099
|
async function readLiveLock(projectDir) {
|
|
10934
|
-
const lockPath =
|
|
11100
|
+
const lockPath = path9.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
|
|
10935
11101
|
const result = await readLockForInspection(lockPath);
|
|
10936
11102
|
if (result.kind === "live") {
|
|
10937
11103
|
return { kind: "live", lock: result.lock };
|
|
@@ -10942,7 +11108,7 @@ async function readLiveLock(projectDir) {
|
|
|
10942
11108
|
return { kind: "absent" };
|
|
10943
11109
|
}
|
|
10944
11110
|
async function atomicWriteJson(targetPath, value) {
|
|
10945
|
-
const dir =
|
|
11111
|
+
const dir = path9.dirname(targetPath);
|
|
10946
11112
|
await fsp7.mkdir(dir, { recursive: true });
|
|
10947
11113
|
const tmp = `${targetPath}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
10948
11114
|
const body = JSON.stringify(value, null, 2) + "\n";
|
|
@@ -11228,9 +11394,9 @@ function makeMailboxTool(opts = {}) {
|
|
|
11228
11394
|
const shortHint = "Sub-commands: check (unread), send (to/broadcast), ack (read/complete), query (filter), status (all agents), online (active only), unread (count).";
|
|
11229
11395
|
return {
|
|
11230
11396
|
name: "mailbox",
|
|
11231
|
-
description: "
|
|
11232
|
-
usageHint: shortHint,
|
|
11233
|
-
category: "
|
|
11397
|
+
description: "Low-level inter-agent mailbox with 7 actions (check, send, ack, query, status, online, unread). For most use cases, prefer the simpler `mail_send` and `mail_inbox` tools \u2014 they cover the common send/read operations with a cleaner interface. This tool is for advanced queries (filter by sender, priority, since-timestamp), agent status/online lists, and message ack/control.",
|
|
11398
|
+
usageHint: shortHint + " For simple send/read, use mail_send / mail_inbox instead.",
|
|
11399
|
+
category: "Coordination",
|
|
11234
11400
|
permission: "auto",
|
|
11235
11401
|
mutating: true,
|
|
11236
11402
|
capabilities: [ToolCapabilities.COORDINATION_MAIL],
|
|
@@ -11243,7 +11409,7 @@ function makeMailboxTool(opts = {}) {
|
|
|
11243
11409
|
description: "Which mailbox operation to perform."
|
|
11244
11410
|
},
|
|
11245
11411
|
to: { type: "string", description: "Recipient agent id, or '*' / 'all' for broadcast." },
|
|
11246
|
-
type: { type: "string", enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result"], description: "Message type." },
|
|
11412
|
+
type: { type: "string", enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result", "review"], description: "Message type." },
|
|
11247
11413
|
subject: { type: "string", description: "Short subject line." },
|
|
11248
11414
|
body: { type: "string", description: "Full message content." },
|
|
11249
11415
|
priority: { type: "string", enum: ["low", "normal", "high"] },
|
|
@@ -11492,9 +11658,9 @@ function makeMailSendTool(opts = {}) {
|
|
|
11492
11658
|
const resolveMailbox = makeResolver(opts);
|
|
11493
11659
|
return {
|
|
11494
11660
|
name: "mail_send",
|
|
11495
|
-
description: 'Send a mail to other agents working on this project (other terminals, TUIs, WebUIs). Use it to hand off work
|
|
11496
|
-
usageHint: 'mail_send to="
|
|
11497
|
-
category: "
|
|
11661
|
+
description: 'Send a mail to other agents working on this project (other terminals, TUIs, WebUIs). Use it to hand off work, ask questions, announce what you just did, or request a review (type="review" \u2014 passive ask, no immediate reply required). to="*" broadcasts to everyone; to="leader" reaches every leader process; an exact id like "leader@a1b2c3d4" reaches one agent. Recipients see your mail automatically before their next step. Pick the type that matches the intent: note (default), ask (blocking question), assign (task), steer (mid-task direction), result (completion notice), review (passive ask), btw/status/broadcast/control (informational).',
|
|
11662
|
+
usageHint: 'mail_send to="<id>" type="review" body="please skim <file>"',
|
|
11663
|
+
category: "Coordination",
|
|
11498
11664
|
permission: "auto",
|
|
11499
11665
|
mutating: true,
|
|
11500
11666
|
capabilities: [ToolCapabilities.COORDINATION_MAIL],
|
|
@@ -11509,8 +11675,8 @@ function makeMailSendTool(opts = {}) {
|
|
|
11509
11675
|
body: { type: "string", description: "The message." },
|
|
11510
11676
|
type: {
|
|
11511
11677
|
type: "string",
|
|
11512
|
-
enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result"],
|
|
11513
|
-
description: 'Message intent. Default: "broadcast" when to="*", otherwise "note".'
|
|
11678
|
+
enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result", "review"],
|
|
11679
|
+
description: 'Message intent. Default: "broadcast" when to="*", otherwise "note". Actionable types: ask (blocking question), assign (task), result (completion notice), review (passive ask \u2014 code/doc/PR review, no immediate reply required). Behavioral: steer (mid-task direction change), btw (low-priority aside). Informational: note/status/broadcast/control.'
|
|
11514
11680
|
},
|
|
11515
11681
|
priority: { type: "string", enum: ["low", "normal", "high"] },
|
|
11516
11682
|
replyTo: { type: "string", description: "Message id this replies to." }
|
|
@@ -11552,9 +11718,9 @@ function makeMailInboxTool(opts = {}) {
|
|
|
11552
11718
|
const resolveMailbox = makeResolver(opts);
|
|
11553
11719
|
return {
|
|
11554
11720
|
name: "mail_inbox",
|
|
11555
|
-
description: 'Read your unread mail from other agents on this project and mark it read. Covers mail addressed to you directly, to your base name (e.g. "leader"), and broadcasts ("*"). Urgent steer/btw mail is already injected automatically \u2014 use this to catch up on notes, questions, handoffs
|
|
11721
|
+
description: 'Read your unread mail from other agents on this project and mark it read. Covers mail addressed to you directly, to your base name (e.g. "leader"), and broadcasts ("*"). Urgent steer/btw mail is already injected automatically \u2014 use this to catch up on notes, questions, handoffs, results, and review requests (type="review" \u2014 passive asks where no reply is required). Best called after a long stretch of tool work.',
|
|
11556
11722
|
usageHint: "mail_inbox (optionally: limit=10, markRead=false to peek)",
|
|
11557
|
-
category: "
|
|
11723
|
+
category: "Coordination",
|
|
11558
11724
|
permission: "auto",
|
|
11559
11725
|
mutating: false,
|
|
11560
11726
|
capabilities: [ToolCapabilities.COORDINATION_MAIL],
|
|
@@ -11805,7 +11971,7 @@ function createMailboxHooks(opts) {
|
|
|
11805
11971
|
var DEFAULT_MAX_ENTRIES = 1e4;
|
|
11806
11972
|
var LOG_FILENAME = "package-authors.json";
|
|
11807
11973
|
function logPath(storageDir) {
|
|
11808
|
-
return
|
|
11974
|
+
return path9.join(storageDir, LOG_FILENAME);
|
|
11809
11975
|
}
|
|
11810
11976
|
async function loadLog(storageDir, projectRoot) {
|
|
11811
11977
|
try {
|
|
@@ -11829,7 +11995,7 @@ async function saveLog(storageDir, log) {
|
|
|
11829
11995
|
await fsp7.rename(tmp, logPath(storageDir));
|
|
11830
11996
|
}
|
|
11831
11997
|
function detectEcosystem(manifestPath) {
|
|
11832
|
-
const name =
|
|
11998
|
+
const name = path9.basename(manifestPath).toLowerCase();
|
|
11833
11999
|
if (name === "package.json") return "npm";
|
|
11834
12000
|
if (name === "go.mod") return "go";
|
|
11835
12001
|
if (name === "cargo.toml") return "cargo";
|
|
@@ -12107,13 +12273,27 @@ var KnowledgeGraph = class {
|
|
|
12107
12273
|
pendingDeliveries = /* @__PURE__ */ new Map();
|
|
12108
12274
|
filePath;
|
|
12109
12275
|
graphFilePath;
|
|
12276
|
+
/**
|
|
12277
|
+
* Stable per-node insertion sequence. `nodes` (a Map) preserves insertion
|
|
12278
|
+
* order even across `update()` (Map.set on an existing key keeps its slot),
|
|
12279
|
+
* but the type index's `Set<string>` does NOT — `update()` removes then
|
|
12280
|
+
* re-adds a node's id, moving it to the set's tail. Index-routed queries sort
|
|
12281
|
+
* by this sequence so they return nodes in creation order, matching the old
|
|
12282
|
+
* `nodes.values()` scan that callers like decision-history `slice(-10)` rely on.
|
|
12283
|
+
*/
|
|
12284
|
+
seq = /* @__PURE__ */ new Map();
|
|
12285
|
+
seqCounter = 0;
|
|
12286
|
+
/** Assign a stable insertion sequence the first time a node id is seen. */
|
|
12287
|
+
_trackSeq(id) {
|
|
12288
|
+
if (!this.seq.has(id)) this.seq.set(id, this.seqCounter++);
|
|
12289
|
+
}
|
|
12110
12290
|
/** Exposed for unit-testing only: read current index contents. */
|
|
12111
12291
|
getIndex() {
|
|
12112
12292
|
return this.index;
|
|
12113
12293
|
}
|
|
12114
12294
|
constructor(sessionDir) {
|
|
12115
|
-
this.filePath =
|
|
12116
|
-
this.graphFilePath =
|
|
12295
|
+
this.filePath = path9.join(sessionDir, "_knowledge_graph");
|
|
12296
|
+
this.graphFilePath = path9.join(this.filePath, "graph.jsonl");
|
|
12117
12297
|
}
|
|
12118
12298
|
// ── Write ──────────────────────────────────────────────────────────────
|
|
12119
12299
|
/**
|
|
@@ -12123,6 +12303,7 @@ var KnowledgeGraph = class {
|
|
|
12123
12303
|
async add(node) {
|
|
12124
12304
|
const full = { id: randomUUID(), ...node };
|
|
12125
12305
|
this.nodes.set(full.id, full);
|
|
12306
|
+
this._trackSeq(full.id);
|
|
12126
12307
|
this._addToIndex(full, this._indexKeys(full));
|
|
12127
12308
|
await this._persist(full);
|
|
12128
12309
|
this._deliver(full);
|
|
@@ -12145,7 +12326,19 @@ var KnowledgeGraph = class {
|
|
|
12145
12326
|
return this.nodes.get(id);
|
|
12146
12327
|
}
|
|
12147
12328
|
getAll(filter) {
|
|
12148
|
-
|
|
12329
|
+
const f = filter ?? {};
|
|
12330
|
+
if (f.type) {
|
|
12331
|
+
const ids = this.index.get(`type:${f.type}`);
|
|
12332
|
+
if (!ids || ids.size === 0) return [];
|
|
12333
|
+
const out = [];
|
|
12334
|
+
for (const id of ids) {
|
|
12335
|
+
const node = this.nodes.get(id);
|
|
12336
|
+
if (node && this._matches(node, f)) out.push(node);
|
|
12337
|
+
}
|
|
12338
|
+
out.sort((a, b) => (this.seq.get(a.id) ?? 0) - (this.seq.get(b.id) ?? 0));
|
|
12339
|
+
return out;
|
|
12340
|
+
}
|
|
12341
|
+
return Array.from(this.nodes.values()).filter((n) => this._matches(n, f));
|
|
12149
12342
|
}
|
|
12150
12343
|
getGoals(filter) {
|
|
12151
12344
|
return this.getAll({ type: "goal", ...filter });
|
|
@@ -12321,9 +12514,11 @@ var KnowledgeGraph = class {
|
|
|
12321
12514
|
this._removeFromIndex(oldNode, this._indexKeys(oldNode));
|
|
12322
12515
|
}
|
|
12323
12516
|
this.nodes.set(parsed.node.id, parsed.node);
|
|
12517
|
+
this._trackSeq(parsed.node.id);
|
|
12324
12518
|
this._addToIndex(parsed.node, this._indexKeys(parsed.node));
|
|
12325
12519
|
} else {
|
|
12326
12520
|
this.nodes.set(parsed.id, parsed);
|
|
12521
|
+
this._trackSeq(parsed.id);
|
|
12327
12522
|
this._addToIndex(parsed, this._indexKeys(parsed));
|
|
12328
12523
|
}
|
|
12329
12524
|
} catch {
|
|
@@ -13353,11 +13548,12 @@ var AutonomousBrain = class {
|
|
|
13353
13548
|
};
|
|
13354
13549
|
}
|
|
13355
13550
|
_inferDecisionType(req) {
|
|
13356
|
-
|
|
13357
|
-
if (
|
|
13358
|
-
if (
|
|
13359
|
-
if (
|
|
13360
|
-
if (
|
|
13551
|
+
const q = req.question.toLowerCase();
|
|
13552
|
+
if (q.includes("spawn")) return "spawn";
|
|
13553
|
+
if (q.includes("approve") || q.includes("change")) return "approve_change";
|
|
13554
|
+
if (q.includes("retry") || q.includes("fail")) return "retry_task";
|
|
13555
|
+
if (q.includes("priorit")) return "prioritize_goals";
|
|
13556
|
+
if (q.includes("decompos")) return "decompose_goal";
|
|
13361
13557
|
return "assign_task";
|
|
13362
13558
|
}
|
|
13363
13559
|
_serializeContext(ctx) {
|
|
@@ -14214,17 +14410,17 @@ ${input.detail}`
|
|
|
14214
14410
|
_waitForDagProgress(timeoutMs) {
|
|
14215
14411
|
const before = this._dagProgressKey();
|
|
14216
14412
|
if (this.dag.isDone()) return Promise.resolve();
|
|
14217
|
-
return new Promise((
|
|
14413
|
+
return new Promise((resolve6) => {
|
|
14218
14414
|
let off;
|
|
14219
14415
|
const timer = setTimeout(() => {
|
|
14220
14416
|
off?.();
|
|
14221
|
-
|
|
14417
|
+
resolve6();
|
|
14222
14418
|
}, timeoutMs);
|
|
14223
14419
|
off = this.dag.onEvent(() => {
|
|
14224
14420
|
if (this._dagProgressKey() === before) return;
|
|
14225
14421
|
clearTimeout(timer);
|
|
14226
14422
|
off?.();
|
|
14227
|
-
|
|
14423
|
+
resolve6();
|
|
14228
14424
|
});
|
|
14229
14425
|
});
|
|
14230
14426
|
}
|
|
@@ -14501,6 +14697,7 @@ ${input.detail}`
|
|
|
14501
14697
|
var AgentMonitorService = class {
|
|
14502
14698
|
_fleetBus;
|
|
14503
14699
|
_events;
|
|
14700
|
+
_sessionId;
|
|
14504
14701
|
_transcriptsDir;
|
|
14505
14702
|
_maxEntries;
|
|
14506
14703
|
_streamEnabled;
|
|
@@ -14516,6 +14713,7 @@ var AgentMonitorService = class {
|
|
|
14516
14713
|
constructor(opts) {
|
|
14517
14714
|
this._fleetBus = opts.fleetBus;
|
|
14518
14715
|
this._events = opts.events;
|
|
14716
|
+
this._sessionId = opts.sessionId;
|
|
14519
14717
|
this._transcriptsDir = opts.transcriptsDir;
|
|
14520
14718
|
this._maxEntries = opts.maxEntriesPerAgent ?? 500;
|
|
14521
14719
|
this._streamEnabled = opts.streamEnabled ?? false;
|
|
@@ -14600,6 +14798,7 @@ var AgentMonitorService = class {
|
|
|
14600
14798
|
iteration: 0
|
|
14601
14799
|
});
|
|
14602
14800
|
this._events.emit("agent.status_changed", {
|
|
14801
|
+
sessionId: this._sessionId,
|
|
14603
14802
|
subagentId,
|
|
14604
14803
|
agentName,
|
|
14605
14804
|
status: "spawned",
|
|
@@ -14624,6 +14823,7 @@ var AgentMonitorService = class {
|
|
|
14624
14823
|
iteration: 999
|
|
14625
14824
|
});
|
|
14626
14825
|
this._events.emit("agent.status_changed", {
|
|
14826
|
+
sessionId: this._sessionId,
|
|
14627
14827
|
subagentId,
|
|
14628
14828
|
agentName: session.agentName,
|
|
14629
14829
|
status,
|
|
@@ -14729,6 +14929,7 @@ var AgentMonitorService = class {
|
|
|
14729
14929
|
this._appendToFile(subagentId, entry).catch(() => {
|
|
14730
14930
|
});
|
|
14731
14931
|
this._events.emit("agent.timeline.message", {
|
|
14932
|
+
sessionId: this._sessionId,
|
|
14732
14933
|
subagentId: entry.subagentId,
|
|
14733
14934
|
agentName: entry.agentName,
|
|
14734
14935
|
content: entry.content,
|
|
@@ -14741,9 +14942,9 @@ var AgentMonitorService = class {
|
|
|
14741
14942
|
this._onEntry?.(entry);
|
|
14742
14943
|
}
|
|
14743
14944
|
async _appendToFile(subagentId, entry) {
|
|
14744
|
-
const dir =
|
|
14945
|
+
const dir = path9.join(this._transcriptsDir, subagentId);
|
|
14745
14946
|
await fsp7.mkdir(dir, { recursive: true });
|
|
14746
|
-
const filePath =
|
|
14947
|
+
const filePath = path9.join(dir, "transcript.jsonl");
|
|
14747
14948
|
const line = JSON.stringify(entry) + "\n";
|
|
14748
14949
|
await fsp7.appendFile(filePath, line, { encoding: "utf8" });
|
|
14749
14950
|
}
|
|
@@ -14935,6 +15136,6 @@ var AdaptiveConcurrencyController = class {
|
|
|
14935
15136
|
}
|
|
14936
15137
|
};
|
|
14937
15138
|
|
|
14938
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AdaptiveConcurrencyController, AgentMonitorService, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MAILBOX_BRIDGE_LOCK_FILENAME, MAILBOX_BRIDGE_TOKEN_FILENAME, MAILBOX_HEALTH_DEFAULT_FAILURE_THRESHOLD, MAILBOX_HEALTH_DEFAULT_FROM, MAILBOX_HEALTH_DEFAULT_INTERVAL_MS, MAILBOX_HEALTH_DEFAULT_TIMEOUT_MS, MEDIUM_BUDGET, META_AGENTS, MailboxHealthWatchdog, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, acquireOrJoin, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, buildDownAlert, buildRecoveryAlert, composeDirectorPrompt, composeSubagentPrompt, createAgentMonitorService, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, finalize, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, readLiveLock, recordPackageAction, release, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, validateWatchdogOptions, withDisabledToolFiltering };
|
|
15139
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AdaptiveConcurrencyController, AgentMonitorService, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MAILBOX_BRIDGE_LOCK_FILENAME, MAILBOX_BRIDGE_TOKEN_FILENAME, MAILBOX_HEALTH_DEFAULT_FAILURE_THRESHOLD, MAILBOX_HEALTH_DEFAULT_FROM, MAILBOX_HEALTH_DEFAULT_INTERVAL_MS, MAILBOX_HEALTH_DEFAULT_TIMEOUT_MS, MEDIUM_BUDGET, META_AGENTS, MailboxHealthWatchdog, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, acquireOrJoin, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, buildDownAlert, buildRecoveryAlert, composeDirectorPrompt, composeSubagentPrompt, createAgentMonitorService, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, finalize, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetTool as makeFleetHealthTool, makeFleetTool as makeFleetSessionTool, makeFleetTool as makeFleetStatusTool, makeFleetTool, makeFleetTool as makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateAllTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, readLiveLock, recordPackageAction, release, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, validateWatchdogOptions, withDisabledToolFiltering };
|
|
14939
15140
|
//# sourceMappingURL=index.js.map
|
|
14940
15141
|
//# sourceMappingURL=index.js.map
|