@wrongstack/core 0.275.1 → 0.276.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{agent-bridge-D9JkPvJ0.d.ts → agent-bridge-D7A-eu3C.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-CArSFKFl.d.ts → agent-subagent-runner-CEuw4ATz.d.ts} +16 -10
  3. package/dist/{brain-DCkB5_e7.d.ts → brain-BLOyN5ZP.d.ts} +127 -1
  4. package/dist/{compactor-CzSvxM1g.d.ts → compactor-DcBpaJsI.d.ts} +1 -1
  5. package/dist/{config-BzFRKkg7.d.ts → config-Bf5mj-ad.d.ts} +20 -2
  6. package/dist/{context-BrLe8pJy.d.ts → context-CLnUMW5g.d.ts} +40 -2
  7. package/dist/coordination/index.d.ts +43 -24
  8. package/dist/coordination/index.js +849 -648
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +28 -28
  11. package/dist/defaults/index.js +1636 -845
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +16 -16
  14. package/dist/execution/index.js +218 -49
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +7 -7
  18. package/dist/extension/index.js.map +1 -1
  19. package/dist/{global-mailbox-CXkugtNQ.d.ts → global-mailbox-Iqfkgmwu.d.ts} +3 -3
  20. package/dist/{goal-store-DUwdbdoY.d.ts → goal-store-DGb6b5Ed.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +6 -6
  22. package/dist/hq/index.js +178 -75
  23. package/dist/hq/index.js.map +1 -1
  24. package/dist/{index-CtlizLTK.d.ts → index-Cn0NOshr.d.ts} +10 -5
  25. package/dist/{index-neOCEy6q.d.ts → index-L4RZN9jJ.d.ts} +2 -2
  26. package/dist/index.d.ts +56 -48
  27. package/dist/index.js +2789 -1546
  28. package/dist/index.js.map +1 -1
  29. package/dist/infrastructure/index.d.ts +6 -6
  30. package/dist/infrastructure/index.js +26 -7
  31. package/dist/infrastructure/index.js.map +1 -1
  32. package/dist/kernel/index.d.ts +20 -12
  33. package/dist/kernel/index.js +55 -9
  34. package/dist/kernel/index.js.map +1 -1
  35. package/dist/{mailbox-types-_7gaY0Rl.d.ts → mailbox-types-DTl7bRH3.d.ts} +3 -1
  36. package/dist/{mcp-servers-MLL6bMlv.d.ts → mcp-servers-CuZGf9fI.d.ts} +4 -4
  37. package/dist/models/index.d.ts +5 -5
  38. package/dist/models/index.js +223 -139
  39. package/dist/models/index.js.map +1 -1
  40. package/dist/{models-registry-CrkcxQ-g.d.ts → models-registry-8XOdxWQu.d.ts} +16 -1
  41. package/dist/{multi-agent-coordinator-Dc_HuG9p.d.ts → multi-agent-coordinator-CiRtKVTk.d.ts} +8 -1
  42. package/dist/{null-fleet-bus-BMZwMin7.d.ts → null-fleet-bus-d9G-bVy9.d.ts} +26 -22
  43. package/dist/observability/index.d.ts +2 -2
  44. package/dist/{path-resolver-uVK4BatM.d.ts → path-resolver-BhIb6mtd.d.ts} +8 -3
  45. package/dist/{permission-CJR1qfOi.d.ts → permission-BCbQDR2s.d.ts} +1 -1
  46. package/dist/{permission-policy-DLVKKk4w.d.ts → permission-policy-C0ikndX_.d.ts} +2 -18
  47. package/dist/{pipeline-BYR-Vdau.d.ts → pipeline-Dl6XbfE7.d.ts} +10 -6
  48. package/dist/{provider-model-resolve-iREK_1lG.d.ts → provider-model-resolve-B70epO19.d.ts} +3 -3
  49. package/dist/{provider-runner-i7SQXZuC.d.ts → provider-runner-DZ808MSM.d.ts} +3 -3
  50. package/dist/{retry-policy-BmY5ooh3.d.ts → retry-policy-Dt3_z8Aj.d.ts} +1 -1
  51. package/dist/sdd/index.d.ts +19 -10
  52. package/dist/sdd/index.js +411 -240
  53. package/dist/sdd/index.js.map +1 -1
  54. package/dist/{secret-vault-C9leEMzr.d.ts → secret-vault-BUJ2d1gB.d.ts} +1 -1
  55. package/dist/security/index.d.ts +5 -5
  56. package/dist/security/index.js +30 -6
  57. package/dist/security/index.js.map +1 -1
  58. package/dist/{selector-qjpee9BF.d.ts → selector-BCkWgdwy.d.ts} +1 -1
  59. package/dist/{session-event-bridge-m7y--I-H.d.ts → session-event-bridge-CMvIO59_.d.ts} +1 -1
  60. package/dist/{session-reader-BjLH4V9n.d.ts → session-reader-C8aiChUu.d.ts} +1 -1
  61. package/dist/skills/index.js +1 -0
  62. package/dist/skills/index.js.map +1 -1
  63. package/dist/storage/index.d.ts +68 -30
  64. package/dist/storage/index.js +839 -528
  65. package/dist/storage/index.js.map +1 -1
  66. package/dist/{strategy-compactor-C2bmlWYg.d.ts → strategy-compactor-DI1OHVbB.d.ts} +10 -10
  67. package/dist/{todos-checkpoint-oDS9IBNS.d.ts → todos-checkpoint-Ddd2CGr0.d.ts} +56 -9
  68. package/dist/{tool-executor-D4YdaJ-M.d.ts → tool-executor-Bmd5Ygoo.d.ts} +45 -10
  69. package/dist/tools/index.d.ts +2 -2
  70. package/dist/tools/index.js.map +1 -1
  71. package/dist/types/index.d.ts +20 -20
  72. package/dist/types/index.js +331 -98
  73. package/dist/types/index.js.map +1 -1
  74. package/dist/utils/index.d.ts +16 -3
  75. package/dist/utils/index.js +159 -83
  76. package/dist/utils/index.js.map +1 -1
  77. package/dist/{worktree-manager-A1Efnvs0.d.ts → worktree-manager-DBdl_5rs.d.ts} +4 -1
  78. package/instructions/agents/shadow-agent.md +3 -3
  79. package/instructions/coordination/director-preamble.md +3 -3
  80. package/instructions/modes/research-web.md +4 -4
  81. package/package.json +1 -1
  82. package/skills/research-web/SKILL.md +26 -26
  83. 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 path8 from 'path';
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((resolve5) => {
63
- const entry = { request, resolve: resolve5 };
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
- resolve5({ type: "deny", reason: "Brain human decision timed out." });
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/safe-json.ts
448
- function safeParse(input, maxBytes = 5e6) {
449
- if (input.length > maxBytes) {
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(path8.basename(absRoot));
685
- const hash = createHash("sha256").update(path8.resolve(absRoot)).digest("hex").slice(0, 6);
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 path8.resolve(fromEnv);
694
- return path8.join(os.homedir(), ".wrongstack");
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
- describe() {
730
- const ctx = this.context ? ` ${formatContext(this.context)}` : "";
731
- return `${this.code}: ${this.message}${ctx}`;
728
+ async releaseLock() {
729
+ return releaseDirectorStateLock(this.lockPath);
732
730
  }
733
- };
734
- function formatContext(ctx) {
735
- const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
736
- return parts.length > 0 ? `[${parts.join(" ")}]` : "";
737
- }
738
- var AgentError = class extends WrongStackError {
739
- constructor(opts) {
740
- super({
741
- message: opts.message,
742
- code: opts.code,
743
- subsystem: "agent",
744
- severity: opts.code === ERROR_CODES.AGENT_ABORTED ? "warning" : "error",
745
- recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,
746
- context: opts.context,
747
- cause: opts.cause
748
- });
749
- this.name = "AgentError";
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((resolve5, reject) => {
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: resolve5,
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", "fleet_status", "read", "grep", "glob", "bash", "write"],
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(path8.join(root, relativePath), "utf8").trimEnd();
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 = path8.dirname(fileURLToPath(import.meta.url));
1595
+ const here = path9.dirname(fileURLToPath(import.meta.url));
1526
1596
  const candidates = [
1527
- path8.resolve(here, "../../instructions"),
1528
- path8.resolve(here, "../instructions"),
1529
- path8.resolve(here, "instructions")
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(path8.join(dir, fileName), "utf8").trimEnd();
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 = path8.dirname(fileURLToPath(import.meta.url));
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"] || path8.join(os.homedir(), ".wrongstack");
1737
+ const globalRoot = process.env["WRONGSTACK_HOME"] || path9.join(os.homedir(), ".wrongstack");
1668
1738
  const candidates = [
1669
- ...explicitDir ? [path8.resolve(explicitDir)] : [],
1670
- path8.join(globalRoot, "instructions", "agents"),
1671
- path8.resolve(here, "../../../../instructions/agents"),
1672
- path8.resolve(here, "../../../instructions/agents"),
1673
- path8.resolve(here, "../../instructions/agents"),
1674
- path8.resolve(here, "../instructions/agents"),
1675
- path8.resolve(here, "instructions/agents")
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 makeFleetStatusTool(director) {
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: "fleet_session",
3532
- description: "Read a subagent's JSONL transcript and extract its last assistant text, stop reason, and tool-use count. Use this to see what a running or timed-out subagent actually produced.",
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
- subagentId: { type: "string", description: "Subagent id to read the transcript of." },
3540
- tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
3541
- },
3542
- required: ["subagentId"]
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 result = await director.readSession(i.subagentId, i.tail);
3547
- if (!result) {
3548
- return {
3549
- error: `fleet_session: transcript unavailable for "${i.subagentId}". Is sessionsRoot configured?`
3550
- };
3551
- }
3552
- return result;
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
- id: s.id,
3574
- status: s.status,
3575
- lastEventAt: usage?.lastEventAt,
3576
- budgetPressure: {
3577
- iterations: usage?.iterations,
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((resolve5) => {
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
- resolve5(d);
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: { ...stats, subagentStatuses }
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((resolve5, reject) => {
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
- resolve5(result);
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
- maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
5229
- maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
5230
- maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
5231
- maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
5232
- // Wall-clock cap is opt-in (explicit config / defaultBudget only); the
5233
- // roster no longer supplies one. Idle is the default reaper.
5234
- timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs,
5235
- idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
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 resolve5 = (d) => {
5421
+ const resolve6 = (d) => {
5334
5422
  if (settled) return;
5335
5423
  settled = true;
5336
5424
  resolveDecision(d);
5337
5425
  };
5338
- const fallback = setTimeout(() => resolve5("stop"), DECISION_TIMEOUT_MS);
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(() => resolve5({ extend: extra }));
5444
+ queueMicrotask(() => resolve6({ extend: extra }));
5355
5445
  },
5356
5446
  deny: () => {
5357
5447
  clearTimeout(fallback);
5358
- resolve5("stop");
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 (fleet_session tool). */
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(path8.dirname(this.manifestPath), { recursive: true });
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 resolve5;
6672
+ let resolve6;
6574
6673
  const promise = new Promise((res) => {
6575
- resolve5 = res;
6674
+ resolve6 = res;
6576
6675
  });
6577
- this.taskWaiters.set(id, { promise, resolve: resolve5 });
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 = path8.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
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
- makeFleetStatusTool(this),
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((resolve5) => {
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
- resolve5(value);
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(path8.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
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(path8.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
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 ? path8.join(opts.dir, `${path8.basename(id)}.summary.json`) : "";
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
- function sanitizeModel(model) {
8064
- return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
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 time = startedAt.slice(11, 19).replace(/:/g, "-");
8069
- const suffix = randomBytes(2).toString("hex");
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 path8.join(this.dir, "_index.jsonl");
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 path8.join(this.dir, `${id}${ext}`);
8306
+ return sessionScopedPath(this.dir, id, ext);
8207
8307
  }
8208
8308
  shardManifestPath(shardKey) {
8209
- return shardKey ? path8.join(this.dir, shardKey, "_manifest.json") : path8.join(this.dir, "_manifest.json");
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 = path8.dirname(id);
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 = path8.dirname(path8.join(this.dir, id));
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, meta.model ?? meta.provider);
8330
+ const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt);
8231
8331
  const shardDir = await this.ensureShardDir(id);
8232
- const file = path8.join(shardDir, `${path8.basename(id)}.jsonl`);
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: path8.dirname(file),
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
- for (const line of lines) {
8341
- try {
8342
- const parsed = JSON.parse(line);
8343
- if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
8344
- const ev = parsed;
8345
- events.push(ev);
8346
- if (ev.type === "session_start" && !sessionStartEvent) {
8347
- sessionStartEvent = ev;
8348
- sessionModel = ev.model;
8349
- sessionProvider = ev.provider;
8350
- }
8351
- if (ev.type === "session_end") {
8352
- sessionEndEvent = ev;
8353
- sessionPendingToolUses = ev.pendingToolUses;
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
- usage = {
8364
- input: usage.input + (ev.usage.input ?? 0),
8365
- output: usage.output + (ev.usage.output ?? 0),
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
- openToolUses.delete(ev.id);
8378
- const resultBlock = {
8379
- type: "tool_result",
8380
- tool_use_id: ev.id,
8381
- content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
8382
- is_error: ev.isError
8383
- };
8384
- const last = messages[messages.length - 1];
8385
- const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
8386
- if (lastIsToolResultUser && Array.isArray(last.content)) {
8387
- last.content.push(resultBlock);
8388
- } else {
8389
- messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
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
- if (openToolUses.size > 0) {
8397
- this.events?.emit("session.damaged", {
8398
- sessionId: id,
8399
- detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
8400
- });
8401
- }
8402
- const repaired = repairToolUseAdjacency(messages);
8403
- if (repaired.report.changed) {
8404
- this.events?.emit("session.damaged", {
8405
- sessionId: id,
8406
- detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
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: repaired.messages, usage, toolCallEnds };
8419
- if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
8420
- const oldest = this._loadCache.keys().next().value;
8421
- if (oldest !== void 0) {
8422
- this._loadCache.delete(oldest);
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
- const line = JSON.stringify(summary) + "\n";
8587
- await fsp7.appendFile(this.indexFile, line, "utf8");
8588
- this._indexCache = null;
8589
- this.invalidateShardManifestBySessionId(summary.id);
8590
- this.indexAppendCount++;
8591
- if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
8592
- await this.compactIndex();
8593
- this.indexAppendCount = 0;
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
- const line = JSON.stringify({ action: "delete", id }) + "\n";
8603
- await fsp7.appendFile(this.indexFile, line, "utf8");
8604
- this._indexCache = null;
8605
- this.invalidateShardManifestBySessionId(id);
8606
- this.indexAppendCount++;
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. Atomic via temp+rename.
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
- const entries = await this.readIndex();
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 fsp7.writeFile(tmp, lines, "utf8");
8694
- await fsp7.rename(tmp, this.indexFile);
8695
- this._indexCache = null;
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 ? path8.join(this.dir, shardKey) : this.dir;
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: path8.join(dir, entry.name) });
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(path8.join(dir, entry.name), childPrefix, depth + 1);
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(path8.join(dir, entry.name), childPrefix, depth + 1);
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 = path8.dirname(path8.join(this.dir, id));
8951
- const base = path8.basename(id);
8952
- const sessDir = path8.join(shardDir, base);
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(path8.join(shardDir, `${base}.plan.json`)),
8957
- fsp7.unlink(path8.join(shardDir, `${base}.todos.json`))
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(path8.join(this.dir, "active.json"), "utf8");
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 = path8.join(dir, name);
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 = path8.join(this.dir, entry.name);
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 = path8.join(this.dir, entry.name);
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 ? path8.join(opts.sessionsRoot, runId) : "(caller-managed)";
9323
+ dir = opts.sessionsRoot ? path9.join(opts.sessionsRoot, runId) : "(caller-managed)";
9164
9324
  } else if (opts.sessionsRoot) {
9165
- dir = path8.join(opts.sessionsRoot, runId);
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(path8.dirname(this.manifestPath), { recursive: true });
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 = path8.join(sessionDir, MAILBOX_FILE);
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(path8.dirname(this.filePath), { recursive: true });
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 path8.join(globalRoot, "projects", projectSlug(projectRoot));
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 = path8.join(projectDir, MAILBOX_FILE2);
10228
- this.registryPath = path8.join(projectDir, "_mailbox.registry.json");
10229
- this.clientRegistryPath = path8.join(projectDir, CLIENT_REGISTRY_FILE);
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 `${path8.basename(path8.dirname(this.messagePath))}:mailbox`;
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(path8.dirname(this.messagePath), { recursive: true });
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(path8.dirname(this.registryPath), { recursive: true });
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(path8.dirname(this.clientRegistryPath), { recursive: true });
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 = path8.join(opts.projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
10859
- const tokenPath = path8.join(opts.projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
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 = path8.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
10888
- const tokenPath = path8.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
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 = path8.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
10900
- const tokenPath = path8.join(projectDir, MAILBOX_BRIDGE_TOKEN_FILENAME);
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 = path8.join(projectDir, MAILBOX_BRIDGE_LOCK_FILENAME);
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 = path8.dirname(targetPath);
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: "Inter-agent mailbox with cross-session support. Send messages, check for incoming messages, acknowledge with read receipts, query by criteria, see online agents.",
11232
- usageHint: shortHint,
11233
- category: "coordination",
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 ("can you review src/auth.ts?"), ask questions, or announce what you just did. 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.',
11496
- usageHint: 'mail_send to="*" subject="auth refactor done" body="touched src/auth/*, please review"',
11497
- category: "coordination",
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 and results, or after a long stretch of tool work.',
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: "coordination",
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 path8.join(storageDir, LOG_FILENAME);
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 = path8.basename(manifestPath).toLowerCase();
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 = path8.join(sessionDir, "_knowledge_graph");
12116
- this.graphFilePath = path8.join(this.filePath, "graph.jsonl");
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
- return Array.from(this.nodes.values()).filter((n) => this._matches(n, filter ?? {}));
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
- if (req.question.toLowerCase().includes("spawn")) return "spawn";
13357
- if (req.question.toLowerCase().includes("approve") || req.question.toLowerCase().includes("change")) return "approve_change";
13358
- if (req.question.toLowerCase().includes("retry") || req.question.toLowerCase().includes("fail")) return "retry_task";
13359
- if (req.question.toLowerCase().includes("priorit")) return "prioritize_goals";
13360
- if (req.question.toLowerCase().includes("decompos")) return "decompose_goal";
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((resolve5) => {
14413
+ return new Promise((resolve6) => {
14218
14414
  let off;
14219
14415
  const timer = setTimeout(() => {
14220
14416
  off?.();
14221
- resolve5();
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
- resolve5();
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 = path8.join(this._transcriptsDir, subagentId);
14945
+ const dir = path9.join(this._transcriptsDir, subagentId);
14745
14946
  await fsp7.mkdir(dir, { recursive: true });
14746
- const filePath = path8.join(dir, "transcript.jsonl");
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