@wrongstack/core 0.275.1 → 0.276.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -2,11 +2,11 @@ import * as crypto2 from 'crypto';
2
2
  import { randomBytes, randomUUID, createCipheriv, createDecipheriv, createHash, scryptSync } from 'crypto';
3
3
  import * as fsp7 from 'fs/promises';
4
4
  import { readFile, writeFile, mkdir } from 'fs/promises';
5
- import * as path3 from 'path';
5
+ import * as path4 from 'path';
6
6
  import { isAbsolute, join, resolve, sep } from 'path';
7
7
  import * as os from 'os';
8
8
  import { hostname } from 'os';
9
- import * as fs11 from 'fs';
9
+ import * as fs12 from 'fs';
10
10
  import { readFileSync, statSync, createReadStream, existsSync } from 'fs';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { EventEmitter } from 'events';
@@ -29,6 +29,19 @@ var __export = (target, all) => {
29
29
  __defProp(target, name, { get: all[name], enumerable: true });
30
30
  };
31
31
 
32
+ // src/utils/assert-never.ts
33
+ function assertNever(x, message) {
34
+ const err = new Error(
35
+ `Unhandled case: ${JSON.stringify(x)}`
36
+ );
37
+ err.name = "AssertNeverError";
38
+ throw err;
39
+ }
40
+ var init_assert_never = __esm({
41
+ "src/utils/assert-never.ts"() {
42
+ }
43
+ });
44
+
32
45
  // src/utils/atomic-write.ts
33
46
  var atomic_write_exports = {};
34
47
  __export(atomic_write_exports, {
@@ -37,9 +50,9 @@ __export(atomic_write_exports, {
37
50
  withFileLock: () => withFileLock
38
51
  });
39
52
  async function atomicWrite(targetPath, content, opts = {}) {
40
- const dir = path3.dirname(targetPath);
53
+ const dir = path4.dirname(targetPath);
41
54
  await fsp7.mkdir(dir, { recursive: true });
42
- const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
55
+ const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
43
56
  try {
44
57
  if (typeof content === "string") {
45
58
  await fsp7.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -57,8 +70,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
57
70
  }
58
71
  let mode;
59
72
  try {
60
- const stat9 = await fsp7.stat(targetPath);
61
- mode = stat9.mode & 511;
73
+ const stat10 = await fsp7.stat(targetPath);
74
+ mode = stat10.mode & 511;
62
75
  } catch {
63
76
  mode = opts.mode;
64
77
  }
@@ -84,9 +97,9 @@ async function ensureDir(dir) {
84
97
  await fsp7.mkdir(dir, { recursive: true });
85
98
  }
86
99
  async function withFileLock(targetPath, fn, opts = {}) {
87
- const dir = path3.dirname(targetPath);
100
+ const dir = path4.dirname(targetPath);
88
101
  await fsp7.mkdir(dir, { recursive: true });
89
- const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
102
+ const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
90
103
  const timeoutMs = opts.timeoutMs ?? 5e3;
91
104
  const staleMs = opts.staleMs ?? 3e4;
92
105
  const started = Date.now();
@@ -104,8 +117,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
104
117
  }
105
118
  if (code !== "EEXIST") throw err;
106
119
  try {
107
- const stat9 = await fsp7.stat(lockPath);
108
- if (Date.now() - stat9.mtimeMs > staleMs) {
120
+ const stat10 = await fsp7.stat(lockPath);
121
+ if (Date.now() - stat10.mtimeMs > staleMs) {
109
122
  await fsp7.unlink(lockPath);
110
123
  continue;
111
124
  }
@@ -113,9 +126,14 @@ async function withFileLock(targetPath, fn, opts = {}) {
113
126
  continue;
114
127
  }
115
128
  if (Date.now() - started >= timeoutMs) {
116
- throw new Error(`Timed out waiting for file lock: ${targetPath}`);
129
+ throw new FsError({
130
+ message: `Timed out waiting for file lock: ${targetPath}`,
131
+ code: "FS_ATOMIC_WRITE_FAILED",
132
+ path: targetPath,
133
+ context: { timeoutMs }
134
+ });
117
135
  }
118
- await new Promise((resolve11) => setTimeout(resolve11, 25));
136
+ await new Promise((resolve12) => setTimeout(resolve12, 25));
119
137
  }
120
138
  }
121
139
  try {
@@ -148,7 +166,7 @@ async function renameWithRetry(from, to) {
148
166
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
149
167
  throw err;
150
168
  }
151
- await new Promise((resolve11) => setTimeout(resolve11, delays[i]));
169
+ await new Promise((resolve12) => setTimeout(resolve12, delays[i]));
152
170
  }
153
171
  }
154
172
  throw lastErr;
@@ -156,64 +174,12 @@ async function renameWithRetry(from, to) {
156
174
  var TRANSIENT_RENAME_CODES;
157
175
  var init_atomic_write = __esm({
158
176
  "src/utils/atomic-write.ts"() {
177
+ init_errors();
159
178
  TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
160
179
  }
161
180
  });
162
181
 
163
- // src/utils/assert-never.ts
164
- function assertNever(x, message) {
165
- const err = new Error(
166
- `Unhandled case: ${JSON.stringify(x)}`
167
- );
168
- err.name = "AssertNeverError";
169
- throw err;
170
- }
171
-
172
- // src/utils/index.ts
173
- init_atomic_write();
174
-
175
182
  // src/utils/child-env.ts
176
- var ALLOWED_KEYS = /* @__PURE__ */ new Set([
177
- "PATH",
178
- "HOME",
179
- "USER",
180
- "USERNAME",
181
- "LOGNAME",
182
- "SHELL",
183
- "LANG",
184
- "LC_ALL",
185
- "LC_CTYPE",
186
- "TERM",
187
- "TZ",
188
- "TMPDIR",
189
- "TEMP",
190
- "TMP",
191
- "PWD",
192
- "OLDPWD",
193
- "COMSPEC",
194
- "SYSTEMROOT",
195
- "SYSTEMDRIVE",
196
- "WINDIR",
197
- "PROGRAMFILES",
198
- "PROGRAMFILES(X86)",
199
- "PROGRAMDATA",
200
- "APPDATA",
201
- "LOCALAPPDATA",
202
- "USERPROFILE",
203
- "PUBLIC",
204
- "PATHEXT"
205
- ]);
206
- var SECRET_NAME_PARTS = [
207
- "TOKEN",
208
- "SECRET",
209
- "PASSWORD",
210
- "PASSWD",
211
- "AUTH",
212
- "CRED",
213
- "BEARER",
214
- "COOKIE",
215
- "PRIVATE"
216
- ];
217
183
  function looksSecret(name) {
218
184
  const upper = name.toUpperCase();
219
185
  for (const p of SECRET_NAME_PARTS) {
@@ -230,8 +196,6 @@ function looksSecret(name) {
230
196
  function valueHasEmbeddedCredential(value) {
231
197
  return /\b[a-z][a-z0-9+.-]*:\/\/[^/\s:@]*:[^/\s@]+@/i.test(value);
232
198
  }
233
- var NODE_OPTIONS_INJECTION_FLAG = /^(?:--require|-r|--import|--loader|--experimental-loader)$/;
234
- var NODE_OPTIONS_INJECTION_FLAG_EQ = /^(?:--require|-r|--import|--loader|--experimental-loader)=/;
235
199
  function sanitizeNodeOptions(value) {
236
200
  const tokens = value.split(/\s+/).filter(Boolean);
237
201
  const kept = [];
@@ -291,55 +255,123 @@ function buildChildEnv(optsOrSessionId) {
291
255
  if (opts.sessionId) out["WRONGSTACK_SESSION_ID"] = opts.sessionId;
292
256
  return out;
293
257
  }
258
+ var ALLOWED_KEYS, SECRET_NAME_PARTS, NODE_OPTIONS_INJECTION_FLAG, NODE_OPTIONS_INJECTION_FLAG_EQ;
259
+ var init_child_env = __esm({
260
+ "src/utils/child-env.ts"() {
261
+ ALLOWED_KEYS = /* @__PURE__ */ new Set([
262
+ "PATH",
263
+ "HOME",
264
+ "USER",
265
+ "USERNAME",
266
+ "LOGNAME",
267
+ "SHELL",
268
+ "LANG",
269
+ "LC_ALL",
270
+ "LC_CTYPE",
271
+ "TERM",
272
+ "TZ",
273
+ "TMPDIR",
274
+ "TEMP",
275
+ "TMP",
276
+ "PWD",
277
+ "OLDPWD",
278
+ "COMSPEC",
279
+ "SYSTEMROOT",
280
+ "SYSTEMDRIVE",
281
+ "WINDIR",
282
+ "PROGRAMFILES",
283
+ "PROGRAMFILES(X86)",
284
+ "PROGRAMDATA",
285
+ "APPDATA",
286
+ "LOCALAPPDATA",
287
+ "USERPROFILE",
288
+ "PUBLIC",
289
+ "PATHEXT"
290
+ ]);
291
+ SECRET_NAME_PARTS = [
292
+ "TOKEN",
293
+ "SECRET",
294
+ "PASSWORD",
295
+ "PASSWD",
296
+ "AUTH",
297
+ "CRED",
298
+ "BEARER",
299
+ "COOKIE",
300
+ "PRIVATE"
301
+ ];
302
+ NODE_OPTIONS_INJECTION_FLAG = /^(?:--require|-r|--import|--loader|--experimental-loader)$/;
303
+ NODE_OPTIONS_INJECTION_FLAG_EQ = /^(?:--require|-r|--import|--loader|--experimental-loader)=/;
304
+ }
305
+ });
294
306
 
295
307
  // src/utils/term.ts
296
- var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
297
308
  function isStdoutTTY() {
298
309
  return hasStdout() && Boolean(process.stdout.isTTY);
299
310
  }
300
311
  function writeTo(s, stream) {
301
312
  if (!stream || typeof stream.write !== "function") return false;
302
- {
313
+ const guard = activeOutputGuard;
314
+ if (!guard) {
303
315
  stream.write(s);
304
316
  return true;
305
317
  }
318
+ guard.suspend();
319
+ stream.write(s);
320
+ guard.resume();
321
+ return true;
306
322
  }
307
323
  function writeErr(s, stream = process.stderr) {
308
324
  return writeTo(s, stream);
309
325
  }
326
+ var hasStdout, activeOutputGuard;
327
+ var init_term = __esm({
328
+ "src/utils/term.ts"() {
329
+ hasStdout = () => typeof process !== "undefined" && !!process.stdout;
330
+ activeOutputGuard = null;
331
+ }
332
+ });
310
333
 
311
334
  // src/utils/color.ts
312
- var isColorTty = () => {
313
- if (envFlag(process.env.NO_COLOR)) return false;
314
- if (envFlag(process.env.FORCE_COLOR)) return true;
315
- return isStdoutTTY();
316
- };
317
335
  function envFlag(value) {
318
336
  if (value === void 0) return false;
319
337
  if (value.trim() === "") return false;
320
338
  return !/^(0|false|no|off)$/i.test(value.trim());
321
339
  }
322
- var COLOR = isColorTty();
323
- var wrap = (open4, close) => (s) => COLOR ? `\x1B[${open4}m${s}\x1B[${close}m` : s;
324
- var color = {
325
- reset: wrap("0", "0"),
326
- bold: wrap("1", "22"),
327
- dim: wrap("2", "22"),
328
- italic: wrap("3", "23"),
329
- underline: wrap("4", "24"),
330
- red: wrap("31", "39"),
331
- green: wrap("32", "39"),
332
- yellow: wrap("33", "39"),
333
- blue: wrap("34", "39"),
334
- magenta: wrap("35", "39"),
335
- cyan: wrap("36", "39"),
336
- gray: wrap("90", "39"),
337
- amber: wrap("38;5;214", "39"),
338
- pink: wrap("38;5;205", "39"),
339
- bgRed: wrap("41", "49"),
340
- bgGreen: wrap("42", "49")
341
- };
342
- var MAX_DIGEST_CHARS = 4e3;
340
+ var isColorTty, COLOR, wrap, color;
341
+ var init_color = __esm({
342
+ "src/utils/color.ts"() {
343
+ init_term();
344
+ isColorTty = () => {
345
+ if (envFlag(process.env.NO_COLOR)) return false;
346
+ if (envFlag(process.env.FORCE_COLOR)) return true;
347
+ return isStdoutTTY();
348
+ };
349
+ COLOR = isColorTty();
350
+ wrap = (open4, close) => (s) => COLOR ? `\x1B[${open4}m${s}\x1B[${close}m` : s;
351
+ color = {
352
+ reset: wrap("0", "0"),
353
+ bold: wrap("1", "22"),
354
+ dim: wrap("2", "22"),
355
+ italic: wrap("3", "23"),
356
+ underline: wrap("4", "24"),
357
+ red: wrap("31", "39"),
358
+ green: wrap("32", "39"),
359
+ yellow: wrap("33", "39"),
360
+ blue: wrap("34", "39"),
361
+ magenta: wrap("35", "39"),
362
+ cyan: wrap("36", "39"),
363
+ gray: wrap("90", "39"),
364
+ amber: wrap("38;5;214", "39"),
365
+ pink: wrap("38;5;205", "39"),
366
+ bgRed: wrap("41", "49"),
367
+ bgGreen: wrap("42", "49")
368
+ };
369
+ }
370
+ });
371
+ var init_config_json = __esm({
372
+ "src/utils/config-json.ts"() {
373
+ }
374
+ });
343
375
  function createContextEvidenceState() {
344
376
  return {
345
377
  sessionGoals: [],
@@ -412,17 +444,14 @@ function ensureEvidence(ctx) {
412
444
  }
413
445
  return ctx.contextEvidence;
414
446
  }
447
+ var MAX_DIGEST_CHARS;
448
+ var init_context_evidence = __esm({
449
+ "src/utils/context-evidence.ts"() {
450
+ MAX_DIGEST_CHARS = 4e3;
451
+ }
452
+ });
415
453
 
416
454
  // src/utils/deep-merge.ts
417
- var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
418
- "__proto__",
419
- "constructor",
420
- "prototype",
421
- "__defineGetter__",
422
- "__defineSetter__",
423
- "__lookupGetter__",
424
- "__lookupSetter__"
425
- ]);
426
455
  function isPrimitiveArray(a) {
427
456
  return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
428
457
  }
@@ -471,11 +500,35 @@ function deepMerge(base, patch, options = {}) {
471
500
  }
472
501
  return out;
473
502
  }
503
+ var FORBIDDEN_PROTO_KEYS;
504
+ var init_deep_merge = __esm({
505
+ "src/utils/deep-merge.ts"() {
506
+ FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
507
+ "__proto__",
508
+ "constructor",
509
+ "prototype",
510
+ "__defineGetter__",
511
+ "__defineSetter__",
512
+ "__lookupGetter__",
513
+ "__lookupSetter__"
514
+ ]);
515
+ }
516
+ });
517
+
518
+ // src/utils/diff.ts
519
+ var init_diff = __esm({
520
+ "src/utils/diff.ts"() {
521
+ }
522
+ });
474
523
 
475
524
  // src/utils/error.ts
476
525
  function toErrorMessage(err) {
477
526
  return err instanceof Error ? err.message : String(err);
478
527
  }
528
+ var init_error = __esm({
529
+ "src/utils/error.ts"() {
530
+ }
531
+ });
479
532
 
480
533
  // src/utils/expect-defined.ts
481
534
  function expectDefined(value, label) {
@@ -486,9 +539,10 @@ function expectDefined(value, label) {
486
539
  }
487
540
  return value;
488
541
  }
489
- var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
490
- var IS_WINDOWS = process.platform === "win32";
491
- var SEP = IS_WINDOWS ? "\\" : "/";
542
+ var init_expect_defined = __esm({
543
+ "src/utils/expect-defined.ts"() {
544
+ }
545
+ });
492
546
  function isGlob(p) {
493
547
  for (const c of p) {
494
548
  if (GLOB_CHARS.has(c)) return true;
@@ -573,8 +627,8 @@ async function expandGlob(pattern) {
573
627
  for (const e of entries) {
574
628
  const full = `${dir}${SEP}${e}`;
575
629
  try {
576
- const stat9 = await fsp7.stat(full);
577
- if (stat9.isDirectory()) await walk3(full, rest);
630
+ const stat10 = await fsp7.stat(full);
631
+ if (stat10.isDirectory()) await walk3(full, rest);
578
632
  } catch {
579
633
  }
580
634
  }
@@ -591,8 +645,8 @@ async function expandGlob(pattern) {
591
645
  if (entries.includes(seg)) {
592
646
  const full = `${dir}${SEP}${seg}`;
593
647
  try {
594
- const stat9 = await fsp7.stat(full);
595
- if (stat9.isDirectory()) await walk3(full, rest);
648
+ const stat10 = await fsp7.stat(full);
649
+ if (stat10.isDirectory()) await walk3(full, rest);
596
650
  } catch {
597
651
  }
598
652
  }
@@ -601,14 +655,20 @@ async function expandGlob(pattern) {
601
655
  await walk3(base === "." ? "." : base, relPat);
602
656
  return [...results];
603
657
  }
658
+ var GLOB_CHARS, IS_WINDOWS, SEP;
659
+ var init_glob_expand = __esm({
660
+ "src/utils/glob-expand.ts"() {
661
+ init_expect_defined();
662
+ GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
663
+ IS_WINDOWS = process.platform === "win32";
664
+ SEP = IS_WINDOWS ? "\\" : "/";
665
+ }
666
+ });
604
667
 
605
668
  // src/utils/glob-match.ts
606
669
  function escapeRegex(s) {
607
670
  return s.replace(/[.+^${}()|\\]/g, "\\$&");
608
671
  }
609
- var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
610
- var CACHE_MAX_SIZE = 2e3;
611
- var NEVER_MATCH = /[^\s\S]/;
612
672
  function getCachedGlob(pattern) {
613
673
  const cached = COMPILED_GLOB_CACHE.get(pattern);
614
674
  if (cached) return cached;
@@ -627,7 +687,6 @@ function getCachedGlob(pattern) {
627
687
  COMPILED_GLOB_CACHE.set(pattern, re);
628
688
  return re;
629
689
  }
630
- var MAX_GLOB_PATTERN_LEN = 1024;
631
690
  function compileGlob(pattern) {
632
691
  if (pattern.length > MAX_GLOB_PATTERN_LEN) {
633
692
  throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
@@ -683,6 +742,16 @@ function matchGlob(pattern, input) {
683
742
  function matchAny(patterns, input) {
684
743
  return patterns.some((p) => matchGlob(p, input));
685
744
  }
745
+ var COMPILED_GLOB_CACHE, CACHE_MAX_SIZE, NEVER_MATCH, MAX_GLOB_PATTERN_LEN;
746
+ var init_glob_match = __esm({
747
+ "src/utils/glob-match.ts"() {
748
+ init_expect_defined();
749
+ COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
750
+ CACHE_MAX_SIZE = 2e3;
751
+ NEVER_MATCH = /[^\s\S]/;
752
+ MAX_GLOB_PATTERN_LEN = 1024;
753
+ }
754
+ });
686
755
 
687
756
  // src/utils/json-repair.ts
688
757
  function completePartialObject(s) {
@@ -759,7 +828,6 @@ function repairTruncated(s) {
759
828
  }
760
829
  return result;
761
830
  }
762
- var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
763
831
  function endsWithInvalidEscape(str) {
764
832
  const last = str[str.length - 1];
765
833
  if (str[str.length - 2] !== "\\" || last === void 0) return false;
@@ -775,6 +843,13 @@ function tryParse(s) {
775
843
  return { ok: false };
776
844
  }
777
845
  }
846
+ var VALID_ESCAPE;
847
+ var init_json_repair = __esm({
848
+ "src/utils/json-repair.ts"() {
849
+ init_expect_defined();
850
+ VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
851
+ }
852
+ });
778
853
 
779
854
  // src/utils/json-schema-validate.ts
780
855
  function validateAgainstSchema(value, schema) {
@@ -782,7 +857,6 @@ function validateAgainstSchema(value, schema) {
782
857
  walk(value, schema, "", errors, 0);
783
858
  return { ok: errors.length === 0, errors };
784
859
  }
785
- var MAX_SCHEMA_DEPTH = 64;
786
860
  function walk(value, schema, path32, errors, depth) {
787
861
  if (depth > MAX_SCHEMA_DEPTH) {
788
862
  errors.push({
@@ -879,6 +953,12 @@ function deepEqual(a, b) {
879
953
  }
880
954
  return false;
881
955
  }
956
+ var MAX_SCHEMA_DEPTH;
957
+ var init_json_schema_validate = __esm({
958
+ "src/utils/json-schema-validate.ts"() {
959
+ MAX_SCHEMA_DEPTH = 64;
960
+ }
961
+ });
882
962
 
883
963
  // src/utils/merge-models-payload.ts
884
964
  function mergeModelsPayload(base, overlay) {
@@ -942,6 +1022,10 @@ function stripUndefined(obj) {
942
1022
  }
943
1023
  return out;
944
1024
  }
1025
+ var init_merge_models_payload = __esm({
1026
+ "src/utils/merge-models-payload.ts"() {
1027
+ }
1028
+ });
945
1029
 
946
1030
  // src/utils/message-invariants.ts
947
1031
  function repairToolUseAdjacency(messages) {
@@ -1034,15 +1118,19 @@ function isEmptyMessage(msg) {
1034
1118
  if (typeof msg.content === "string") return msg.content.trim().length === 0;
1035
1119
  return msg.content.length === 0;
1036
1120
  }
1121
+ var init_message_invariants = __esm({
1122
+ "src/utils/message-invariants.ts"() {
1123
+ init_expect_defined();
1124
+ }
1125
+ });
1126
+
1127
+ // src/utils/newline-normalize.ts
1128
+ var init_newline_normalize = __esm({
1129
+ "src/utils/newline-normalize.ts"() {
1130
+ }
1131
+ });
1037
1132
 
1038
1133
  // src/utils/regex-guard.ts
1039
- var MAX_PATTERN_LEN = 512;
1040
- var DANGEROUS_PATTERNS = [
1041
- /(\([^)]*[+*][^)]*\))[+*]/,
1042
- // (a+)+, (.*)+, etc
1043
- /(\(\?:[^)]*[+*][^)]*\))[+*]/
1044
- // same, with non-capturing group
1045
- ];
1046
1134
  function compileUserRegex(pattern, flags) {
1047
1135
  if (typeof pattern !== "string") {
1048
1136
  return { ok: false, reason: "pattern must be a string" };
@@ -1070,6 +1158,18 @@ function compileUserRegex(pattern, flags) {
1070
1158
  };
1071
1159
  }
1072
1160
  }
1161
+ var MAX_PATTERN_LEN, DANGEROUS_PATTERNS;
1162
+ var init_regex_guard = __esm({
1163
+ "src/utils/regex-guard.ts"() {
1164
+ MAX_PATTERN_LEN = 512;
1165
+ DANGEROUS_PATTERNS = [
1166
+ /(\([^)]*[+*][^)]*\))[+*]/,
1167
+ // (a+)+, (.*)+, etc
1168
+ /(\(\?:[^)]*[+*][^)]*\))[+*]/
1169
+ // same, with non-capturing group
1170
+ ];
1171
+ }
1172
+ });
1073
1173
 
1074
1174
  // src/utils/safe-json.ts
1075
1175
  function safeParse(input, maxBytes = 5e6) {
@@ -1085,15 +1185,44 @@ function safeParse(input, maxBytes = 5e6) {
1085
1185
  };
1086
1186
  }
1087
1187
  }
1188
+ var init_safe_json = __esm({
1189
+ "src/utils/safe-json.ts"() {
1190
+ init_error();
1191
+ }
1192
+ });
1193
+ function sessionScopedPath(dir, sessionId, suffix) {
1194
+ if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
1195
+ throw invalid(sessionId);
1196
+ }
1197
+ const resolved = path4.resolve(dir, `${sessionId}${suffix}`);
1198
+ const rel = path4.relative(path4.resolve(dir), resolved);
1199
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) {
1200
+ throw invalid(sessionId);
1201
+ }
1202
+ return resolved;
1203
+ }
1204
+ function invalid(sessionId) {
1205
+ return new FsError({
1206
+ message: `Invalid sessionId: ${sessionId}`,
1207
+ code: ERROR_CODES.FS_DELETE_FAILED,
1208
+ path: sessionId,
1209
+ context: { reason: "path_traversal" }
1210
+ });
1211
+ }
1212
+ var init_session_scoped_path = __esm({
1213
+ "src/utils/session-scoped-path.ts"() {
1214
+ init_errors();
1215
+ }
1216
+ });
1088
1217
 
1089
1218
  // src/utils/slug.ts
1090
1219
  function slugify(name, fallback = "prompt", maxLen = 64) {
1091
1220
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLen).replace(/-+$/g, "") || fallback;
1092
1221
  }
1093
- var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1094
- var ENCODING_LEN = ENCODING.length;
1095
- var TIME_LEN = 10;
1096
- var RANDOM_LEN = 16;
1222
+ var init_slug = __esm({
1223
+ "src/utils/slug.ts"() {
1224
+ }
1225
+ });
1097
1226
  function encodeTime(now, len) {
1098
1227
  let mod;
1099
1228
  let str = "";
@@ -1115,19 +1244,47 @@ function encodeRandom(len) {
1115
1244
  function ulid(seedTime = Date.now()) {
1116
1245
  return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);
1117
1246
  }
1247
+ var ENCODING, ENCODING_LEN, TIME_LEN, RANDOM_LEN;
1248
+ var init_ulid = __esm({
1249
+ "src/utils/ulid.ts"() {
1250
+ ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1251
+ ENCODING_LEN = ENCODING.length;
1252
+ TIME_LEN = 10;
1253
+ RANDOM_LEN = 16;
1254
+ }
1255
+ });
1118
1256
 
1119
1257
  // src/utils/sleep.ts
1120
1258
  function sleep(ms) {
1121
- return new Promise((resolve11) => setTimeout(resolve11, ms));
1259
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
1122
1260
  }
1261
+ var init_sleep = __esm({
1262
+ "src/utils/sleep.ts"() {
1263
+ }
1264
+ });
1123
1265
 
1124
1266
  // src/utils/string.ts
1125
1267
  function truncate(s, max) {
1126
1268
  return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
1127
1269
  }
1270
+ var init_string = __esm({
1271
+ "src/utils/string.ts"() {
1272
+ }
1273
+ });
1274
+
1275
+ // src/utils/task-format.ts
1276
+ var init_task_format = __esm({
1277
+ "src/utils/task-format.ts"() {
1278
+ }
1279
+ });
1280
+
1281
+ // src/utils/todos-format.ts
1282
+ var init_todos_format = __esm({
1283
+ "src/utils/todos-format.ts"() {
1284
+ }
1285
+ });
1128
1286
 
1129
1287
  // src/utils/tool-subject.ts
1130
- var GLOB_METACHARACTERS = /[*?[\]]/g;
1131
1288
  function escapeGlobSubject(value) {
1132
1289
  return value.replace(GLOB_METACHARACTERS, (char) => `\\${char}`);
1133
1290
  }
@@ -1160,11 +1317,14 @@ function subjectForToolInput(toolName, input, subjectKey) {
1160
1317
  }
1161
1318
  return void 0;
1162
1319
  }
1320
+ var GLOB_METACHARACTERS;
1321
+ var init_tool_subject = __esm({
1322
+ "src/utils/tool-subject.ts"() {
1323
+ GLOB_METACHARACTERS = /[*?[\]]/g;
1324
+ }
1325
+ });
1163
1326
 
1164
1327
  // src/utils/tool-wire-compact.ts
1165
- var TOOL_DESCRIPTION_MAX_CHARS = 640;
1166
- var SCHEMA_DESCRIPTION_MAX_CHARS = 180;
1167
- var compactCache = /* @__PURE__ */ new WeakMap();
1168
1328
  function compactToolDefinitionForWire(tool, opts = {}) {
1169
1329
  const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
1170
1330
  if (useDefaultOptions && typeof tool === "object" && tool !== null) {
@@ -1230,11 +1390,16 @@ function findSemanticBoundary(text, limit) {
1230
1390
  function isRecord(value) {
1231
1391
  return !!value && typeof value === "object" && !Array.isArray(value);
1232
1392
  }
1393
+ var TOOL_DESCRIPTION_MAX_CHARS, SCHEMA_DESCRIPTION_MAX_CHARS, compactCache;
1394
+ var init_tool_wire_compact = __esm({
1395
+ "src/utils/tool-wire-compact.ts"() {
1396
+ TOOL_DESCRIPTION_MAX_CHARS = 640;
1397
+ SCHEMA_DESCRIPTION_MAX_CHARS = 180;
1398
+ compactCache = /* @__PURE__ */ new WeakMap();
1399
+ }
1400
+ });
1233
1401
 
1234
1402
  // src/utils/token-estimate.ts
1235
- var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1236
- var CALIBRATION_GLOBAL_KEY = "__global__";
1237
- var _cals = /* @__PURE__ */ new Map();
1238
1403
  function calState(key) {
1239
1404
  let state = _cals.get(key);
1240
1405
  if (!state) {
@@ -1243,21 +1408,6 @@ function calState(key) {
1243
1408
  }
1244
1409
  return state;
1245
1410
  }
1246
- var MIN_SAMPLES_FOR_CALIBRATION = 3;
1247
- var MODEL_FAMILY_RATIO = {
1248
- // Anthropic: ~3.8-4.0 chars/token depending on model
1249
- claude: 3.8,
1250
- // OpenAI: ~4.0 chars/token
1251
- "gpt-4": 4,
1252
- "gpt-3.5": 4,
1253
- // Google: ~3.5 chars/token
1254
- gemini: 3.5,
1255
- // DeepSeek: ~3.5 chars/token
1256
- deepseek: 3.5
1257
- };
1258
- var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1259
- var _estimateCacheOrder = [];
1260
- var ESTIMATE_CACHE_MAX_SIZE = 5e4;
1261
1411
  function getCachedEstimate(key, compute) {
1262
1412
  const existing = ESTIMATE_CACHE.get(key);
1263
1413
  if (existing !== void 0) return existing;
@@ -1404,17 +1554,32 @@ function getModelFamilyRatio(calibrationKey) {
1404
1554
  }
1405
1555
  return null;
1406
1556
  }
1557
+ var RoughTokenEstimate, CALIBRATION_GLOBAL_KEY, _cals, MIN_SAMPLES_FOR_CALIBRATION, MODEL_FAMILY_RATIO, ESTIMATE_CACHE, _estimateCacheOrder, ESTIMATE_CACHE_MAX_SIZE;
1558
+ var init_token_estimate = __esm({
1559
+ "src/utils/token-estimate.ts"() {
1560
+ init_tool_wire_compact();
1561
+ RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1562
+ CALIBRATION_GLOBAL_KEY = "__global__";
1563
+ _cals = /* @__PURE__ */ new Map();
1564
+ MIN_SAMPLES_FOR_CALIBRATION = 3;
1565
+ MODEL_FAMILY_RATIO = {
1566
+ // Anthropic: ~3.8-4.0 chars/token depending on model
1567
+ claude: 3.8,
1568
+ // OpenAI: ~4.0 chars/token
1569
+ "gpt-4": 4,
1570
+ "gpt-3.5": 4,
1571
+ // Google: ~3.5 chars/token
1572
+ gemini: 3.5,
1573
+ // DeepSeek: ~3.5 chars/token
1574
+ deepseek: 3.5
1575
+ };
1576
+ ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1577
+ _estimateCacheOrder = [];
1578
+ ESTIMATE_CACHE_MAX_SIZE = 5e4;
1579
+ }
1580
+ });
1407
1581
 
1408
1582
  // src/utils/tool-output-serializer.ts
1409
- var DEFAULT_LIST_LIMIT = 500;
1410
- var LOG_ENTRY_LIMIT = 200;
1411
- var INLINE_LIMIT = 240;
1412
- var GREP_FILE_LIMIT = 80;
1413
- var GREP_MATCHES_PER_FILE = 3;
1414
- var DIFF_INLINE_LINE_LIMIT = 260;
1415
- var DIFF_HUNK_LIMIT = 8;
1416
- var DIFF_HUNK_CONTEXT = 14;
1417
- var GREP_LINE_RE = /^(.+?):(\d+):(.*)$/;
1418
1583
  function createToolOutputSerializer(opts = {}) {
1419
1584
  const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
1420
1585
  function serialize(value, context = {}) {
@@ -2062,9 +2227,22 @@ function isRecord2(value) {
2062
2227
  function isScalar(value) {
2063
2228
  return value === null || ["string", "number", "boolean"].includes(typeof value);
2064
2229
  }
2230
+ var DEFAULT_LIST_LIMIT, LOG_ENTRY_LIMIT, INLINE_LIMIT, GREP_FILE_LIMIT, GREP_MATCHES_PER_FILE, DIFF_INLINE_LINE_LIMIT, DIFF_HUNK_LIMIT, DIFF_HUNK_CONTEXT, GREP_LINE_RE;
2231
+ var init_tool_output_serializer = __esm({
2232
+ "src/utils/tool-output-serializer.ts"() {
2233
+ DEFAULT_LIST_LIMIT = 500;
2234
+ LOG_ENTRY_LIMIT = 200;
2235
+ INLINE_LIMIT = 240;
2236
+ GREP_FILE_LIMIT = 80;
2237
+ GREP_MATCHES_PER_FILE = 3;
2238
+ DIFF_INLINE_LINE_LIMIT = 260;
2239
+ DIFF_HUNK_LIMIT = 8;
2240
+ DIFF_HUNK_CONTEXT = 14;
2241
+ GREP_LINE_RE = /^(.+?):(\d+):(.*)$/;
2242
+ }
2243
+ });
2065
2244
 
2066
2245
  // src/utils/tool-result-render-mode.ts
2067
- var DEFAULT_TOOL_RESULT_RENDER_MODE = "extend";
2068
2246
  function normalizeToolResultRenderMode(value) {
2069
2247
  if (typeof value !== "string") return void 0;
2070
2248
  const raw = value.trim().toLowerCase();
@@ -2075,12 +2253,18 @@ function normalizeToolResultRenderMode(value) {
2075
2253
  function resolveToolResultRenderMode(modes, toolName) {
2076
2254
  return normalizeToolResultRenderMode(modes?.[toolName]) ?? DEFAULT_TOOL_RESULT_RENDER_MODE;
2077
2255
  }
2256
+ var DEFAULT_TOOL_RESULT_RENDER_MODE;
2257
+ var init_tool_result_render_mode = __esm({
2258
+ "src/utils/tool-result-render-mode.ts"() {
2259
+ DEFAULT_TOOL_RESULT_RENDER_MODE = "extend";
2260
+ }
2261
+ });
2078
2262
  function projectHash(absRoot) {
2079
- return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
2263
+ return createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 12);
2080
2264
  }
2081
2265
  function projectSlug(absRoot) {
2082
- const base = slugify2(path3.basename(absRoot));
2083
- const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
2266
+ const base = slugify2(path4.basename(absRoot));
2267
+ const hash = createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 6);
2084
2268
  return `${base}-${hash}`;
2085
2269
  }
2086
2270
  function slugify2(name) {
@@ -2088,170 +2272,91 @@ function slugify2(name) {
2088
2272
  }
2089
2273
  function wstackGlobalRoot() {
2090
2274
  const fromEnv = process.env["WRONGSTACK_HOME"];
2091
- if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
2092
- return path3.join(os.homedir(), ".wrongstack");
2275
+ if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
2276
+ return path4.join(os.homedir(), ".wrongstack");
2093
2277
  }
2094
2278
  function resolveWstackPaths(opts) {
2095
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
2279
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
2096
2280
  const hash = projectHash(opts.projectRoot);
2097
2281
  const slug = projectSlug(opts.projectRoot);
2098
- const projectDir = path3.join(globalRoot, "projects", slug);
2282
+ const projectDir = path4.join(globalRoot, "projects", slug);
2099
2283
  return {
2100
2284
  globalRoot,
2101
2285
  configDir: globalRoot,
2102
- globalConfig: path3.join(globalRoot, "config.json"),
2103
- secretsKey: path3.join(globalRoot, ".key"),
2104
- globalMemory: path3.join(globalRoot, "memory.md"),
2105
- globalSkills: path3.join(globalRoot, "skills"),
2106
- globalDesignKits: path3.join(globalRoot, "design-kits"),
2107
- globalPrompts: path3.join(globalRoot, "prompts"),
2108
- globalInstructions: path3.join(globalRoot, "instructions"),
2109
- promptUsage: path3.join(globalRoot, "prompt-usage.json"),
2110
- cacheDir: path3.join(globalRoot, "cache"),
2111
- modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
2112
- modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
2113
- historyFile: path3.join(globalRoot, "history"),
2114
- logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
2286
+ globalConfig: path4.join(globalRoot, "config.json"),
2287
+ secretsKey: path4.join(globalRoot, ".key"),
2288
+ globalMemory: path4.join(globalRoot, "memory.md"),
2289
+ globalSkills: path4.join(globalRoot, "skills"),
2290
+ globalDesignKits: path4.join(globalRoot, "design-kits"),
2291
+ globalPrompts: path4.join(globalRoot, "prompts"),
2292
+ globalInstructions: path4.join(globalRoot, "instructions"),
2293
+ promptUsage: path4.join(globalRoot, "prompt-usage.json"),
2294
+ cacheDir: path4.join(globalRoot, "cache"),
2295
+ modelsCache: path4.join(globalRoot, "cache", "models.dev.json"),
2296
+ modelsOverlayCache: path4.join(globalRoot, "cache", "models-overlay.json"),
2297
+ historyFile: path4.join(globalRoot, "history"),
2298
+ logFile: path4.join(globalRoot, "logs", "wrongstack.log"),
2115
2299
  projectDir,
2116
- projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
2117
- projectMemory: path3.join(projectDir, "memory.md"),
2118
- projectSessions: path3.join(projectDir, "sessions"),
2119
- projectTrust: path3.join(projectDir, "trust.json"),
2120
- projectMeta: path3.join(projectDir, "meta.json"),
2121
- projectLocalConfig: path3.join(projectDir, "config.local.json"),
2122
- inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
2123
- inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
2124
- inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
2125
- inProjectPrompts: path3.join(opts.projectRoot, ".wrongstack", "prompts"),
2126
- inProjectInstructions: path3.join(opts.projectRoot, ".wrongstack", "instructions"),
2127
- inProjectDesignKits: path3.join(opts.projectRoot, ".wrongstack", "design-kits"),
2128
- inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
2300
+ projectCodebaseIndex: path4.join(projectDir, "codebase-index"),
2301
+ projectMemory: path4.join(projectDir, "memory.md"),
2302
+ projectSessions: path4.join(projectDir, "sessions"),
2303
+ projectTrust: path4.join(projectDir, "trust.json"),
2304
+ projectMeta: path4.join(projectDir, "meta.json"),
2305
+ projectLocalConfig: path4.join(projectDir, "config.local.json"),
2306
+ inProjectConfig: path4.join(opts.projectRoot, ".wrongstack", "config.json"),
2307
+ inProjectAgentsFile: path4.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
2308
+ inProjectSkills: path4.join(opts.projectRoot, ".wrongstack", "skills"),
2309
+ inProjectPrompts: path4.join(opts.projectRoot, ".wrongstack", "prompts"),
2310
+ inProjectInstructions: path4.join(opts.projectRoot, ".wrongstack", "instructions"),
2311
+ inProjectDesignKits: path4.join(opts.projectRoot, ".wrongstack", "design-kits"),
2312
+ inProjectWorktrees: path4.join(opts.projectRoot, ".wrongstack", "worktrees"),
2129
2313
  projectHash: hash,
2130
2314
  projectSlug: slug,
2131
- projectGoal: path3.join(projectDir, "goal.json"),
2132
- projectSpecs: path3.join(projectDir, "specs"),
2133
- projectTaskGraphs: path3.join(projectDir, "task-graphs"),
2134
- projectSddSession: path3.join(projectDir, "sdd-session.json"),
2135
- projectPlan: path3.join(projectDir, "plan.json"),
2136
- projectAutophase: path3.join(projectDir, "autophase"),
2137
- projectSddBoards: path3.join(projectDir, "sdd-boards"),
2138
- syncConfig: path3.join(globalRoot, "sync.json"),
2139
- projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
2315
+ projectGoal: path4.join(projectDir, "goal.json"),
2316
+ projectSpecs: path4.join(projectDir, "specs"),
2317
+ projectTaskGraphs: path4.join(projectDir, "task-graphs"),
2318
+ projectSddSession: path4.join(projectDir, "sdd-session.json"),
2319
+ projectPlan: path4.join(projectDir, "plan.json"),
2320
+ projectAutophase: path4.join(projectDir, "autophase"),
2321
+ projectSddBoards: path4.join(projectDir, "sdd-boards"),
2322
+ syncConfig: path4.join(globalRoot, "sync.json"),
2323
+ projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
2140
2324
  };
2141
2325
  }
2142
-
2143
- // src/types/errors.ts
2144
- var ERROR_CODES = {
2145
- // Provider
2146
- PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
2147
- PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
2148
- PROVIDER_OVERLOADED: "PROVIDER_OVERLOADED",
2149
- PROVIDER_INVALID_REQUEST: "PROVIDER_INVALID_REQUEST",
2150
- PROVIDER_SERVER_ERROR: "PROVIDER_SERVER_ERROR",
2151
- PROVIDER_NETWORK_ERROR: "PROVIDER_NETWORK_ERROR",
2152
- PROVIDER_CONTEXT_OVERFLOW: "PROVIDER_CONTEXT_OVERFLOW",
2153
- // Tool
2154
- TOOL_NOT_FOUND: "TOOL_NOT_FOUND",
2155
- TOOL_PERMISSION_DENIED: "TOOL_PERMISSION_DENIED",
2156
- TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED",
2157
- TOOL_TIMEOUT: "TOOL_TIMEOUT",
2158
- TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
2159
- // Config
2160
- CONFIG_INVALID: "CONFIG_INVALID",
2161
- CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
2162
- CONFIG_PARSE_FAILED: "CONFIG_PARSE_FAILED",
2163
- CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
2164
- // Plugin
2165
- PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
2166
- PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
2167
- PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
2168
- // Agent
2169
- AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
2170
- AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
2171
- AGENT_ABORTED: "AGENT_ABORTED",
2172
- AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
2173
- // Session
2174
- SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
2175
- SESSION_CORRUPTED: "SESSION_CORRUPTED",
2176
- SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
2177
- // Container / Registry
2178
- CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
2179
- CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
2180
- CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
2181
- REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
2182
- REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
2183
- REGISTRY_INVALID: "REGISTRY_INVALID",
2184
- // File system
2185
- FS_READ_FAILED: "FS_READ_FAILED",
2186
- FS_WRITE_FAILED: "FS_WRITE_FAILED",
2187
- FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
2188
- FS_DELETE_FAILED: "FS_DELETE_FAILED",
2189
- FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
2190
- // SDD (Spec-Driven Development)
2191
- SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
2192
- SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
2193
- SDD_INVALID_STATE: "SDD_INVALID_STATE",
2194
- SDD_NOT_READY: "SDD_NOT_READY",
2195
- // General
2196
- VALIDATION_ERROR: "VALIDATION_ERROR",
2197
- UNKNOWN: "UNKNOWN"
2198
- };
2199
- var WrongStackError = class extends Error {
2200
- code;
2201
- subsystem;
2202
- severity;
2203
- recoverable;
2204
- context;
2205
- constructor(opts) {
2206
- super(opts.message, { cause: opts.cause });
2207
- this.name = "WrongStackError";
2208
- this.code = opts.code;
2209
- this.subsystem = opts.subsystem;
2210
- this.severity = opts.severity ?? "error";
2211
- this.recoverable = opts.recoverable ?? false;
2212
- this.context = opts.context;
2326
+ var init_wstack_paths = __esm({
2327
+ "src/utils/wstack-paths.ts"() {
2213
2328
  }
2214
- /**
2215
- * Render a one-line user-facing description.
2216
- * Subclasses should override for domain-specific formatting.
2217
- */
2218
- describe() {
2219
- const ctx = this.context ? ` ${formatContext(this.context)}` : "";
2220
- return `${this.code}: ${this.message}${ctx}`;
2329
+ });
2330
+
2331
+ // src/utils/index.ts
2332
+ var init_utils = __esm({
2333
+ "src/utils/index.ts"() {
2334
+ init_assert_never();
2335
+ init_atomic_write();
2336
+ init_child_env();
2337
+ init_color();
2338
+ init_config_json();
2339
+ init_diff();
2340
+ init_error();
2341
+ init_expect_defined();
2342
+ init_glob_match();
2343
+ init_newline_normalize();
2344
+ init_safe_json();
2345
+ init_sleep();
2346
+ init_string();
2347
+ init_task_format();
2348
+ init_term();
2349
+ init_todos_format();
2350
+ init_tool_subject();
2351
+ init_wstack_paths();
2221
2352
  }
2222
- };
2353
+ });
2354
+
2355
+ // src/types/errors.ts
2223
2356
  function formatContext(ctx) {
2224
2357
  const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
2225
2358
  return parts.length > 0 ? `[${parts.join(" ")}]` : "";
2226
2359
  }
2227
- var ConfigError = class extends WrongStackError {
2228
- constructor(opts) {
2229
- super({
2230
- message: opts.message,
2231
- code: opts.code,
2232
- subsystem: "config",
2233
- severity: "fatal",
2234
- recoverable: false,
2235
- context: opts.context,
2236
- cause: opts.cause
2237
- });
2238
- this.name = "ConfigError";
2239
- }
2240
- };
2241
- var AgentError = class extends WrongStackError {
2242
- constructor(opts) {
2243
- super({
2244
- message: opts.message,
2245
- code: opts.code,
2246
- subsystem: "agent",
2247
- severity: opts.code === ERROR_CODES.AGENT_ABORTED ? "warning" : "error",
2248
- recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,
2249
- context: opts.context,
2250
- cause: opts.cause
2251
- });
2252
- this.name = "AgentError";
2253
- }
2254
- };
2255
2360
  function toWrongStackError(err, code = ERROR_CODES.AGENT_RUN_FAILED) {
2256
2361
  if (err instanceof WrongStackError) return err;
2257
2362
  const message = toErrorMessage(err);
@@ -2261,66 +2366,201 @@ function toWrongStackError(err, code = ERROR_CODES.AGENT_RUN_FAILED) {
2261
2366
  cause: err
2262
2367
  });
2263
2368
  }
2264
- var SddError = class extends WrongStackError {
2265
- constructor(opts) {
2266
- super({
2267
- message: opts.message,
2268
- code: opts.code,
2269
- subsystem: "sdd",
2270
- severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? "warning" : "error",
2271
- recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,
2272
- context: opts.context,
2273
- cause: opts.cause
2274
- });
2275
- this.name = "SddError";
2276
- }
2277
- };
2278
- var FsError = class extends WrongStackError {
2279
- path;
2280
- constructor(opts) {
2281
- super({
2282
- message: opts.message,
2283
- code: opts.code,
2284
- subsystem: "fs",
2285
- severity: "error",
2286
- recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
2287
- context: { path: opts.path, ...opts.context },
2288
- cause: opts.cause
2289
- });
2290
- this.name = "FsError";
2291
- this.path = opts.path;
2292
- }
2293
- };
2294
- var FetchError = class extends WrongStackError {
2295
- status;
2296
- constructor(opts) {
2297
- super({
2298
- message: opts.message,
2299
- code: ERROR_CODES.VALIDATION_ERROR,
2300
- subsystem: "general",
2301
- severity: "error",
2302
- recoverable: opts.status === 429 || opts.status >= 500,
2303
- context: { status: opts.status, ...opts.context },
2304
- cause: opts.cause
2305
- });
2306
- this.name = "FetchError";
2307
- this.status = opts.status;
2308
- }
2309
- };
2310
- var ToolValidationError = class extends WrongStackError {
2311
- constructor(opts) {
2312
- super({
2313
- message: opts.message,
2314
- code: ERROR_CODES.VALIDATION_ERROR,
2315
- subsystem: "general",
2316
- severity: "error",
2317
- recoverable: false,
2318
- context: { field: opts.field, ...opts.context },
2319
- cause: opts.cause
2320
- });
2321
- this.name = "ToolValidationError";
2369
+ function isWrongStackError(err) {
2370
+ return err instanceof WrongStackError;
2371
+ }
2372
+ var ERROR_CODES, WrongStackError, ToolError, ConfigError, AgentError, SddError, FsError, FetchError, ToolValidationError;
2373
+ var init_errors = __esm({
2374
+ "src/types/errors.ts"() {
2375
+ init_utils();
2376
+ ERROR_CODES = {
2377
+ // Provider
2378
+ PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
2379
+ PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
2380
+ PROVIDER_OVERLOADED: "PROVIDER_OVERLOADED",
2381
+ PROVIDER_INVALID_REQUEST: "PROVIDER_INVALID_REQUEST",
2382
+ PROVIDER_SERVER_ERROR: "PROVIDER_SERVER_ERROR",
2383
+ PROVIDER_NETWORK_ERROR: "PROVIDER_NETWORK_ERROR",
2384
+ PROVIDER_CONTEXT_OVERFLOW: "PROVIDER_CONTEXT_OVERFLOW",
2385
+ // Tool
2386
+ TOOL_NOT_FOUND: "TOOL_NOT_FOUND",
2387
+ TOOL_PERMISSION_DENIED: "TOOL_PERMISSION_DENIED",
2388
+ TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED",
2389
+ TOOL_TIMEOUT: "TOOL_TIMEOUT",
2390
+ TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
2391
+ // Config
2392
+ CONFIG_INVALID: "CONFIG_INVALID",
2393
+ CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
2394
+ CONFIG_PARSE_FAILED: "CONFIG_PARSE_FAILED",
2395
+ CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
2396
+ // Plugin
2397
+ PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
2398
+ PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
2399
+ PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
2400
+ // Agent
2401
+ AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
2402
+ AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
2403
+ AGENT_ABORTED: "AGENT_ABORTED",
2404
+ AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
2405
+ // Session
2406
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
2407
+ SESSION_CORRUPTED: "SESSION_CORRUPTED",
2408
+ SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
2409
+ // Container / Registry
2410
+ CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
2411
+ CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
2412
+ CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
2413
+ REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
2414
+ REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
2415
+ REGISTRY_INVALID: "REGISTRY_INVALID",
2416
+ // File system
2417
+ FS_READ_FAILED: "FS_READ_FAILED",
2418
+ FS_WRITE_FAILED: "FS_WRITE_FAILED",
2419
+ FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
2420
+ FS_DELETE_FAILED: "FS_DELETE_FAILED",
2421
+ FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
2422
+ // SDD (Spec-Driven Development)
2423
+ SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
2424
+ SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
2425
+ SDD_INVALID_STATE: "SDD_INVALID_STATE",
2426
+ SDD_NOT_READY: "SDD_NOT_READY",
2427
+ // General
2428
+ VALIDATION_ERROR: "VALIDATION_ERROR",
2429
+ PARSE_FAILED: "PARSE_FAILED",
2430
+ UNKNOWN: "UNKNOWN"
2431
+ };
2432
+ WrongStackError = class extends Error {
2433
+ code;
2434
+ subsystem;
2435
+ severity;
2436
+ recoverable;
2437
+ context;
2438
+ constructor(opts) {
2439
+ super(opts.message, { cause: opts.cause });
2440
+ this.name = "WrongStackError";
2441
+ this.code = opts.code;
2442
+ this.subsystem = opts.subsystem;
2443
+ this.severity = opts.severity ?? "error";
2444
+ this.recoverable = opts.recoverable ?? false;
2445
+ this.context = opts.context;
2446
+ }
2447
+ /**
2448
+ * Render a one-line user-facing description.
2449
+ * Subclasses should override for domain-specific formatting.
2450
+ */
2451
+ describe() {
2452
+ const ctx = this.context ? ` ${formatContext(this.context)}` : "";
2453
+ return `${this.code}: ${this.message}${ctx}`;
2454
+ }
2455
+ };
2456
+ ToolError = class extends WrongStackError {
2457
+ toolName;
2458
+ constructor(opts) {
2459
+ super({
2460
+ message: opts.message,
2461
+ code: opts.code,
2462
+ subsystem: "tool",
2463
+ recoverable: opts.recoverable,
2464
+ context: { tool: opts.toolName, ...opts.context },
2465
+ cause: opts.cause
2466
+ });
2467
+ this.name = "ToolError";
2468
+ this.toolName = opts.toolName;
2469
+ }
2470
+ };
2471
+ ConfigError = class extends WrongStackError {
2472
+ constructor(opts) {
2473
+ super({
2474
+ message: opts.message,
2475
+ code: opts.code,
2476
+ subsystem: "config",
2477
+ severity: "fatal",
2478
+ recoverable: false,
2479
+ context: opts.context,
2480
+ cause: opts.cause
2481
+ });
2482
+ this.name = "ConfigError";
2483
+ }
2484
+ };
2485
+ AgentError = class extends WrongStackError {
2486
+ constructor(opts) {
2487
+ super({
2488
+ message: opts.message,
2489
+ code: opts.code,
2490
+ subsystem: "agent",
2491
+ severity: opts.code === ERROR_CODES.AGENT_ABORTED ? "warning" : "error",
2492
+ recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,
2493
+ context: opts.context,
2494
+ cause: opts.cause
2495
+ });
2496
+ this.name = "AgentError";
2497
+ }
2498
+ };
2499
+ SddError = class extends WrongStackError {
2500
+ constructor(opts) {
2501
+ super({
2502
+ message: opts.message,
2503
+ code: opts.code,
2504
+ subsystem: "sdd",
2505
+ severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? "warning" : "error",
2506
+ recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,
2507
+ context: opts.context,
2508
+ cause: opts.cause
2509
+ });
2510
+ this.name = "SddError";
2511
+ }
2512
+ };
2513
+ FsError = class extends WrongStackError {
2514
+ path;
2515
+ constructor(opts) {
2516
+ super({
2517
+ message: opts.message,
2518
+ code: opts.code,
2519
+ subsystem: "fs",
2520
+ severity: "error",
2521
+ recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
2522
+ context: { path: opts.path, ...opts.context },
2523
+ cause: opts.cause
2524
+ });
2525
+ this.name = "FsError";
2526
+ this.path = opts.path;
2527
+ }
2528
+ };
2529
+ FetchError = class extends WrongStackError {
2530
+ status;
2531
+ constructor(opts) {
2532
+ super({
2533
+ message: opts.message,
2534
+ code: ERROR_CODES.VALIDATION_ERROR,
2535
+ subsystem: "general",
2536
+ severity: "error",
2537
+ recoverable: opts.status === 429 || opts.status >= 500,
2538
+ context: { status: opts.status, ...opts.context },
2539
+ cause: opts.cause
2540
+ });
2541
+ this.name = "FetchError";
2542
+ this.status = opts.status;
2543
+ }
2544
+ };
2545
+ ToolValidationError = class extends WrongStackError {
2546
+ constructor(opts) {
2547
+ super({
2548
+ message: opts.message,
2549
+ code: ERROR_CODES.VALIDATION_ERROR,
2550
+ subsystem: "general",
2551
+ severity: "error",
2552
+ recoverable: false,
2553
+ context: { field: opts.field, ...opts.context },
2554
+ cause: opts.cause
2555
+ });
2556
+ this.name = "ToolValidationError";
2557
+ }
2558
+ };
2322
2559
  }
2323
- };
2560
+ });
2561
+
2562
+ // src/coordination/agent-bridge.ts
2563
+ init_errors();
2324
2564
 
2325
2565
  // src/coordination/in-memory-transport.ts
2326
2566
  var InMemoryBridgeTransport = class {
@@ -2424,7 +2664,7 @@ var InMemoryAgentBridge = class {
2424
2664
  });
2425
2665
  }
2426
2666
  this.inflightGuards.add(correlationId);
2427
- return new Promise((resolve11, reject) => {
2667
+ return new Promise((resolve12, reject) => {
2428
2668
  const timer = setTimeout(() => {
2429
2669
  this.inflightGuards.delete(correlationId);
2430
2670
  this.pendingRequests.delete(correlationId);
@@ -2443,7 +2683,7 @@ var InMemoryAgentBridge = class {
2443
2683
  return;
2444
2684
  }
2445
2685
  this.pendingRequests.set(correlationId, {
2446
- resolve: resolve11,
2686
+ resolve: resolve12,
2447
2687
  reject,
2448
2688
  timer
2449
2689
  });
@@ -2485,6 +2725,9 @@ function createMessage(type, from, payload, to) {
2485
2725
  };
2486
2726
  }
2487
2727
 
2728
+ // src/coordination/agent-subagent-runner.ts
2729
+ init_errors();
2730
+
2488
2731
  // src/coordination/subagent-budget.ts
2489
2732
  var TIMEOUT_PREEMPT_FRACTION = 0.85;
2490
2733
  var DECISION_TIMEOUT_MS = 6e4;
@@ -2556,6 +2799,7 @@ var SubagentBudget = class _SubagentBudget {
2556
2799
  */
2557
2800
  lastActivityTime = null;
2558
2801
  _onThreshold;
2802
+ _sessionId;
2559
2803
  /**
2560
2804
  * Hard cap on how long `_negotiateExtension` waits for the coordinator to
2561
2805
  * respond before defaulting to 'stop'. Without this fallback an absent
@@ -2624,10 +2868,15 @@ var SubagentBudget = class _SubagentBudget {
2624
2868
  get mode() {
2625
2869
  return this._mode;
2626
2870
  }
2627
- constructor(limits = {}, mode = "auto") {
2871
+ constructor(limits = {}, mode = "auto", options = {}) {
2628
2872
  this._mode = mode;
2873
+ this._sessionId = options.sessionId;
2629
2874
  this.limits = { ...limits };
2630
2875
  }
2876
+ currentSessionId() {
2877
+ const value = typeof this._sessionId === "function" ? this._sessionId() : this._sessionId;
2878
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2879
+ }
2631
2880
  start() {
2632
2881
  this.startTime = Date.now();
2633
2882
  this.lastActivityTime = this.startTime;
@@ -2786,16 +3035,18 @@ var SubagentBudget = class _SubagentBudget {
2786
3035
  if (!bus?.hasListenerFor("budget.threshold_reached")) {
2787
3036
  return Promise.resolve("stop");
2788
3037
  }
2789
- return new Promise((resolve11) => {
3038
+ return new Promise((resolve12) => {
2790
3039
  let resolved = false;
2791
3040
  const respond = (d) => {
2792
3041
  if (resolved) return;
2793
3042
  resolved = true;
2794
3043
  clearTimeout(fallback);
2795
- resolve11(d);
3044
+ resolve12(d);
2796
3045
  };
2797
3046
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
3047
+ const sessionId = this.currentSessionId();
2798
3048
  bus.emit("budget.threshold_reached", {
3049
+ ...sessionId ? { sessionId } : {},
2799
3050
  kind: entry.kind,
2800
3051
  used: entry.used,
2801
3052
  limit: entry.limit,
@@ -3194,24 +3445,24 @@ function agentPrompt(id) {
3194
3445
  const fileName = `${id}.md`;
3195
3446
  for (const dir of agentPromptDirCandidates()) {
3196
3447
  try {
3197
- return readFileSync(path3.join(dir, fileName), "utf8").trimEnd();
3448
+ return readFileSync(path4.join(dir, fileName), "utf8").trimEnd();
3198
3449
  } catch {
3199
3450
  }
3200
3451
  }
3201
3452
  return "";
3202
3453
  }
3203
3454
  function agentPromptDirCandidates() {
3204
- const here = path3.dirname(fileURLToPath(import.meta.url));
3455
+ const here = path4.dirname(fileURLToPath(import.meta.url));
3205
3456
  const explicitDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"];
3206
- const globalRoot = process.env["WRONGSTACK_HOME"] || path3.join(os.homedir(), ".wrongstack");
3457
+ const globalRoot = process.env["WRONGSTACK_HOME"] || path4.join(os.homedir(), ".wrongstack");
3207
3458
  const candidates = [
3208
- ...explicitDir ? [path3.resolve(explicitDir)] : [],
3209
- path3.join(globalRoot, "instructions", "agents"),
3210
- path3.resolve(here, "../../../../instructions/agents"),
3211
- path3.resolve(here, "../../../instructions/agents"),
3212
- path3.resolve(here, "../../instructions/agents"),
3213
- path3.resolve(here, "../instructions/agents"),
3214
- path3.resolve(here, "instructions/agents")
3459
+ ...explicitDir ? [path4.resolve(explicitDir)] : [],
3460
+ path4.join(globalRoot, "instructions", "agents"),
3461
+ path4.resolve(here, "../../../../instructions/agents"),
3462
+ path4.resolve(here, "../../../instructions/agents"),
3463
+ path4.resolve(here, "../../instructions/agents"),
3464
+ path4.resolve(here, "../instructions/agents"),
3465
+ path4.resolve(here, "instructions/agents")
3215
3466
  ];
3216
3467
  return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
3217
3468
  }
@@ -4874,6 +5125,8 @@ FLEET_ROSTER_BUDGETS["goose"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations:
4874
5125
  });
4875
5126
 
4876
5127
  // src/coordination/delegate-tool.ts
5128
+ init_safe_json();
5129
+ init_error();
4877
5130
  function createDelegateTool(opts) {
4878
5131
  const defaultTimeoutMs = opts.defaultTimeoutMs ?? 30 * 60 * 1e3;
4879
5132
  const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
@@ -4946,6 +5199,7 @@ function createDelegateTool(opts) {
4946
5199
  mutating: false,
4947
5200
  inputSchema,
4948
5201
  async execute(input) {
5202
+ const sessionId = opts.directorRunId;
4949
5203
  const i = input ?? {};
4950
5204
  if (typeof i.task !== "string" || !i.task.trim()) {
4951
5205
  return { ok: false, error: "`task` is required." };
@@ -5011,7 +5265,7 @@ function createDelegateTool(opts) {
5011
5265
  if (!cfg.timeoutMs) {
5012
5266
  cfg.timeoutMs = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
5013
5267
  }
5014
- opts.events?.emit("delegate.started", { target, task: i.task });
5268
+ opts.events?.emit("delegate.started", { sessionId, target, task: i.task });
5015
5269
  const subagentId = await director.spawn(cfg);
5016
5270
  const taskId = await director.assign({
5017
5271
  id: `${randomUUID()}`,
@@ -5019,7 +5273,7 @@ function createDelegateTool(opts) {
5019
5273
  subagentId
5020
5274
  });
5021
5275
  const dir = director;
5022
- const result = await new Promise((resolve11) => {
5276
+ const result = await new Promise((resolve12) => {
5023
5277
  let settled = false;
5024
5278
  let timer;
5025
5279
  const finish = (value) => {
@@ -5029,7 +5283,7 @@ function createDelegateTool(opts) {
5029
5283
  offTool();
5030
5284
  offIter();
5031
5285
  offProgress();
5032
- resolve11(value);
5286
+ resolve12(value);
5033
5287
  };
5034
5288
  const arm = () => {
5035
5289
  if (timer) clearTimeout(timer);
@@ -5047,6 +5301,7 @@ function createDelegateTool(opts) {
5047
5301
  if ("__timeout" in result) {
5048
5302
  const partial2 = await readSubagentPartial(opts, subagentId);
5049
5303
  opts.events?.emit("delegate.completed", {
5304
+ sessionId,
5050
5305
  target,
5051
5306
  task: i.task,
5052
5307
  ok: false,
@@ -5070,6 +5325,7 @@ function createDelegateTool(opts) {
5070
5325
  if ("__emptyResult" in result) {
5071
5326
  const partial2 = await readSubagentPartial(opts, subagentId);
5072
5327
  opts.events?.emit("delegate.completed", {
5328
+ sessionId,
5073
5329
  target,
5074
5330
  task: i.task,
5075
5331
  ok: false,
@@ -5103,6 +5359,7 @@ function createDelegateTool(opts) {
5103
5359
  costUsd = void 0;
5104
5360
  }
5105
5361
  opts.events?.emit("delegate.completed", {
5362
+ sessionId,
5106
5363
  target,
5107
5364
  task: i.task,
5108
5365
  ok: result.status === "success",
@@ -5137,6 +5394,7 @@ function createDelegateTool(opts) {
5137
5394
  } catch (err) {
5138
5395
  const message = toErrorMessage(err);
5139
5396
  opts.events?.emit("delegate.completed", {
5397
+ sessionId,
5140
5398
  target,
5141
5399
  task: i.task,
5142
5400
  ok: false,
@@ -5236,13 +5494,13 @@ async function readSubagentPartial(opts, subagentId) {
5236
5494
  if (!opts.sessionsRoot) return void 0;
5237
5495
  const candidates = [];
5238
5496
  if (opts.directorRunId) {
5239
- candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
5497
+ candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
5240
5498
  } else {
5241
5499
  try {
5242
5500
  const entries = await fsp7.readdir(opts.sessionsRoot, { withFileTypes: true });
5243
5501
  for (const entry of entries) {
5244
5502
  if (entry.isDirectory()) {
5245
- candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
5503
+ candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
5246
5504
  }
5247
5505
  }
5248
5506
  } catch {
@@ -5288,6 +5546,7 @@ async function readSubagentPartial(opts, subagentId) {
5288
5546
 
5289
5547
  // src/storage/director-state.ts
5290
5548
  init_atomic_write();
5549
+ init_error();
5291
5550
  async function loadDirectorState(filePath) {
5292
5551
  let raw;
5293
5552
  try {
@@ -5469,6 +5728,11 @@ var DirectorStateCheckpoint = class {
5469
5728
 
5470
5729
  // src/coordination/director.ts
5471
5730
  init_atomic_write();
5731
+ init_error();
5732
+ init_safe_json();
5733
+
5734
+ // src/coordination/collab-debug.ts
5735
+ init_glob_expand();
5472
5736
  var DEFAULT_MAX_TARGET_FILES = 30;
5473
5737
  var CollabSession = class extends EventEmitter {
5474
5738
  sessionId;
@@ -5557,7 +5821,7 @@ var CollabSession = class extends EventEmitter {
5557
5821
  }
5558
5822
  for (const filePath of allFiles) {
5559
5823
  try {
5560
- const [content, stat9] = await Promise.all([
5824
+ const [content, stat10] = await Promise.all([
5561
5825
  fsp7.readFile(filePath, "utf8"),
5562
5826
  fsp7.stat(filePath)
5563
5827
  ]);
@@ -5567,8 +5831,8 @@ var CollabSession = class extends EventEmitter {
5567
5831
  path: filePath,
5568
5832
  content,
5569
5833
  language,
5570
- snapshotMtimeMs: stat9.mtimeMs,
5571
- snapshotSizeBytes: stat9.size
5834
+ snapshotMtimeMs: stat10.mtimeMs,
5835
+ snapshotSizeBytes: stat10.size
5572
5836
  });
5573
5837
  } catch {
5574
5838
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
@@ -5724,7 +5988,7 @@ var CollabSession = class extends EventEmitter {
5724
5988
  id: `${role}-${this.sessionId}`,
5725
5989
  name: role,
5726
5990
  role,
5727
- tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
5991
+ tools: ["fleet_emit", "fleet", "read", "grep", "glob", "bash", "write"],
5728
5992
  maxIterations: budget.maxIterations,
5729
5993
  maxToolCalls: budget.maxToolCalls,
5730
5994
  timeoutMs: budget.timeoutMs
@@ -5981,9 +6245,9 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
5981
6245
  for (const file of this.snapshot.files) {
5982
6246
  if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
5983
6247
  try {
5984
- const stat9 = await fsp7.stat(file.path);
5985
- const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat9.mtimeMs > file.snapshotMtimeMs + 1;
5986
- const sizeChanged = file.snapshotSizeBytes !== void 0 && stat9.size !== file.snapshotSizeBytes;
6248
+ const stat10 = await fsp7.stat(file.path);
6249
+ const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat10.mtimeMs > file.snapshotMtimeMs + 1;
6250
+ const sizeChanged = file.snapshotSizeBytes !== void 0 && stat10.size !== file.snapshotSizeBytes;
5987
6251
  if (mtimeChanged || sizeChanged) {
5988
6252
  warnings.push(`${file.path} changed after the collab snapshot was captured.`);
5989
6253
  }
@@ -6060,7 +6324,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
6060
6324
  function readBundledInstructionText(relativePath) {
6061
6325
  for (const root of instructionRootCandidates()) {
6062
6326
  try {
6063
- return readFileSync(path3.join(root, relativePath), "utf8").trimEnd();
6327
+ return readFileSync(path4.join(root, relativePath), "utf8").trimEnd();
6064
6328
  } catch {
6065
6329
  }
6066
6330
  }
@@ -6073,11 +6337,11 @@ function renderInstructionTemplate(template, values) {
6073
6337
  );
6074
6338
  }
6075
6339
  function instructionRootCandidates() {
6076
- const here = path3.dirname(fileURLToPath(import.meta.url));
6340
+ const here = path4.dirname(fileURLToPath(import.meta.url));
6077
6341
  const candidates = [
6078
- path3.resolve(here, "../../instructions"),
6079
- path3.resolve(here, "../instructions"),
6080
- path3.resolve(here, "instructions")
6342
+ path4.resolve(here, "../../instructions"),
6343
+ path4.resolve(here, "../instructions"),
6344
+ path4.resolve(here, "instructions")
6081
6345
  ];
6082
6346
  return candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
6083
6347
  }
@@ -6151,6 +6415,7 @@ function rosterSummaryFromConfigs(roster) {
6151
6415
  }
6152
6416
 
6153
6417
  // src/coordination/dispatcher.ts
6418
+ init_safe_json();
6154
6419
  var DEFAULT_DISPATCH_ROLE = "executor";
6155
6420
  var FALLBACK_DEFINITION = {
6156
6421
  config: { role: "unknown", name: "Unknown Agent" },
@@ -6272,6 +6537,9 @@ function makeLLMClassifier(complete) {
6272
6537
  };
6273
6538
  }
6274
6539
 
6540
+ // src/coordination/director-tools.ts
6541
+ init_error();
6542
+
6275
6543
  // src/security/capabilities.ts
6276
6544
  var ToolCapabilities = {
6277
6545
  /** Can execute arbitrary commands in the user's shell (the `bash` tool). */
@@ -6574,96 +6842,91 @@ function makeTerminateAllTool(director) {
6574
6842
  }
6575
6843
  };
6576
6844
  }
6577
- function makeFleetStatusTool(director) {
6578
- return {
6579
- name: "fleet_status",
6580
- description: "Snapshot of the fleet \u2014 every subagent's current status, coordinator counts (total/running/idle/stopped), pending task descriptions, and usage rollup.",
6581
- permission: "auto",
6582
- mutating: false,
6583
- capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
6584
- inputSchema: { type: "object", properties: {}, required: [] },
6585
- async execute() {
6586
- const base = director.status();
6587
- const fm = director.fleetManager;
6588
- const stats = fm?.getFleetStats();
6589
- const fleetStatus = fm?.getFleetStatus();
6590
- return {
6591
- subagents: base.subagents,
6592
- coordinatorStats: stats ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
6593
- pending: fleetStatus?.pending ?? [],
6594
- usage: fm?.snapshot()
6595
- };
6596
- }
6597
- };
6598
- }
6599
- function makeFleetUsageTool(director) {
6845
+ function makeFleetTool(director) {
6600
6846
  return {
6601
- name: "fleet_usage",
6602
- description: "Token + cost breakdown across the fleet, per-subagent and totals.",
6603
- permission: "auto",
6604
- mutating: false,
6605
- capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
6606
- inputSchema: { type: "object", properties: {}, required: [] },
6607
- async execute() {
6608
- return director.snapshot();
6609
- }
6610
- };
6611
- }
6612
- function makeFleetSessionTool(director) {
6613
- return {
6614
- name: "fleet_session",
6615
- 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.",
6847
+ name: "fleet",
6848
+ 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).',
6849
+ usageHint: 'action: "status" (default) | "usage" | "health" | "session".\nFor "session", pass subagentId (required) and optional tail (trailing JSONL lines).',
6616
6850
  permission: "auto",
6617
6851
  mutating: false,
6618
6852
  capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
6619
6853
  inputSchema: {
6620
6854
  type: "object",
6621
6855
  properties: {
6622
- subagentId: { type: "string", description: "Subagent id to read the transcript of." },
6623
- tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
6624
- },
6625
- required: ["subagentId"]
6856
+ action: {
6857
+ type: "string",
6858
+ enum: ["status", "usage", "health", "session"],
6859
+ description: "Observation to retrieve (default: status)."
6860
+ },
6861
+ subagentId: {
6862
+ type: "string",
6863
+ description: 'Subagent id (required for action: "session").'
6864
+ },
6865
+ tail: {
6866
+ type: "number",
6867
+ description: 'Number of trailing JSONL lines (action: "session" only). Omit for the full transcript.'
6868
+ }
6869
+ }
6626
6870
  },
6627
6871
  async execute(input) {
6628
- const i = input;
6629
- const result = await director.readSession(i.subagentId, i.tail);
6630
- if (!result) {
6631
- return {
6632
- error: `fleet_session: transcript unavailable for "${i.subagentId}". Is sessionsRoot configured?`
6633
- };
6634
- }
6635
- return result;
6636
- }
6637
- };
6638
- }
6639
- function makeFleetHealthTool(director) {
6640
- return {
6641
- name: "fleet_health",
6642
- 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.",
6643
- permission: "auto",
6644
- mutating: false,
6645
- capabilities: [ToolCapabilities.COORDINATION_FLEET_READ],
6646
- inputSchema: { type: "object", properties: {}, required: [] },
6647
- async execute() {
6648
- const status = director.status();
6649
- const snapshot = director.snapshot();
6650
- const subagents = status.subagents ?? [];
6651
- const perSubagent = snapshot.perSubagent ?? {};
6652
- return {
6653
- subagents: subagents.map((s) => {
6654
- const usage = perSubagent[s.id];
6872
+ const i = input ?? {};
6873
+ const action = i.action ?? "status";
6874
+ switch (action) {
6875
+ case "status": {
6876
+ const base = director.status();
6877
+ const fm = director.fleetManager;
6878
+ const stats = fm?.getFleetStats();
6879
+ const fleetStatus = fm?.getFleetStatus();
6655
6880
  return {
6656
- id: s.id,
6657
- status: s.status,
6658
- lastEventAt: usage?.lastEventAt,
6659
- budgetPressure: {
6660
- iterations: usage?.iterations,
6661
- toolCalls: usage?.toolCalls,
6662
- costUsd: usage?.cost
6663
- }
6881
+ action: "status",
6882
+ subagents: base.subagents,
6883
+ coordinatorStats: stats ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
6884
+ pending: fleetStatus?.pending ?? [],
6885
+ usage: fm?.snapshot()
6664
6886
  };
6665
- })
6666
- };
6887
+ }
6888
+ case "usage": {
6889
+ return { action: "usage", ...director.snapshot() };
6890
+ }
6891
+ case "health": {
6892
+ const status = director.status();
6893
+ const snapshot = director.snapshot();
6894
+ const subagents = status.subagents ?? [];
6895
+ const perSubagent = snapshot.perSubagent ?? {};
6896
+ return {
6897
+ action: "health",
6898
+ subagents: subagents.map((s) => {
6899
+ const usage = perSubagent[s.id];
6900
+ return {
6901
+ id: s.id,
6902
+ status: s.status,
6903
+ lastEventAt: usage?.lastEventAt,
6904
+ budgetPressure: {
6905
+ iterations: usage?.iterations,
6906
+ toolCalls: usage?.toolCalls,
6907
+ costUsd: usage?.cost
6908
+ }
6909
+ };
6910
+ })
6911
+ };
6912
+ }
6913
+ case "session": {
6914
+ const subagentId = i.subagentId;
6915
+ if (!subagentId) {
6916
+ return { action: "session", error: 'fleet: subagentId is required for action: "session"' };
6917
+ }
6918
+ const result = await director.readSession(subagentId, i.tail);
6919
+ if (!result) {
6920
+ return {
6921
+ action: "session",
6922
+ error: `fleet: transcript unavailable for "${subagentId}". Is sessionsRoot configured?`
6923
+ };
6924
+ }
6925
+ return { action: "session", ...result };
6926
+ }
6927
+ default:
6928
+ return { error: `fleet: unknown action "${action}". Valid: status, usage, health, session.` };
6929
+ }
6667
6930
  }
6668
6931
  };
6669
6932
  }
@@ -7039,6 +7302,8 @@ function hashStr(s) {
7039
7302
  }
7040
7303
 
7041
7304
  // src/types/provider.ts
7305
+ init_errors();
7306
+ init_string();
7042
7307
  var ProviderError = class extends WrongStackError {
7043
7308
  status;
7044
7309
  retryable;
@@ -7146,6 +7411,8 @@ function resolveModelMatrix(matrix, role) {
7146
7411
  }
7147
7412
 
7148
7413
  // src/coordination/coordinator/error-classifier.ts
7414
+ init_errors();
7415
+ init_error();
7149
7416
  function classifySubagentError(err, hints = {}) {
7150
7417
  if (err instanceof AgentError && err.cause) {
7151
7418
  return classifySubagentError(err.cause, hints);
@@ -7332,6 +7599,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7332
7599
  coordinatorId;
7333
7600
  config;
7334
7601
  runner;
7602
+ sessionId;
7335
7603
  fleetBus;
7336
7604
  subagents = /* @__PURE__ */ new Map();
7337
7605
  /**
@@ -7366,6 +7634,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7366
7634
  this.coordinatorId = config.coordinatorId;
7367
7635
  this.config = config;
7368
7636
  this.runner = options.runner;
7637
+ this.sessionId = options.sessionId;
7638
+ }
7639
+ currentSessionId() {
7640
+ const value = typeof this.sessionId === "function" ? this.sessionId() : this.sessionId;
7641
+ return typeof value === "string" && value.length > 0 ? value : void 0;
7369
7642
  }
7370
7643
  /**
7371
7644
  * Replace the runner after construction. Used when the runner depends
@@ -7524,11 +7797,16 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7524
7797
  status: s.status,
7525
7798
  assigned: s.context.parentBridge !== null
7526
7799
  }));
7800
+ const sessionId = this.currentSessionId();
7527
7801
  this.fleetBus?.emit({
7528
7802
  subagentId: this.coordinatorId,
7529
7803
  ts: Date.now(),
7530
7804
  type: "coordinator.stats",
7531
- payload: { ...stats, subagentStatuses }
7805
+ payload: {
7806
+ ...sessionId ? { sessionId } : {},
7807
+ ...stats,
7808
+ subagentStatuses
7809
+ }
7532
7810
  });
7533
7811
  }
7534
7812
  getStatus() {
@@ -7560,7 +7838,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7560
7838
  taskIds.map((id) => {
7561
7839
  const cached = this.completedResults.find((r) => r.taskId === id);
7562
7840
  if (cached) return cached;
7563
- return new Promise((resolve11, reject) => {
7841
+ return new Promise((resolve12, reject) => {
7564
7842
  const timeout = setTimeout(() => {
7565
7843
  this.off("task.completed", handler);
7566
7844
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -7569,7 +7847,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7569
7847
  if (result.taskId === id) {
7570
7848
  clearTimeout(timeout);
7571
7849
  this.off("task.completed", handler);
7572
- resolve11(result);
7850
+ resolve12(result);
7573
7851
  }
7574
7852
  };
7575
7853
  this.on("task.completed", handler);
@@ -7731,16 +8009,20 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7731
8009
  const rawTimeoutMs = subagent.config.timeoutMs;
7732
8010
  const rawIdleTimeoutMs = subagent.config.idleTimeoutMs;
7733
8011
  const configWithRosterDefaults = applyRosterBudget(subagent.config);
7734
- const budget = new SubagentBudget({
7735
- maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
7736
- maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
7737
- maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
7738
- maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
7739
- // Wall-clock cap is opt-in (explicit config / defaultBudget only); the
7740
- // roster no longer supplies one. Idle is the default reaper.
7741
- timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs,
7742
- idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
7743
- });
8012
+ const budget = new SubagentBudget(
8013
+ {
8014
+ maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
8015
+ maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
8016
+ maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
8017
+ maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
8018
+ // Wall-clock cap is opt-in (explicit config / defaultBudget only); the
8019
+ // roster no longer supplies one. Idle is the default reaper.
8020
+ timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs,
8021
+ idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
8022
+ },
8023
+ "auto",
8024
+ { sessionId: () => this.currentSessionId() }
8025
+ );
7744
8026
  subagent.activeBudget = budget;
7745
8027
  if (!this.runner) {
7746
8028
  return;
@@ -7837,13 +8119,15 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7837
8119
  }
7838
8120
  return new Promise((resolveDecision) => {
7839
8121
  let settled = false;
7840
- const resolve11 = (d) => {
8122
+ const resolve12 = (d) => {
7841
8123
  if (settled) return;
7842
8124
  settled = true;
7843
8125
  resolveDecision(d);
7844
8126
  };
7845
- const fallback = setTimeout(() => resolve11("stop"), DECISION_TIMEOUT_MS);
8127
+ const fallback = setTimeout(() => resolve12("stop"), DECISION_TIMEOUT_MS);
8128
+ const sessionId = this.currentSessionId();
7846
8129
  budget._events?.emit("budget.threshold_reached", {
8130
+ ...sessionId ? { sessionId } : {},
7847
8131
  kind: "timeout",
7848
8132
  used,
7849
8133
  limit,
@@ -7858,11 +8142,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7858
8142
  // disagreeing, resolves as a stop). Async grants still resolve.
7859
8143
  extend: (extra) => {
7860
8144
  clearTimeout(fallback);
7861
- queueMicrotask(() => resolve11({ extend: extra }));
8145
+ queueMicrotask(() => resolve12({ extend: extra }));
7862
8146
  },
7863
8147
  deny: () => {
7864
8148
  clearTimeout(fallback);
7865
- resolve11("stop");
8149
+ resolve12("stop");
7866
8150
  }
7867
8151
  });
7868
8152
  });
@@ -7877,7 +8161,9 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7877
8161
  const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
7878
8162
  const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
7879
8163
  if (idleExceeded && !wallExceeded) {
8164
+ const sessionId = this.currentSessionId();
7880
8165
  budget._events?.emit("budget.threshold_reached", {
8166
+ ...sessionId ? { sessionId } : {},
7881
8167
  kind: "idle_timeout",
7882
8168
  used: budget.idleMs(),
7883
8169
  limit: idleLimit ?? 0,
@@ -8168,6 +8454,10 @@ var Director = class _Director {
8168
8454
  const resolved = typeof this.maxContext === "function" ? this.maxContext() : this.maxContext;
8169
8455
  return resolved && resolved > 0 ? resolved : 128e3;
8170
8456
  }
8457
+ currentSessionId() {
8458
+ const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
8459
+ return typeof value === "string" && value.length > 0 ? value : void 0;
8460
+ }
8171
8461
  /** Optional Brain arbiter for director-level policy decisions. */
8172
8462
  brain;
8173
8463
  /**
@@ -8227,6 +8517,7 @@ var Director = class _Director {
8227
8517
  stateCheckpoint;
8228
8518
  /** Optional session writer for emitting task_* / agent_* lifecycle events. */
8229
8519
  sessionWriter;
8520
+ sessionIdSource;
8230
8521
  /** Debounce timer for periodic manifest writes. */
8231
8522
  manifestTimer = null;
8232
8523
  manifestDebounceMs;
@@ -8234,7 +8525,7 @@ var Director = class _Director {
8234
8525
  maxFleetCostUsd;
8235
8526
  /** Max auto-extensions per subagent per budget kind before denying. */
8236
8527
  maxBudgetExtensions;
8237
- /** Sessions root for direct subagent JSONL reads (fleet_session tool). */
8528
+ /** Sessions root for direct subagent JSONL reads (fleet tool, action: session). */
8238
8529
  sessionsRoot;
8239
8530
  /** Director run id for JSONL path resolution. */
8240
8531
  directorRunId;
@@ -8310,6 +8601,7 @@ var Director = class _Director {
8310
8601
  this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
8311
8602
  this.spawnDepth = opts.spawnDepth ?? 0;
8312
8603
  this.sessionWriter = opts.sessionWriter ?? null;
8604
+ this.sessionIdSource = opts.sessionId ?? (() => opts.sessionWriter?.id);
8313
8605
  this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
8314
8606
  this.dispatchClassifier = opts.dispatchClassifier;
8315
8607
  this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
@@ -8356,7 +8648,7 @@ var Director = class _Director {
8356
8648
  }
8357
8649
  this.coordinator = new DefaultMultiAgentCoordinator(
8358
8650
  { ...opts.config, coordinatorId: this.id },
8359
- { runner: opts.runner }
8651
+ { runner: opts.runner, sessionId: () => this.currentSessionId() }
8360
8652
  );
8361
8653
  this.coordinator.setFleetBus(this.fleet);
8362
8654
  this.fleetManager?.setCoordinator(this.coordinator);
@@ -8484,6 +8776,7 @@ var Director = class _Director {
8484
8776
  if (this.brain) {
8485
8777
  void this.brain.decide({
8486
8778
  id: `director-budget-${e.subagentId}-${payload.kind}`,
8779
+ sessionId: this.currentSessionId(),
8487
8780
  source: "director",
8488
8781
  question: `Should the director extend the ${payload.kind} budget for subagent ${e.subagentId}?`,
8489
8782
  context: [
@@ -8933,7 +9226,7 @@ var Director = class _Director {
8933
9226
  })),
8934
9227
  usage: this.usage.snapshot()
8935
9228
  };
8936
- await fsp7.mkdir(path3.dirname(this.manifestPath), { recursive: true });
9229
+ await fsp7.mkdir(path4.dirname(this.manifestPath), { recursive: true });
8937
9230
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
8938
9231
  return this.manifestPath;
8939
9232
  }
@@ -9077,11 +9370,11 @@ var Director = class _Director {
9077
9370
  if (cached) return cached;
9078
9371
  const existing = this.taskWaiters.get(id);
9079
9372
  if (existing) return existing.promise;
9080
- let resolve11;
9373
+ let resolve12;
9081
9374
  const promise = new Promise((res) => {
9082
- resolve11 = res;
9375
+ resolve12 = res;
9083
9376
  });
9084
- this.taskWaiters.set(id, { promise, resolve: resolve11 });
9377
+ this.taskWaiters.set(id, { promise, resolve: resolve12 });
9085
9378
  return promise;
9086
9379
  })
9087
9380
  );
@@ -9164,7 +9457,7 @@ var Director = class _Director {
9164
9457
  */
9165
9458
  async readSession(subagentId, tail) {
9166
9459
  if (!this.sessionsRoot) return null;
9167
- const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
9460
+ const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
9168
9461
  let raw;
9169
9462
  try {
9170
9463
  raw = await fsp7.readFile(filePath, "utf8");
@@ -9280,10 +9573,7 @@ var Director = class _Director {
9280
9573
  makeRollUpTool(this),
9281
9574
  makeTerminateTool(this),
9282
9575
  makeTerminateAllTool(this),
9283
- makeFleetStatusTool(this),
9284
- makeFleetUsageTool(this),
9285
- makeFleetSessionTool(this),
9286
- makeFleetHealthTool(this),
9576
+ makeFleetTool(this),
9287
9577
  makeCollabDebugTool(this),
9288
9578
  makeFleetEmitTool(this),
9289
9579
  makeWorkCompleteTool(this)
@@ -9335,9 +9625,13 @@ var Director = class _Director {
9335
9625
 
9336
9626
  // src/storage/session-store.ts
9337
9627
  init_atomic_write();
9628
+ init_message_invariants();
9629
+ init_utils();
9630
+ init_session_scoped_path();
9338
9631
 
9339
9632
  // src/storage/file-session-writer.ts
9340
9633
  init_atomic_write();
9634
+ init_utils();
9341
9635
 
9342
9636
  // src/storage/session-helpers.ts
9343
9637
  function userInputTitle(content) {
@@ -9354,7 +9648,7 @@ var FileSessionWriter = class _FileSessionWriter {
9354
9648
  this.meta = meta;
9355
9649
  this.events = events;
9356
9650
  this.resumed = opts.resumed ?? false;
9357
- this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
9651
+ this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
9358
9652
  this.filePath = opts.filePath ?? "";
9359
9653
  this.secretScrubber = opts.secretScrubber;
9360
9654
  this.onCloseCb = opts.onClose;
@@ -9723,6 +10017,7 @@ var FileSessionWriter = class _FileSessionWriter {
9723
10017
  promptPreview
9724
10018
  });
9725
10019
  this.events?.emit("checkpoint.written", {
10020
+ sessionId: this.id,
9726
10021
  promptIndex,
9727
10022
  promptPreview,
9728
10023
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -9892,6 +10187,7 @@ var FileSessionWriter = class _FileSessionWriter {
9892
10187
  revertedFiles: []
9893
10188
  });
9894
10189
  this.events?.emit("session.rewound", {
10190
+ sessionId: this.id,
9895
10191
  toPromptIndex: targetPromptIndex,
9896
10192
  revertedFiles: [],
9897
10193
  removedEvents: removedCount
@@ -9931,7 +10227,7 @@ var FileSessionWriter = class _FileSessionWriter {
9931
10227
  ts: (/* @__PURE__ */ new Date()).toISOString(),
9932
10228
  context
9933
10229
  });
9934
- this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
10230
+ this.events?.emit("in_flight.started", { sessionId: this.id, context, ts: (/* @__PURE__ */ new Date()).toISOString() });
9935
10231
  }
9936
10232
  /**
9937
10233
  * Close the in-flight marker. Idempotent in spirit
@@ -9946,18 +10242,16 @@ var FileSessionWriter = class _FileSessionWriter {
9946
10242
  ts: (/* @__PURE__ */ new Date()).toISOString(),
9947
10243
  reason
9948
10244
  });
9949
- this.events?.emit("in_flight.ended", { reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
10245
+ this.events?.emit("in_flight.ended", { sessionId: this.id, reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
9950
10246
  }
9951
10247
  };
9952
- function sanitizeModel(model) {
9953
- return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
9954
- }
9955
- function generateSessionId(startedAt, model) {
10248
+
10249
+ // src/storage/session-id.ts
10250
+ init_ulid();
10251
+ function generateSessionId(startedAt, _model) {
9956
10252
  const date = startedAt.slice(0, 10);
9957
- const time = startedAt.slice(11, 19).replace(/:/g, "-");
9958
- const suffix = randomBytes(2).toString("hex");
9959
- const modelPart = model ? `_${sanitizeModel(model)}` : "";
9960
- return `${date}/${time}Z${modelPart}_${suffix}`;
10253
+ const seedTime = Number.isNaN(Date.parse(startedAt)) ? Date.now() : Date.parse(startedAt);
10254
+ return `${date}/sess_${ulid(seedTime)}`;
9961
10255
  }
9962
10256
 
9963
10257
  // src/storage/session-summary.ts
@@ -10088,17 +10382,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
10088
10382
  }
10089
10383
  /** Absolute path to the session index file. */
10090
10384
  get indexFile() {
10091
- return path3.join(this.dir, "_index.jsonl");
10385
+ return path4.join(this.dir, "_index.jsonl");
10092
10386
  }
10093
10387
  /** Join session ID to its absolute path within the store directory. */
10094
10388
  sessionPath(id, ext) {
10095
- return path3.join(this.dir, `${id}${ext}`);
10389
+ return sessionScopedPath(this.dir, id, ext);
10096
10390
  }
10097
10391
  shardManifestPath(shardKey) {
10098
- return shardKey ? path3.join(this.dir, shardKey, "_manifest.json") : path3.join(this.dir, "_manifest.json");
10392
+ return shardKey ? path4.join(this.dir, shardKey, "_manifest.json") : path4.join(this.dir, "_manifest.json");
10099
10393
  }
10100
10394
  shardKeyForSessionId(id) {
10101
- const dirName = path3.dirname(id);
10395
+ const dirName = path4.dirname(id);
10102
10396
  return dirName === "." ? "" : dirName;
10103
10397
  }
10104
10398
  invalidateShardManifestBySessionId(id) {
@@ -10110,15 +10404,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
10110
10404
  * subdirectory so sessions group naturally by day.
10111
10405
  */
10112
10406
  async ensureShardDir(id) {
10113
- const dirPath = path3.dirname(path3.join(this.dir, id));
10407
+ const dirPath = path4.dirname(sessionScopedPath(this.dir, id, ""));
10114
10408
  await ensureDir(dirPath);
10115
10409
  return dirPath;
10116
10410
  }
10117
10411
  async create(meta) {
10118
10412
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
10119
- const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
10413
+ const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt);
10120
10414
  const shardDir = await this.ensureShardDir(id);
10121
- const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
10415
+ const file = this.sessionPath(id, ".jsonl");
10122
10416
  const t0 = Date.now();
10123
10417
  let handle;
10124
10418
  try {
@@ -10180,7 +10474,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10180
10474
  // Shard directory (sessions/<date>/) — must match create() so the
10181
10475
  // .summary.json sidecar lands next to the JSONL instead of the
10182
10476
  // sessions root (where summaryFor() would never find it).
10183
- dir: path3.dirname(file),
10477
+ dir: path4.dirname(file),
10184
10478
  filePath: file,
10185
10479
  secretScrubber: this.secretScrubber,
10186
10480
  onClose: (s) => this.appendToIndex(s)
@@ -10200,6 +10494,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
10200
10494
  }
10201
10495
  }
10202
10496
  async load(id) {
10497
+ return this.loadInternal(id, { full: true });
10498
+ }
10499
+ /**
10500
+ * Fast-path loader that skips message reconstruction and adjacency repair.
10501
+ *
10502
+ * Use this for callers that only need the raw event stream + session
10503
+ * metadata — e.g. session listers, analytics, audit, and the TUI's
10504
+ * "events only" views. It avoids the message array build and
10505
+ * repairToolUseAdjacency cost on large session files (a long agent
10506
+ * run can have 50k+ events; rebuilding messages is O(events) and
10507
+ * allocates per-block, so skipping it is a meaningful win).
10508
+ *
10509
+ * The returned data.messages is an empty array; data.toolCallEnds
10510
+ * is computed from the raw events. usage is the sum across all
10511
+ * llm_response events — same as full load().
10512
+ */
10513
+ async loadEventsOnly(id) {
10514
+ return this.loadInternal(id, { full: false });
10515
+ }
10516
+ async loadInternal(id, mode) {
10203
10517
  const file = this.sessionPath(id, ".jsonl");
10204
10518
  const t0 = Date.now();
10205
10519
  let outcome = "success";
@@ -10207,93 +10521,113 @@ var DefaultSessionStore = class _DefaultSessionStore {
10207
10521
  let cacheHit = false;
10208
10522
  try {
10209
10523
  const s = await fsp7.stat(file);
10210
- const stat9 = { mtimeMs: s.mtimeMs, size: s.size };
10524
+ const stat10 = { mtimeMs: s.mtimeMs, size: s.size };
10211
10525
  const cached = this._loadCache.get(id);
10212
- if (cached && cached.mtimeMs === stat9.mtimeMs && cached.size === stat9.size) {
10526
+ if (cached && cached.mtimeMs === stat10.mtimeMs && cached.size === stat10.size) {
10213
10527
  cacheHit = true;
10214
10528
  this._loadCache.delete(id);
10215
10529
  this._loadCache.set(id, cached);
10216
- return cached.data;
10530
+ if (mode.full) return cached.data;
10531
+ return { ...cached.data, messages: [] };
10217
10532
  }
10218
- const raw = await fsp7.readFile(file, "utf8");
10219
- const lines = raw.split("\n").filter((l) => l.trim());
10220
10533
  const events = [];
10221
10534
  let sessionStartEvent;
10222
10535
  let sessionEndEvent;
10223
10536
  let sessionModel;
10224
10537
  let sessionProvider;
10225
10538
  let sessionPendingToolUses;
10226
- const messages = [];
10227
- const openToolUses = /* @__PURE__ */ new Set();
10539
+ const messages = mode.full ? [] : void 0;
10540
+ const openToolUses = mode.full ? /* @__PURE__ */ new Set() : void 0;
10228
10541
  let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
10229
- for (const line of lines) {
10230
- try {
10231
- const parsed = JSON.parse(line);
10232
- if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
10233
- const ev = parsed;
10234
- events.push(ev);
10235
- if (ev.type === "session_start" && !sessionStartEvent) {
10236
- sessionStartEvent = ev;
10237
- sessionModel = ev.model;
10238
- sessionProvider = ev.provider;
10239
- }
10240
- if (ev.type === "session_end") {
10241
- sessionEndEvent = ev;
10242
- sessionPendingToolUses = ev.pendingToolUses;
10243
- }
10244
- if (ev.type === "user_input") {
10245
- openToolUses.clear();
10246
- messages.push({ role: "user", content: ev.content, ts: ev.ts });
10247
- } else if (ev.type === "llm_response") {
10248
- messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
10249
- for (const b of ev.content) {
10250
- if (b.type === "tool_use") openToolUses.add(b.id);
10542
+ const stream = createReadStream(file, { encoding: "utf8" });
10543
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
10544
+ try {
10545
+ for await (const line of rl) {
10546
+ if (!line.trim()) continue;
10547
+ try {
10548
+ const parsed = JSON.parse(line);
10549
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
10550
+ const ev = parsed;
10551
+ events.push(ev);
10552
+ if (ev.type === "session_start" && !sessionStartEvent) {
10553
+ sessionStartEvent = ev;
10554
+ sessionModel = ev.model;
10555
+ sessionProvider = ev.provider;
10251
10556
  }
10252
- usage = {
10253
- input: usage.input + (ev.usage.input ?? 0),
10254
- output: usage.output + (ev.usage.output ?? 0),
10255
- cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
10256
- cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
10257
- };
10258
- } else if (ev.type === "tool_result") {
10259
- if (!openToolUses.has(ev.id)) {
10260
- this.events?.emit("session.damaged", {
10261
- sessionId: id,
10262
- detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
10263
- });
10264
- continue;
10557
+ if (ev.type === "session_end") {
10558
+ sessionEndEvent = ev;
10559
+ sessionPendingToolUses = ev.pendingToolUses;
10265
10560
  }
10266
- openToolUses.delete(ev.id);
10267
- const resultBlock = {
10268
- type: "tool_result",
10269
- tool_use_id: ev.id,
10270
- content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
10271
- is_error: ev.isError
10272
- };
10273
- const last = messages[messages.length - 1];
10274
- const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
10275
- if (lastIsToolResultUser && Array.isArray(last.content)) {
10276
- last.content.push(resultBlock);
10277
- } else {
10278
- messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
10561
+ if (mode.full && messages !== void 0 && openToolUses !== void 0) {
10562
+ if (ev.type === "user_input") {
10563
+ openToolUses.clear();
10564
+ messages.push({ role: "user", content: ev.content, ts: ev.ts });
10565
+ } else if (ev.type === "llm_response") {
10566
+ messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
10567
+ for (const b of ev.content) {
10568
+ if (b.type === "tool_use") openToolUses.add(b.id);
10569
+ }
10570
+ usage = {
10571
+ input: usage.input + (ev.usage.input ?? 0),
10572
+ output: usage.output + (ev.usage.output ?? 0),
10573
+ cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
10574
+ cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
10575
+ };
10576
+ } else if (ev.type === "tool_result") {
10577
+ if (!openToolUses.has(ev.id)) {
10578
+ this.events?.emit("session.damaged", {
10579
+ sessionId: id,
10580
+ detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
10581
+ });
10582
+ continue;
10583
+ }
10584
+ openToolUses.delete(ev.id);
10585
+ const resultBlock = {
10586
+ type: "tool_result",
10587
+ tool_use_id: ev.id,
10588
+ content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
10589
+ is_error: ev.isError
10590
+ };
10591
+ const last = messages[messages.length - 1];
10592
+ const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
10593
+ if (lastIsToolResultUser && Array.isArray(last.content)) {
10594
+ last.content.push(resultBlock);
10595
+ } else {
10596
+ messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
10597
+ }
10598
+ }
10599
+ } else if (ev.type === "llm_response") {
10600
+ usage = {
10601
+ input: usage.input + (ev.usage.input ?? 0),
10602
+ output: usage.output + (ev.usage.output ?? 0),
10603
+ cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
10604
+ cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
10605
+ };
10279
10606
  }
10280
10607
  }
10608
+ } catch {
10281
10609
  }
10282
- } catch {
10283
10610
  }
10611
+ } finally {
10612
+ rl.close();
10613
+ stream.close();
10284
10614
  }
10285
- if (openToolUses.size > 0) {
10286
- this.events?.emit("session.damaged", {
10287
- sessionId: id,
10288
- detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
10289
- });
10290
- }
10291
- const repaired = repairToolUseAdjacency(messages);
10292
- if (repaired.report.changed) {
10293
- this.events?.emit("session.damaged", {
10294
- sessionId: id,
10295
- detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
10296
- });
10615
+ let finalMessages = [];
10616
+ if (mode.full && messages !== void 0 && openToolUses !== void 0) {
10617
+ if (openToolUses.size > 0) {
10618
+ this.events?.emit("session.damaged", {
10619
+ sessionId: id,
10620
+ detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
10621
+ });
10622
+ }
10623
+ const repaired = repairToolUseAdjacency(messages);
10624
+ if (repaired.report.changed) {
10625
+ this.events?.emit("session.damaged", {
10626
+ sessionId: id,
10627
+ detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
10628
+ });
10629
+ }
10630
+ finalMessages = repaired.messages;
10297
10631
  }
10298
10632
  const meta = {
10299
10633
  id,
@@ -10304,27 +10638,29 @@ var DefaultSessionStore = class _DefaultSessionStore {
10304
10638
  pendingToolUses: sessionPendingToolUses
10305
10639
  };
10306
10640
  const toolCallEnds = extractToolCallEnds(events);
10307
- const data = { metadata: meta, events, messages: repaired.messages, usage, toolCallEnds };
10308
- if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
10309
- const oldest = this._loadCache.keys().next().value;
10310
- if (oldest !== void 0) {
10311
- this._loadCache.delete(oldest);
10641
+ const data = { metadata: meta, events, messages: finalMessages, usage, toolCallEnds };
10642
+ if (mode.full) {
10643
+ if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
10644
+ const oldest = this._loadCache.keys().next().value;
10645
+ if (oldest !== void 0) {
10646
+ this._loadCache.delete(oldest);
10647
+ }
10312
10648
  }
10649
+ this._loadCache.set(id, { mtimeMs: stat10.mtimeMs, size: stat10.size, data });
10313
10650
  }
10314
- this._loadCache.set(id, { mtimeMs: stat9.mtimeMs, size: stat9.size, data });
10315
10651
  return data;
10316
10652
  } catch (err) {
10317
10653
  outcome = "failure";
10318
10654
  errorMsg = toErrorMessage(err);
10319
10655
  throw err;
10320
10656
  } finally {
10321
- this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
10657
+ this.emitRead(id, file, mode.full ? "load" : "load_events_only", outcome, Date.now() - t0, errorMsg);
10322
10658
  if (cacheHit) {
10323
10659
  this.events?.emit("storage.cache_hit", {
10324
10660
  sessionId: id,
10325
10661
  store: "session",
10326
10662
  filePath: file,
10327
- operation: "load",
10663
+ operation: mode.full ? "load" : "load_events_only",
10328
10664
  durationMs: Date.now() - t0
10329
10665
  });
10330
10666
  }
@@ -10352,14 +10688,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
10352
10688
  const limit = opts?.limit;
10353
10689
  const signal = opts?.signal;
10354
10690
  const out = [];
10355
- let stat9;
10691
+ let stat10;
10356
10692
  try {
10357
- stat9 = await fsp7.stat(file);
10693
+ stat10 = await fsp7.stat(file);
10358
10694
  } catch (err) {
10359
10695
  if (err.code === "ENOENT") return [];
10360
10696
  throw err;
10361
10697
  }
10362
- if (stat9.size === 0) return [];
10698
+ if (stat10.size === 0) return [];
10363
10699
  let fh;
10364
10700
  try {
10365
10701
  fh = await fsp7.open(file, "r");
@@ -10472,14 +10808,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
10472
10808
  async appendToIndex(summary) {
10473
10809
  try {
10474
10810
  await ensureDir(this.dir);
10475
- const line = JSON.stringify(summary) + "\n";
10476
- await fsp7.appendFile(this.indexFile, line, "utf8");
10477
- this._indexCache = null;
10478
- this.invalidateShardManifestBySessionId(summary.id);
10479
- this.indexAppendCount++;
10480
- if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
10481
- await this.compactIndex();
10482
- this.indexAppendCount = 0;
10811
+ let shouldCompact = false;
10812
+ await withFileLock(this.indexFile, async () => {
10813
+ const line = JSON.stringify(summary) + "\n";
10814
+ await fsp7.appendFile(this.indexFile, line, "utf8");
10815
+ this._indexCache = null;
10816
+ this.invalidateShardManifestBySessionId(summary.id);
10817
+ this.indexAppendCount++;
10818
+ if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
10819
+ shouldCompact = true;
10820
+ this.indexAppendCount = 0;
10821
+ }
10822
+ });
10823
+ if (shouldCompact) {
10824
+ await withFileLock(this.indexFile, () => this.compactIndexInner());
10483
10825
  }
10484
10826
  } catch {
10485
10827
  }
@@ -10488,30 +10830,28 @@ var DefaultSessionStore = class _DefaultSessionStore {
10488
10830
  async writeTombstone(id) {
10489
10831
  try {
10490
10832
  await ensureDir(this.dir);
10491
- const line = JSON.stringify({ action: "delete", id }) + "\n";
10492
- await fsp7.appendFile(this.indexFile, line, "utf8");
10493
- this._indexCache = null;
10494
- this.invalidateShardManifestBySessionId(id);
10495
- this.indexAppendCount++;
10833
+ await withFileLock(this.indexFile, async () => {
10834
+ const line = JSON.stringify({ action: "delete", id }) + "\n";
10835
+ await fsp7.appendFile(this.indexFile, line, "utf8");
10836
+ this._indexCache = null;
10837
+ this.invalidateShardManifestBySessionId(id);
10838
+ this.indexAppendCount++;
10839
+ });
10496
10840
  } catch {
10497
10841
  }
10498
10842
  }
10499
10843
  /**
10500
10844
  * Compact the index: read all entries, drop tombstones, deduplicate
10501
- * (keep latest per session), and rewrite. Atomic via temp+rename.
10845
+ * (keep latest per session), and rewrite atomically. Acquires the index
10846
+ * file lock so a concurrent append (this process or another wstack in the
10847
+ * same project) can't be overwritten by the rewrite.
10502
10848
  */
10503
10849
  async compactIndex() {
10504
10850
  const t0 = Date.now();
10505
10851
  let outcome = "success";
10506
10852
  let errorMsg;
10507
10853
  try {
10508
- const entries = await this.readIndex();
10509
- if (entries.length === 0) return;
10510
- const tmp = `${this.indexFile}.compact.tmp`;
10511
- const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
10512
- await fsp7.writeFile(tmp, lines, "utf8");
10513
- await fsp7.rename(tmp, this.indexFile);
10514
- this._indexCache = null;
10854
+ await withFileLock(this.indexFile, () => this.compactIndexInner());
10515
10855
  } catch (err) {
10516
10856
  outcome = "failure";
10517
10857
  errorMsg = toErrorMessage(err);
@@ -10519,21 +10859,34 @@ var DefaultSessionStore = class _DefaultSessionStore {
10519
10859
  this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
10520
10860
  }
10521
10861
  }
10862
+ /**
10863
+ * Lock-free compaction body. The caller MUST already hold the index file
10864
+ * lock (via withFileLock(this.indexFile, ...)). Uses atomicWrite for the
10865
+ * rewrite so the temp file gets a random suffix (no collision between two
10866
+ * compactions) and the Windows transient-EPERM rename retry.
10867
+ */
10868
+ async compactIndexInner() {
10869
+ const entries = await this.readIndex();
10870
+ if (entries.length === 0) return;
10871
+ const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
10872
+ await atomicWrite(this.indexFile, lines, { mode: 384 });
10873
+ this._indexCache = null;
10874
+ }
10522
10875
  /**
10523
10876
  * Read the index file and return deduplicated session summaries.
10524
10877
  * Entries with a matching tombstone are filtered out.
10525
10878
  * Returns empty array when the index doesn't exist or is corrupt.
10526
10879
  */
10527
10880
  async readIndex() {
10528
- let stat9;
10881
+ let stat10;
10529
10882
  try {
10530
10883
  const s = await fsp7.stat(this.indexFile);
10531
- stat9 = { mtimeMs: s.mtimeMs, size: s.size };
10884
+ stat10 = { mtimeMs: s.mtimeMs, size: s.size };
10532
10885
  } catch {
10533
10886
  this._indexCache = null;
10534
10887
  return [];
10535
10888
  }
10536
- if (this._indexCache !== null && this._indexCache.mtimeMs === stat9.mtimeMs && this._indexCache.size === stat9.size) {
10889
+ if (this._indexCache !== null && this._indexCache.mtimeMs === stat10.mtimeMs && this._indexCache.size === stat10.size) {
10537
10890
  return [...this._indexCache.summaries];
10538
10891
  }
10539
10892
  let raw;
@@ -10566,7 +10919,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10566
10919
  if (a.startedAt > b.startedAt) return -1;
10567
10920
  return a.id.localeCompare(b.id);
10568
10921
  });
10569
- this._indexCache = { ...stat9, summaries };
10922
+ this._indexCache = { ...stat10, summaries };
10570
10923
  return [...summaries];
10571
10924
  }
10572
10925
  /**
@@ -10577,11 +10930,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
10577
10930
  const ids = await this.collectSessionIds(this.dir);
10578
10931
  const summaries = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
10579
10932
  const valid = summaries.filter((s) => s !== null);
10580
- const tmp = `${this.indexFile}.tmp`;
10581
10933
  const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
10582
- await fsp7.writeFile(tmp, lines, "utf8");
10583
- await fsp7.rename(tmp, this.indexFile);
10584
- this._indexCache = null;
10934
+ await withFileLock(this.indexFile, async () => {
10935
+ await atomicWrite(this.indexFile, lines, { mode: 384 });
10936
+ this._indexCache = null;
10937
+ });
10585
10938
  return valid.length;
10586
10939
  }
10587
10940
  async listFromDirectoryScan(limit) {
@@ -10657,7 +11010,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10657
11010
  return entry;
10658
11011
  }
10659
11012
  async collectSessionFilesInShard(shardKey) {
10660
- const dir = shardKey ? path3.join(this.dir, shardKey) : this.dir;
11013
+ const dir = shardKey ? path4.join(this.dir, shardKey) : this.dir;
10661
11014
  const entries = await this.collectSessionFiles(dir, shardKey);
10662
11015
  return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
10663
11016
  }
@@ -10680,13 +11033,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
10680
11033
  if (entry.name === "_index.jsonl") continue;
10681
11034
  const base = entry.name.replace(/\.jsonl$/, "");
10682
11035
  const id = prefix ? `${prefix}/${base}` : base;
10683
- files.push({ id, filePath: path3.join(dir, entry.name) });
11036
+ files.push({ id, filePath: path4.join(dir, entry.name) });
10684
11037
  }
10685
11038
  }
10686
11039
  const childFileArrays = await Promise.all(
10687
11040
  dirEntries.map((entry) => {
10688
11041
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
10689
- return this.collectSessionFiles(path3.join(dir, entry.name), childPrefix, depth + 1);
11042
+ return this.collectSessionFiles(path4.join(dir, entry.name), childPrefix, depth + 1);
10690
11043
  })
10691
11044
  );
10692
11045
  return [...childFileArrays.flat(), ...files];
@@ -10719,7 +11072,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10719
11072
  const childIdArrays = await Promise.all(
10720
11073
  dirEntries.map((entry) => {
10721
11074
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
10722
- return this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1);
11075
+ return this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1);
10723
11076
  })
10724
11077
  );
10725
11078
  return [...childIdArrays.flat(), ...fileIds];
@@ -10733,8 +11086,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
10733
11086
  if (fromManifest) return fromManifest;
10734
11087
  try {
10735
11088
  const full = this.sessionPath(id, ".jsonl");
10736
- const stat9 = await fsp7.stat(full);
10737
- const summary = await this.summarize(id, stat9.mtime.toISOString());
11089
+ const stat10 = await fsp7.stat(full);
11090
+ const summary = await this.summarize(id, stat10.mtime.toISOString());
10738
11091
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
10739
11092
  const msg = toErrorMessage(err);
10740
11093
  this.emitError(id, manifest, "summary_fallback", msg, true);
@@ -10777,18 +11130,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
10777
11130
  async summaryHeaderFor(ref) {
10778
11131
  let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
10779
11132
  try {
10780
- const stat9 = await fsp7.stat(ref.filePath);
10781
- if (!stat9.isFile()) {
11133
+ const stat10 = await fsp7.stat(ref.filePath);
11134
+ if (!stat10.isFile()) {
10782
11135
  return {
10783
11136
  id: ref.id,
10784
11137
  title: "(damaged)",
10785
- startedAt: stat9.mtime.toISOString(),
11138
+ startedAt: stat10.mtime.toISOString(),
10786
11139
  model: "unknown",
10787
11140
  provider: "unknown",
10788
11141
  tokenTotal: 0
10789
11142
  };
10790
11143
  }
10791
- mtime = stat9.mtime.toISOString();
11144
+ mtime = stat10.mtime.toISOString();
10792
11145
  } catch {
10793
11146
  return null;
10794
11147
  }
@@ -10836,14 +11189,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
10836
11189
  async deleteSession(id) {
10837
11190
  const jsonlPath = this.sessionPath(id, ".jsonl");
10838
11191
  const summaryPath = this.sessionPath(id, ".summary.json");
10839
- const shardDir = path3.dirname(path3.join(this.dir, id));
10840
- const base = path3.basename(id);
10841
- const sessDir = path3.join(shardDir, base);
11192
+ const shardDir = path4.dirname(jsonlPath);
11193
+ const base = path4.basename(id);
11194
+ const sessDir = path4.join(shardDir, base);
10842
11195
  const deletions = [
10843
11196
  fsp7.unlink(jsonlPath),
10844
11197
  fsp7.unlink(summaryPath),
10845
- fsp7.unlink(path3.join(shardDir, `${base}.plan.json`)),
10846
- fsp7.unlink(path3.join(shardDir, `${base}.todos.json`))
11198
+ fsp7.unlink(sessionScopedPath(this.dir, id, ".plan.json")),
11199
+ fsp7.unlink(sessionScopedPath(this.dir, id, ".tasks.json")),
11200
+ fsp7.unlink(sessionScopedPath(this.dir, id, ".todos.json"))
10847
11201
  ];
10848
11202
  const results = await Promise.allSettled(deletions);
10849
11203
  for (const r of results) {
@@ -10879,17 +11233,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
10879
11233
  let deleted = 0;
10880
11234
  let activeSessionId = null;
10881
11235
  try {
10882
- const raw = await fsp7.readFile(path3.join(this.dir, "active.json"), "utf8");
11236
+ const raw = await fsp7.readFile(path4.join(this.dir, "active.json"), "utf8");
10883
11237
  const active = JSON.parse(raw);
10884
11238
  activeSessionId = active.sessionId ?? null;
10885
11239
  } catch {
10886
11240
  }
10887
11241
  const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
10888
11242
  const pruneFile = async (dir, name, prefix) => {
10889
- const jsonlPath = path3.join(dir, name);
11243
+ const jsonlPath = path4.join(dir, name);
10890
11244
  try {
10891
- const stat9 = await fsp7.stat(jsonlPath);
10892
- if (stat9.mtimeMs >= cutoff) return;
11245
+ const stat10 = await fsp7.stat(jsonlPath);
11246
+ if (stat10.mtimeMs >= cutoff) return;
10893
11247
  } catch {
10894
11248
  return;
10895
11249
  }
@@ -10906,7 +11260,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10906
11260
  continue;
10907
11261
  }
10908
11262
  if (!entry.isDirectory()) continue;
10909
- const dateDir = path3.join(this.dir, entry.name);
11263
+ const dateDir = path4.join(this.dir, entry.name);
10910
11264
  const files = await fsp7.readdir(dateDir, { withFileTypes: true }).catch(() => []);
10911
11265
  for (const file of files) {
10912
11266
  if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
@@ -10918,7 +11272,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
10918
11272
  }
10919
11273
  for (const entry of entries) {
10920
11274
  if (!entry.isDirectory()) continue;
10921
- const dateDir = path3.join(this.dir, entry.name);
11275
+ const dateDir = path4.join(this.dir, entry.name);
10922
11276
  try {
10923
11277
  const remaining = await fsp7.readdir(dateDir);
10924
11278
  if (remaining.length === 0) {
@@ -11049,9 +11403,9 @@ function makeDirectorSessionFactory(opts) {
11049
11403
  let dir;
11050
11404
  if (opts.store) {
11051
11405
  store = opts.store;
11052
- dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
11406
+ dir = opts.sessionsRoot ? path4.join(opts.sessionsRoot, runId) : "(caller-managed)";
11053
11407
  } else if (opts.sessionsRoot) {
11054
- dir = path3.join(opts.sessionsRoot, runId);
11408
+ dir = path4.join(opts.sessionsRoot, runId);
11055
11409
  store = new DefaultSessionStore({ dir });
11056
11410
  } else {
11057
11411
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -11078,6 +11432,9 @@ function makeDirectorSessionFactory(opts) {
11078
11432
  var NULL_FLEET_BUS = new FleetBus();
11079
11433
 
11080
11434
  // src/execution/auto-compaction-middleware.ts
11435
+ init_errors();
11436
+ init_token_estimate();
11437
+ init_context_evidence();
11081
11438
  var LEVEL_RANK = { warn: 0, soft: 1, hard: 2 };
11082
11439
  var MAX_DIGEST_LOG_CHARS = 4e3;
11083
11440
  function truncateDigest(digest) {
@@ -11274,6 +11631,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
11274
11631
  const report = await this.compactor.compact(ctx, { aggressive });
11275
11632
  this.recordAttempt(pressure.level, pressure.tokens, report);
11276
11633
  this.events?.emit("compaction.fired", {
11634
+ sessionId: ctx.session.id,
11277
11635
  level: pressure.level,
11278
11636
  tokens: pressure.tokens,
11279
11637
  load: pressure.load,
@@ -11308,6 +11666,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
11308
11666
  `Auto-compaction left context above the hard threshold after ${pressure.level} compaction`
11309
11667
  );
11310
11668
  this.events?.emit("compaction.failed", {
11669
+ sessionId: ctx.session.id,
11311
11670
  err: error,
11312
11671
  aggressive,
11313
11672
  level: pressure.level,
@@ -11335,6 +11694,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
11335
11694
  const error = err instanceof Error ? err : new Error(String(err));
11336
11695
  const fatal = this.failureMode === "throw" || this.failureMode === "throw_on_hard" && pressure.level === "hard";
11337
11696
  this.events?.emit("compaction.failed", {
11697
+ sessionId: ctx.session.id,
11338
11698
  err: error,
11339
11699
  aggressive,
11340
11700
  level: pressure.level,
@@ -11395,6 +11755,9 @@ function adaptThresholdsForSignals(thresholds, signals) {
11395
11755
  }
11396
11756
 
11397
11757
  // src/execution/autonomous-runner.ts
11758
+ init_errors();
11759
+ init_assert_never();
11760
+ init_regex_guard();
11398
11761
  var DoneConditionChecker = class {
11399
11762
  constructor(condition) {
11400
11763
  this.condition = condition;
@@ -11573,6 +11936,9 @@ var AutonomousRunner = class {
11573
11936
 
11574
11937
  // src/storage/goal-store.ts
11575
11938
  init_atomic_write();
11939
+ init_error();
11940
+ init_wstack_paths();
11941
+ init_errors();
11576
11942
  var MAX_JOURNAL_ENTRIES = 500;
11577
11943
  function goalFilePath(projectRoot) {
11578
11944
  return resolveWstackPaths({ projectRoot }).projectGoal;
@@ -11769,12 +12135,30 @@ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first ite
11769
12135
  };
11770
12136
  }
11771
12137
 
12138
+ // src/execution/compactor.ts
12139
+ init_token_estimate();
12140
+ init_message_invariants();
12141
+ init_context_evidence();
12142
+
11772
12143
  // src/types/blocks.ts
11773
12144
  function isTextBlock(b) {
11774
12145
  return b.type === "text";
11775
12146
  }
11776
12147
 
11777
12148
  // src/execution/compaction-core.ts
12149
+ init_token_estimate();
12150
+ var FAILURE_PATTERN = /(error|fail|exception|timeout|enonet|eacces|eperm|enoent|abort)/i;
12151
+ var CORRECTION_PATTERN = /\b(wrong|no\b|stop\b|don'?t\b|actually|fix that|undo|revert|forget|ignore|skip)\b/i;
12152
+ var ERROR_LANG_PATTERN = /\b(error|exception|fatal|critical|crash|panic|abort|segfault|core dump|undefined is not|null pointer|typeerror|referenceerror|syntaxerror)\b/i;
12153
+ var SECURITY_PATTERN = /\b(security|vulnerability|injection|xss|csrf|secret|apikey|api.key|hardcoded|leak|exploit|cve)\b/i;
12154
+ var ARCHITECTURE_PATTERN = /\b(architecture|design|approach|strategy|pattern|refactor|migrate|restructure|decision|trade.?off)\b/i;
12155
+ var BOILERPLATE_PATTERN = /\b(files_with_matches|count|found \d+ match|directory tree|\.\.\. and \d+ more)\b/i;
12156
+ var PATH_HINT_PATTERN = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
12157
+ var PATH_BACKSLASH_PATTERN = /\\/g;
12158
+ var PATH_TRIM_PATTERN = /^["'`]+|["'`),;:]+$/g;
12159
+ var ERROR_LINE_PATTERN = /\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i;
12160
+ var NEWLINE_SPLIT_PATTERN = /\r?\n/;
12161
+ var WHITESPACE_COLLAPSE_PATTERN = /\s+/g;
11778
12162
  function compactionDebugEnabled() {
11779
12163
  return process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1";
11780
12164
  }
@@ -12010,9 +12394,8 @@ function summarizeToolResultElision(block, tokens) {
12010
12394
  function extractPathHints(content) {
12011
12395
  const text = typeof content === "string" ? content : JSON.stringify(content);
12012
12396
  const out = /* @__PURE__ */ new Set();
12013
- const re = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
12014
- for (const match of text.matchAll(re)) {
12015
- const clean = match[0]?.replace(/\\/g, "/").replace(/^["'`]+|["'`),;:]+$/g, "");
12397
+ for (const match of text.matchAll(PATH_HINT_PATTERN)) {
12398
+ const clean = match[0]?.replace(PATH_BACKSLASH_PATTERN, "/").replace(PATH_TRIM_PATTERN, "");
12016
12399
  if (clean && clean.length <= 220) out.add(clean);
12017
12400
  if (out.size >= 5) break;
12018
12401
  }
@@ -12020,12 +12403,9 @@ function extractPathHints(content) {
12020
12403
  }
12021
12404
  function firstErrorLine(content) {
12022
12405
  const text = typeof content === "string" ? content : JSON.stringify(content);
12023
- for (const line of text.split(/\r?\n/)) {
12024
- if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i.test(
12025
- line
12026
- ))
12027
- continue;
12028
- const trimmed = line.replace(/\s+/g, " ").trim();
12406
+ for (const line of text.split(NEWLINE_SPLIT_PATTERN)) {
12407
+ if (!ERROR_LINE_PATTERN.test(line)) continue;
12408
+ const trimmed = line.replace(WHITESPACE_COLLAPSE_PATTERN, " ").trim();
12029
12409
  if (trimmed) return trimmed.slice(0, 180);
12030
12410
  }
12031
12411
  return void 0;
@@ -12072,9 +12452,9 @@ function scoreMessage(m, context) {
12072
12452
  if (hasToolUse2(m) || hasResult) return 0;
12073
12453
  }
12074
12454
  if (context?.failureCounts && m.role === "user" && hasToolUse2(m) === false) {
12075
- const isFailure = /error|fail|exception|timeout|enonet|eacces|eperm|enoent|abort/i.test(text);
12076
- if (isFailure) {
12077
- const errKey = /(error|fail|exception|timeout|enonet|eacces|eperm|enoent|abort)/i.exec(text)?.[0]?.toLowerCase() ?? "error";
12455
+ const failureMatch = FAILURE_PATTERN.exec(text);
12456
+ if (failureMatch) {
12457
+ const errKey = failureMatch[0]?.toLowerCase() ?? "error";
12078
12458
  const count = (context.failureCounts.get(errKey) ?? 0) + 1;
12079
12459
  context.failureCounts.set(errKey, count);
12080
12460
  if (count >= 5) return 0;
@@ -12082,29 +12462,21 @@ function scoreMessage(m, context) {
12082
12462
  }
12083
12463
  }
12084
12464
  if (m.role === "user") {
12085
- if (/\b(wrong|no\b|stop\b|don'?t\b|actually|fix that|undo|revert|forget|ignore|skip)\b/i.test(
12086
- text
12087
- )) {
12465
+ if (CORRECTION_PATTERN.test(text)) {
12088
12466
  return 5;
12089
12467
  }
12090
12468
  }
12091
- if (/\b(error|exception|fatal|critical|crash|panic|abort|segfault|core dump|undefined is not|null pointer|typeerror|referenceerror|syntaxerror)\b/i.test(
12092
- text
12093
- )) {
12469
+ if (ERROR_LANG_PATTERN.test(text)) {
12094
12470
  return 5;
12095
12471
  }
12096
- if (/\b(security|vulnerability|injection|xss|csrf|secret|apikey|api.key|hardcoded|leak|exploit|cve)\b/i.test(
12097
- text
12098
- )) {
12472
+ if (SECURITY_PATTERN.test(text)) {
12099
12473
  return 5;
12100
12474
  }
12101
- if (m.role === "assistant" && /\b(architecture|design|approach|strategy|pattern|refactor|migrate|restructure|decision|trade.?off)\b/i.test(
12102
- text
12103
- )) {
12475
+ if (m.role === "assistant" && ARCHITECTURE_PATTERN.test(text)) {
12104
12476
  return 5;
12105
12477
  }
12106
12478
  if (hasLargeToolResult(m)) return 1;
12107
- if (m.role === "user" && !hasToolUse2(m) && /\b(files_with_matches|count|found \d+ match|directory tree|\.\.\. and \d+ more)\b/i.test(text)) {
12479
+ if (m.role === "user" && !hasToolUse2(m) && BOILERPLATE_PATTERN.test(text)) {
12108
12480
  return 1;
12109
12481
  }
12110
12482
  return 3;
@@ -12114,6 +12486,11 @@ function buildSmartDigest(messages) {
12114
12486
  const failureCounts = /* @__PURE__ */ new Map();
12115
12487
  let noiseCount = 0;
12116
12488
  for (const m of messages) {
12489
+ const isPureToolIO = Array.isArray(m.content) && m.content.length > 0 && !hasToolUse2(m) && m.content.every((b) => b.type === "tool_result");
12490
+ if (isPureToolIO) {
12491
+ noiseCount++;
12492
+ continue;
12493
+ }
12117
12494
  const score = scoreMessage(m, { failureCounts });
12118
12495
  const text = extractText(m);
12119
12496
  const toolCount = countToolBlocks(m);
@@ -12370,6 +12747,7 @@ var DESIGN_STACKS = ["web", "react-native", "flutter", "swiftui", "compose"];
12370
12747
  function isDesignStack(v) {
12371
12748
  return DESIGN_STACKS.includes(v);
12372
12749
  }
12750
+ init_wstack_paths();
12373
12751
  var KIT_FILE = "KIT.md";
12374
12752
  var TOKENS_FILE = "tokens.json";
12375
12753
  var FOUNDATIONS_ID = "_foundations";
@@ -12482,7 +12860,7 @@ var DefaultDesignKitLoader = class {
12482
12860
  }
12483
12861
  for (const e of entries) {
12484
12862
  if (!e.isDirectory()) continue;
12485
- const kitFile = path3.join(dir, e.name, KIT_FILE);
12863
+ const kitFile = path4.join(dir, e.name, KIT_FILE);
12486
12864
  try {
12487
12865
  const raw = await fsp7.readFile(kitFile, "utf8");
12488
12866
  const fm = parseKitFrontmatter(raw);
@@ -12556,7 +12934,7 @@ var DefaultDesignKitLoader = class {
12556
12934
  const m = await this.find(id);
12557
12935
  let tokens;
12558
12936
  if (m) {
12559
- const tokensPath = path3.join(path3.dirname(m.path), TOKENS_FILE);
12937
+ const tokensPath = path4.join(path4.dirname(m.path), TOKENS_FILE);
12560
12938
  try {
12561
12939
  const raw = await fsp7.readFile(tokensPath, "utf8");
12562
12940
  const parsed = JSON.parse(raw);
@@ -12583,12 +12961,12 @@ var DefaultDesignKitLoader = class {
12583
12961
  };
12584
12962
  function resolveBundledDesignKitsDir() {
12585
12963
  try {
12586
- const here = path3.dirname(fileURLToPath(import.meta.url));
12964
+ const here = path4.dirname(fileURLToPath(import.meta.url));
12587
12965
  const candidates = [
12588
- path3.join(here, "design-kits"),
12589
- path3.join(here, "..", "design-kits"),
12590
- path3.join(here, "..", "..", "design-kits"),
12591
- path3.join(here, "..", "..", "..", "design-kits")
12966
+ path4.join(here, "design-kits"),
12967
+ path4.join(here, "..", "design-kits"),
12968
+ path4.join(here, "..", "..", "design-kits"),
12969
+ path4.join(here, "..", "..", "..", "design-kits")
12592
12970
  ];
12593
12971
  for (const c of candidates) {
12594
12972
  if (existsSync(c)) return c;
@@ -12615,7 +12993,7 @@ function _resetDesignKitLoaderMemo() {
12615
12993
  }
12616
12994
  var DESIGN_DIR = ".design";
12617
12995
  function designProjectDir(projectRoot) {
12618
- return path3.join(projectRoot, DESIGN_DIR);
12996
+ return path4.join(projectRoot, DESIGN_DIR);
12619
12997
  }
12620
12998
  var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
12621
12999
  var rulesCache = /* @__PURE__ */ new Map();
@@ -12624,7 +13002,7 @@ async function loadProjectDesignRules(projectRoot) {
12624
13002
  let rules;
12625
13003
  for (const name of RULE_FILES) {
12626
13004
  try {
12627
- const txt = await fsp7.readFile(path3.join(designProjectDir(projectRoot), name), "utf8");
13005
+ const txt = await fsp7.readFile(path4.join(designProjectDir(projectRoot), name), "utf8");
12628
13006
  if (txt.trim()) {
12629
13007
  rules = txt.trim();
12630
13008
  break;
@@ -12645,7 +13023,7 @@ function parseOverrides(value) {
12645
13023
  }
12646
13024
  async function loadActiveKit(projectRoot) {
12647
13025
  try {
12648
- const raw = await fsp7.readFile(path3.join(designProjectDir(projectRoot), "active.json"), "utf8");
13026
+ const raw = await fsp7.readFile(path4.join(designProjectDir(projectRoot), "active.json"), "utf8");
12649
13027
  const parsed = JSON.parse(raw);
12650
13028
  if (parsed && typeof parsed.kit === "string") {
12651
13029
  return {
@@ -12679,7 +13057,7 @@ function applyTokenOverrides(tokens, overrides) {
12679
13057
  async function ensureDesignDir(projectRoot) {
12680
13058
  const dir = designProjectDir(projectRoot);
12681
13059
  await fsp7.mkdir(dir, { recursive: true });
12682
- const gi = path3.join(dir, ".gitignore");
13060
+ const gi = path4.join(dir, ".gitignore");
12683
13061
  if (!existsSync(gi)) {
12684
13062
  try {
12685
13063
  await fsp7.writeFile(gi, "*\n");
@@ -12693,11 +13071,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
12693
13071
  const dir = await ensureDesignDir(projectRoot);
12694
13072
  const record = { kit, stack: stack ?? null };
12695
13073
  if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
12696
- await fsp7.writeFile(path3.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
13074
+ await fsp7.writeFile(path4.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
12697
13075
  `);
12698
13076
  const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
12699
13077
  `;
12700
- await fsp7.appendFile(path3.join(dir, "decisions.md"), line);
13078
+ await fsp7.appendFile(path4.join(dir, "decisions.md"), line);
12701
13079
  } catch {
12702
13080
  }
12703
13081
  }
@@ -12713,11 +13091,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
12713
13091
  const dir = await ensureDesignDir(projectRoot);
12714
13092
  const record = { kit: active.kit, stack: active.stack ?? null };
12715
13093
  if (Object.keys(merged).length > 0) record.overrides = merged;
12716
- await fsp7.writeFile(path3.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
13094
+ await fsp7.writeFile(path4.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
12717
13095
  `);
12718
13096
  const keys = Object.keys(patch).join(",");
12719
13097
  await fsp7.appendFile(
12720
- path3.join(dir, "decisions.md"),
13098
+ path4.join(dir, "decisions.md"),
12721
13099
  `- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
12722
13100
  `
12723
13101
  );
@@ -12727,7 +13105,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
12727
13105
  }
12728
13106
  async function clearPersistedActiveKit(projectRoot) {
12729
13107
  try {
12730
- await fsp7.rm(path3.join(designProjectDir(projectRoot), "active.json"), { force: true });
13108
+ await fsp7.rm(path4.join(designProjectDir(projectRoot), "active.json"), { force: true });
12731
13109
  } catch {
12732
13110
  }
12733
13111
  }
@@ -12900,14 +13278,14 @@ var SKIP_DIR = /* @__PURE__ */ new Set([
12900
13278
  ".design"
12901
13279
  ]);
12902
13280
  async function walkUiFiles(root, max = 200) {
12903
- const { default: fs15 } = await import('fs/promises');
13281
+ const { default: fs16 } = await import('fs/promises');
12904
13282
  const { default: nodePath } = await import('path');
12905
13283
  const found = [];
12906
13284
  async function rec(dir, depth) {
12907
13285
  if (found.length >= max || depth > 8) return;
12908
13286
  let entries;
12909
13287
  try {
12910
- entries = await fs15.readdir(dir, { withFileTypes: true });
13288
+ entries = await fs16.readdir(dir, { withFileTypes: true });
12911
13289
  } catch {
12912
13290
  return;
12913
13291
  }
@@ -12925,13 +13303,13 @@ async function walkUiFiles(root, max = 200) {
12925
13303
  return found;
12926
13304
  }
12927
13305
  async function runDesignVerify(projectRoot, tokens, explicitFiles) {
12928
- const { default: fs15 } = await import('fs/promises');
13306
+ const { default: fs16 } = await import('fs/promises');
12929
13307
  const { default: nodePath } = await import('path');
12930
13308
  const abs = explicitFiles && explicitFiles.length > 0 ? explicitFiles.map((f) => nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)) : await walkUiFiles(projectRoot);
12931
13309
  const files = [];
12932
13310
  for (const a of abs) {
12933
13311
  try {
12934
- files.push({ path: nodePath.relative(projectRoot, a), text: await fs15.readFile(a, "utf8") });
13312
+ files.push({ path: nodePath.relative(projectRoot, a), text: await fs16.readFile(a, "utf8") });
12935
13313
  } catch {
12936
13314
  }
12937
13315
  }
@@ -13062,10 +13440,10 @@ function makeDesignVerifyToolCallMiddleware() {
13062
13440
  const p = typeof input?.path === "string" ? input.path : "";
13063
13441
  if (!p || !detectFrontendFile(p)) return out;
13064
13442
  const ctx = out.ctx;
13065
- const { default: fs15 } = await import('fs/promises');
13443
+ const { default: fs16 } = await import('fs/promises');
13066
13444
  const { default: nodePath } = await import('path');
13067
13445
  const abs = nodePath.isAbsolute(p) ? p : nodePath.join(ctx.projectRoot, p);
13068
- const text = await fs15.readFile(abs, "utf8").catch(() => "");
13446
+ const text = await fs16.readFile(abs, "utf8").catch(() => "");
13069
13447
  if (!text) return out;
13070
13448
  const loader = getDesignKitLoader(ctx.projectRoot);
13071
13449
  const rawTokens = await loader.readTokens(state.activeKit);
@@ -13365,7 +13743,7 @@ function buildRecoveryStrategies(opts) {
13365
13743
  label: "downgrade_model",
13366
13744
  async attempt(err, ctx) {
13367
13745
  if (!(err instanceof ProviderError)) return null;
13368
- if (err.status !== 429 && err.status !== 529 && err.status < 500) return null;
13746
+ if (err.status !== 529 && err.status < 500) return null;
13369
13747
  return null;
13370
13748
  }
13371
13749
  }
@@ -13414,6 +13792,8 @@ var DefaultErrorHandler = class {
13414
13792
  return null;
13415
13793
  }
13416
13794
  };
13795
+ init_sleep();
13796
+ init_error();
13417
13797
 
13418
13798
  // src/execution/autonomy-brain.ts
13419
13799
  function formatDecisionSummary(decision, request) {
@@ -14044,6 +14424,7 @@ Deliverables: ${goal.deliverables.length} total, progress ${goal.progress ?? "un
14044
14424
  try {
14045
14425
  const decision = await this.opts.brain.decide({
14046
14426
  id: `goal-done-${goal.iterations}`,
14427
+ sessionId: this.opts.agent.ctx.session?.id,
14047
14428
  source: "system",
14048
14429
  question: `Brainstorm returned DONE ${this.consecutiveBrainstormDone}x. Is the goal truly complete?`,
14049
14430
  context: [
@@ -14119,6 +14500,8 @@ function buildGoalPreamble(goal, deliverables) {
14119
14500
  }
14120
14501
 
14121
14502
  // src/execution/intelligent-compactor.ts
14503
+ init_token_estimate();
14504
+ init_message_invariants();
14122
14505
  var IntelligentCompactor = class {
14123
14506
  provider;
14124
14507
  warnThreshold;
@@ -14253,6 +14636,11 @@ var IntelligentCompactor = class {
14253
14636
  return [{ type: "text", text: lines.join("\n") }];
14254
14637
  }
14255
14638
  };
14639
+
14640
+ // src/execution/parallel-eternal-engine.ts
14641
+ init_expect_defined();
14642
+ init_error();
14643
+ init_sleep();
14256
14644
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
14257
14645
  var ParallelEternalEngine = class {
14258
14646
  constructor(opts) {
@@ -14678,6 +15066,12 @@ ${lastFew}` : "No prior iterations.",
14678
15066
  await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
14679
15067
  }
14680
15068
  };
15069
+
15070
+ // src/core/provider-runner.ts
15071
+ init_errors();
15072
+
15073
+ // src/core/streaming-response-builder.ts
15074
+ init_json_repair();
14681
15075
  var STREAM_DRAIN_TIMEOUT_MS = 500;
14682
15076
  function buildResponse(state) {
14683
15077
  const content = [];
@@ -14823,13 +15217,14 @@ function handleMessageStop(state, ev) {
14823
15217
  }
14824
15218
  async function streamProviderToResponse(provider, req, signal, ctx, events, logger) {
14825
15219
  const state = createStreamingState(req.model);
15220
+ const sessionId = ctx.session?.id;
14826
15221
  logger.debug("Stream started", { providerId: provider.id, model: req.model });
14827
15222
  const TEXT_BATCH_SIZE = 4;
14828
15223
  let pendingText = "";
14829
15224
  let pendingCount = 0;
14830
15225
  const flushText = () => {
14831
15226
  if (pendingCount > 0) {
14832
- events.emit("provider.text_delta", { ctx, text: pendingText });
15227
+ events.emit("provider.text_delta", { sessionId, ctx, text: pendingText });
14833
15228
  pendingText = "";
14834
15229
  pendingCount = 0;
14835
15230
  }
@@ -14862,7 +15257,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
14862
15257
  const idVal = ev.id;
14863
15258
  const nameVal = ev.name;
14864
15259
  handleToolUseStart(state, { id: idVal, name: nameVal });
14865
- const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
15260
+ const emittedPayload = { sessionId, ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
14866
15261
  events.emit("provider.tool_use_start", emittedPayload);
14867
15262
  break;
14868
15263
  }
@@ -14873,7 +15268,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
14873
15268
  flushText();
14874
15269
  const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
14875
15270
  handleToolUseStop(state, ev);
14876
- events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
15271
+ events.emit("provider.tool_use_stop", { sessionId, ctx, id: ev.id, name: stoppedName });
14877
15272
  break;
14878
15273
  }
14879
15274
  case "thinking_start":
@@ -14882,7 +15277,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
14882
15277
  case "thinking_delta":
14883
15278
  flushText();
14884
15279
  handleThinkingDelta(state, ev.text);
14885
- events.emit("provider.thinking_delta", { ctx, text: ev.text });
15280
+ events.emit("provider.thinking_delta", { sessionId, ctx, text: ev.text });
14886
15281
  break;
14887
15282
  case "thinking_signature":
14888
15283
  handleThinkingSignature(state, ev.signature);
@@ -14914,6 +15309,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
14914
15309
  });
14915
15310
  flushText();
14916
15311
  events.emit("provider.stream_error", {
15312
+ sessionId,
14917
15313
  ctx,
14918
15314
  eventType: String(evAny.type),
14919
15315
  msg: errMsg
@@ -14942,8 +15338,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
14942
15338
  });
14943
15339
  await Promise.race([
14944
15340
  drainPromise,
14945
- new Promise((resolve11) => {
14946
- drainTimer = setTimeout(resolve11, STREAM_DRAIN_TIMEOUT_MS);
15341
+ new Promise((resolve12) => {
15342
+ drainTimer = setTimeout(resolve12, STREAM_DRAIN_TIMEOUT_MS);
14947
15343
  })
14948
15344
  ]);
14949
15345
  } finally {
@@ -15014,6 +15410,7 @@ async function runProviderWithRetry(opts) {
15014
15410
  if (!canRetry) {
15015
15411
  if (isProviderErr) {
15016
15412
  events.emit("provider.error", {
15413
+ sessionId: ctx.session.id,
15017
15414
  providerId: err.providerId,
15018
15415
  status: err.status,
15019
15416
  description,
@@ -15043,6 +15440,7 @@ async function runProviderWithRetry(opts) {
15043
15440
  });
15044
15441
  if (isProviderErr) {
15045
15442
  events.emit("provider.retry", {
15443
+ sessionId: ctx.session.id,
15046
15444
  providerId: err.providerId,
15047
15445
  attempt: attemptNum,
15048
15446
  delayMs: delay,
@@ -15050,7 +15448,7 @@ async function runProviderWithRetry(opts) {
15050
15448
  description
15051
15449
  });
15052
15450
  }
15053
- await new Promise((resolve11, reject) => {
15451
+ await new Promise((resolve12, reject) => {
15054
15452
  let settled = false;
15055
15453
  const cleanup = () => {
15056
15454
  clearTimeout(t);
@@ -15066,7 +15464,7 @@ async function runProviderWithRetry(opts) {
15066
15464
  if (settled) return;
15067
15465
  settled = true;
15068
15466
  cleanup();
15069
- resolve11();
15467
+ resolve12();
15070
15468
  }, delay);
15071
15469
  if (signal.aborted) {
15072
15470
  onAbort();
@@ -15117,6 +15515,8 @@ var DefaultRetryPolicy = class {
15117
15515
  };
15118
15516
 
15119
15517
  // src/models/llm-selector.ts
15518
+ init_expect_defined();
15519
+ init_token_estimate();
15120
15520
  var DEFAULT_SYSTEM_PROMPT = readBundledInstructionText("llm/llm-selector.md");
15121
15521
  function formatMessages(messages, maxTokens = 2048) {
15122
15522
  const lines = [];
@@ -15287,6 +15687,8 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
15287
15687
  };
15288
15688
 
15289
15689
  // src/execution/selective-compactor.ts
15690
+ init_token_estimate();
15691
+ init_message_invariants();
15290
15692
  var SelectiveCompactor = class {
15291
15693
  provider;
15292
15694
  selector;
@@ -15549,7 +15951,7 @@ var DefaultSkillLoader = class {
15549
15951
  const entries = await fsp7.readdir(dir, { withFileTypes: true });
15550
15952
  for (const e of entries) {
15551
15953
  if (!e.isDirectory()) continue;
15552
- const skillFile = path3.join(dir, e.name, "SKILL.md");
15954
+ const skillFile = path4.join(dir, e.name, "SKILL.md");
15553
15955
  try {
15554
15956
  const raw = await fsp7.readFile(skillFile, "utf8");
15555
15957
  const meta = parseFrontmatter(raw);
@@ -15620,7 +16022,7 @@ var DefaultSkillLoader = class {
15620
16022
  if (cached !== void 0) return cached;
15621
16023
  const m = await this.find(name);
15622
16024
  if (!m) throw new Error(`Skill "${name}" not found`);
15623
- const savePath = path3.join(path3.dirname(m.path), "SKILL.save.md");
16025
+ const savePath = path4.join(path4.dirname(m.path), "SKILL.save.md");
15624
16026
  let result;
15625
16027
  try {
15626
16028
  result = await fsp7.readFile(savePath, "utf8");
@@ -15690,6 +16092,8 @@ function parseDescriptionFromText(desc) {
15690
16092
 
15691
16093
  // src/storage/prompt-store.ts
15692
16094
  init_atomic_write();
16095
+ init_slug();
16096
+ init_ulid();
15693
16097
  var SCHEMA_VERSION = 2;
15694
16098
  function migratePromptEntry(raw) {
15695
16099
  if (!raw || typeof raw !== "object") return null;
@@ -15755,7 +16159,7 @@ var DefaultPromptStore = class {
15755
16159
  if (!file.endsWith(".json")) continue;
15756
16160
  try {
15757
16161
  const raw = JSON.parse(
15758
- await fsp7.readFile(path3.join(this.dir, file), "utf8")
16162
+ await fsp7.readFile(path4.join(this.dir, file), "utf8")
15759
16163
  );
15760
16164
  const migrated = migratePromptEntry(raw.entry);
15761
16165
  if (migrated) entries.push(migrated);
@@ -15769,7 +16173,7 @@ var DefaultPromptStore = class {
15769
16173
  );
15770
16174
  }
15771
16175
  async get(id) {
15772
- const file = path3.join(this.dir, `${id}.json`);
16176
+ const file = path4.join(this.dir, `${id}.json`);
15773
16177
  try {
15774
16178
  const raw = JSON.parse(await fsp7.readFile(file, "utf8"));
15775
16179
  return migratePromptEntry(raw.entry);
@@ -15779,12 +16183,12 @@ var DefaultPromptStore = class {
15779
16183
  }
15780
16184
  async save(entry) {
15781
16185
  await ensureDir(this.dir);
15782
- const file = path3.join(this.dir, `${entry.id}.json`);
16186
+ const file = path4.join(this.dir, `${entry.id}.json`);
15783
16187
  const raw = { version: SCHEMA_VERSION, entry };
15784
16188
  await atomicWrite(file, JSON.stringify(raw, null, 2));
15785
16189
  }
15786
16190
  async delete(id) {
15787
- const file = path3.join(this.dir, `${id}.json`);
16191
+ const file = path4.join(this.dir, `${id}.json`);
15788
16192
  try {
15789
16193
  await fsp7.unlink(file);
15790
16194
  return true;
@@ -15873,7 +16277,7 @@ var DefaultPromptLoader = class {
15873
16277
  constructor(opts) {
15874
16278
  this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
15875
16279
  this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
15876
- this.builtinDir = opts.bundledDir ? path3.join(opts.bundledDir, "prompts") : void 0;
16280
+ this.builtinDir = opts.bundledDir ? path4.join(opts.bundledDir, "prompts") : void 0;
15877
16281
  }
15878
16282
  async list() {
15879
16283
  if (this.cache) return this.cache;
@@ -16005,7 +16409,7 @@ async function walkJson(dir) {
16005
16409
  return out;
16006
16410
  }
16007
16411
  for (const e of entries) {
16008
- const full = path3.join(dir, e.name);
16412
+ const full = path4.join(dir, e.name);
16009
16413
  if (e.isDirectory()) {
16010
16414
  out.push(...await walkJson(full));
16011
16415
  } else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
@@ -16051,7 +16455,7 @@ function isSubsequence(needle, hay) {
16051
16455
  function renderPrompt(entry, values = {}) {
16052
16456
  const declared = new Map((entry.variables ?? []).map((v) => [v.name, v]));
16053
16457
  const missing = [];
16054
- const invalid = [];
16458
+ const invalid2 = [];
16055
16459
  const text = entry.content.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (whole, rawName) => {
16056
16460
  const name = rawName.trim();
16057
16461
  if (Object.hasOwn(values, name) && values[name] !== void 0) {
@@ -16069,10 +16473,10 @@ function renderPrompt(entry, values = {}) {
16069
16473
  }
16070
16474
  }
16071
16475
  if (v.enum && v.enum.length > 0 && Object.hasOwn(values, v.name) && values[v.name] !== void 0 && values[v.name] !== "" && !v.enum.includes(values[v.name])) {
16072
- invalid.push(v.name);
16476
+ invalid2.push(v.name);
16073
16477
  }
16074
16478
  }
16075
- return { text, missing, invalid };
16479
+ return { text, missing, invalid: invalid2 };
16076
16480
  }
16077
16481
  function escapeRegExp(s) {
16078
16482
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -16146,11 +16550,19 @@ function readPolicy(ctx) {
16146
16550
  if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
16147
16551
  return candidate;
16148
16552
  }
16553
+ init_error();
16554
+ init_expect_defined();
16555
+ init_json_schema_validate();
16556
+ init_tool_subject();
16557
+ init_tool_output_serializer();
16558
+ init_wstack_paths();
16559
+ init_errors();
16149
16560
 
16150
16561
  // src/types/tool-markers.ts
16151
16562
  var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
16152
16563
 
16153
16564
  // src/execution/tool-executor.ts
16565
+ init_tool_result_render_mode();
16154
16566
  var ToolExecutor = class _ToolExecutor {
16155
16567
  constructor(registry, opts) {
16156
16568
  this.registry = registry;
@@ -16200,6 +16612,64 @@ var ToolExecutor = class _ToolExecutor {
16200
16612
  const mode = resolveToolResultRenderMode(modes, toolName);
16201
16613
  renderer.setResultRenderMode(toolName, mode);
16202
16614
  }
16615
+ toolLogBase(ctx, use, toolName, durationMs) {
16616
+ return {
16617
+ event: "tool.execution",
16618
+ traceId: ctx.traceId,
16619
+ sessionId: ctx.session.id,
16620
+ agentId: ctx.agentId ?? "<unknown>",
16621
+ toolName,
16622
+ toolUseId: use.id,
16623
+ durationMs
16624
+ };
16625
+ }
16626
+ logToolSuccess(ctx, use, toolName, durationMs, outputChars) {
16627
+ this.opts.events?.emit("tool.completed", {
16628
+ name: toolName,
16629
+ id: use.id,
16630
+ sessionId: ctx.session.id,
16631
+ ...ctx.traceId ? { traceId: ctx.traceId } : {},
16632
+ agentId: ctx.agentId ?? "<unknown>",
16633
+ durationMs,
16634
+ outputChars
16635
+ });
16636
+ this.opts.logger?.info("tool execution completed", {
16637
+ ...this.toolLogBase(ctx, use, toolName, durationMs),
16638
+ outcome: "success",
16639
+ isError: false,
16640
+ outputChars
16641
+ });
16642
+ }
16643
+ logToolFailure(ctx, use, toolName, durationMs, err) {
16644
+ const { category, retryable, detail } = classifyToolError(err);
16645
+ const structured = isWrongStackError(err);
16646
+ const structuredError = structured ? {
16647
+ errorCode: err.code,
16648
+ errorSubsystem: err.subsystem,
16649
+ errorSeverity: err.severity
16650
+ } : {};
16651
+ this.opts.events?.emit("tool.failed", {
16652
+ name: toolName,
16653
+ id: use.id,
16654
+ sessionId: ctx.session.id,
16655
+ ...ctx.traceId ? { traceId: ctx.traceId } : {},
16656
+ agentId: ctx.agentId ?? "<unknown>",
16657
+ durationMs,
16658
+ category,
16659
+ retryable,
16660
+ ...detail ? { detail } : {},
16661
+ ...structuredError
16662
+ });
16663
+ this.opts.logger?.warn("tool execution failed", {
16664
+ ...this.toolLogBase(ctx, use, toolName, durationMs),
16665
+ outcome: "failure",
16666
+ isError: true,
16667
+ errorCategory: category,
16668
+ retryable,
16669
+ errorDetail: detail,
16670
+ ...structuredError
16671
+ });
16672
+ }
16203
16673
  /**
16204
16674
  * Execute a batch of tool uses using the configured strategy.
16205
16675
  * Returns the execution results and the remaining output budget.
@@ -16330,7 +16800,8 @@ ${errorDetails}`,
16330
16800
  "tool.has_dangerous_capabilities": toolCapsForAudit.length > 0
16331
16801
  });
16332
16802
  try {
16333
- let { block: result, bytes } = await this.executeTool(tool, use, ctx, budget);
16803
+ const producedText = await this.produceToolOutput(tool, use, ctx, budget);
16804
+ let { block: result, bytes } = this.settleToolOutput(tool, use, producedText, budget);
16334
16805
  budget -= bytes;
16335
16806
  if (this.opts.hookRunner?.has("PostToolUse")) {
16336
16807
  const post = await this.opts.hookRunner.postToolUse(
@@ -16347,13 +16818,18 @@ ${post.additionalContext}`;
16347
16818
  budget = Math.max(0, budget - Buffer.byteLength(appended, "utf8"));
16348
16819
  }
16349
16820
  }
16821
+ const outputChars = typeof result.content === "string" ? result.content.length : 0;
16350
16822
  span?.setAttribute("tool.is_error", !!result.is_error);
16351
- span?.setAttribute(
16352
- "tool.output_bytes",
16353
- typeof result.content === "string" ? result.content.length : 0
16354
- );
16823
+ span?.setAttribute("tool.output_bytes", outputChars);
16824
+ this.logToolSuccess(ctx, use, tool.name, Date.now() - start, outputChars);
16355
16825
  return { result, tool, durationMs: Date.now() - start };
16356
16826
  } catch (err) {
16827
+ if (isWrongStackError(err)) {
16828
+ if (err instanceof Error) span?.recordError(err);
16829
+ span?.setAttribute("tool.is_error", true);
16830
+ this.logToolFailure(ctx, use, tool.name, Date.now() - start, err);
16831
+ throw err;
16832
+ }
16357
16833
  const msg = toErrorMessage(err);
16358
16834
  const scrubbed = this.opts.secretScrubber.scrub(msg);
16359
16835
  const { category, retryable, detail } = classifyToolError(err);
@@ -16371,6 +16847,7 @@ ${post.additionalContext}`;
16371
16847
  span?.setAttribute("tool.error_category", category);
16372
16848
  span?.setAttribute("tool.error_retryable", retryable);
16373
16849
  if (detail) span?.setAttribute("tool.error_detail", detail);
16850
+ this.logToolFailure(ctx, use, tool.name, Date.now() - start, err);
16374
16851
  return { result, tool, durationMs: Date.now() - start };
16375
16852
  } finally {
16376
16853
  span?.end();
@@ -16380,7 +16857,8 @@ ${post.additionalContext}`;
16380
16857
  try {
16381
16858
  return await runOne(use);
16382
16859
  } catch (err) {
16383
- const msg = toErrorMessage(err);
16860
+ const isStructured = isWrongStackError(err);
16861
+ const msg = isStructured ? err.describe() : toErrorMessage(err);
16384
16862
  const scrubbed = this.opts.secretScrubber.scrub(msg);
16385
16863
  const { category, retryable, detail } = classifyToolError(err);
16386
16864
  const tool = this.registry.get(use.name);
@@ -16390,7 +16868,7 @@ ${post.additionalContext}`;
16390
16868
  const result = {
16391
16869
  type: "tool_result",
16392
16870
  tool_use_id: use.id,
16393
- content: `Tool "${use.name}" execution failed: ${scrubbed}`,
16871
+ content: isStructured ? scrubbed : `Tool "${use.name}" execution failed: ${scrubbed}`,
16394
16872
  is_error: true
16395
16873
  };
16396
16874
  budget = this.budgetForString(result.content, budget);
@@ -16433,7 +16911,23 @@ ${post.additionalContext}`;
16433
16911
  * to call a tool" and "tool.executed".
16434
16912
  */
16435
16913
  async executeTool(tool, use, ctx, budget) {
16914
+ const text = await this.produceToolOutput(tool, use, ctx, budget);
16915
+ return this.settleToolOutput(tool, use, text, budget);
16916
+ }
16917
+ /**
16918
+ * Async "produce" phase: run the tool, serialize, scrub, and spill an
16919
+ * oversized output to a disk artifact. Returns the pre-cap text. This is
16920
+ * the long-running, concurrency-safe part — it touches NO shared budget
16921
+ * state, so multiple invocations can run in parallel without racing.
16922
+ *
16923
+ * The `budgetHint` only gates the disk-spill threshold (a heuristic for
16924
+ * "is this output large enough to persist"); it is NOT the output cap.
16925
+ * The authoritative cap is applied synchronously in settleToolOutput()
16926
+ * against the live budget.
16927
+ */
16928
+ async produceToolOutput(tool, use, ctx, budgetHint) {
16436
16929
  this.opts.events?.emit("tool.started", {
16930
+ sessionId: ctx.session.id,
16437
16931
  name: tool.name,
16438
16932
  id: use.id,
16439
16933
  input: use.input
@@ -16442,8 +16936,20 @@ ${post.additionalContext}`;
16442
16936
  const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
16443
16937
  const text = this.serializer.serialize(output, { toolName: tool.name, input: use.input, tool });
16444
16938
  const scrubbed = this.opts.secretScrubber.scrub(text);
16445
- const withArtifact = await maybePersistLargeToolOutput(tool.name, scrubbed, budget);
16446
- const { text: capped, newBudget } = this.serializer.enforceCap(withArtifact, budget);
16939
+ return maybePersistLargeToolOutput(tool.name, scrubbed, budgetHint);
16940
+ }
16941
+ /**
16942
+ * Synchronous "settle" phase: enforce the output cap against the CURRENT
16943
+ * budget, render, and build the result block. This MUST stay synchronous
16944
+ * (no awaits) so that, in the parallel/smart strategies, two tools settling
16945
+ * one after another against the shared closure budget can't interleave a
16946
+ * stale read with a write. The first to settle consumes its bytes; the next
16947
+ * settles against the reduced budget; once the budget hits 0, enforceCap
16948
+ * truncates — making the per-iteration cap genuinely CUMULATIVE across
16949
+ * parallel tools instead of degrading into a per-tool cap.
16950
+ */
16951
+ settleToolOutput(tool, use, text, budget) {
16952
+ const { text: capped, newBudget } = this.serializer.enforceCap(text, budget);
16447
16953
  this.hintRenderMode(tool.name);
16448
16954
  this.opts.renderer?.writeToolResult(tool.name, capped, false);
16449
16955
  return {
@@ -16496,7 +17002,12 @@ ${post.additionalContext}`;
16496
17002
  let finalOutput;
16497
17003
  let sawFinal = false;
16498
17004
  if (!tool.executeStream) {
16499
- throw new Error(`Tool "${tool.name}" does not support streaming execution`);
17005
+ throw new ToolError({
17006
+ message: `Tool "${tool.name}" does not support streaming execution`,
17007
+ code: "TOOL_EXECUTION_FAILED",
17008
+ toolName: tool.name,
17009
+ context: { reason: "streaming_not_supported" }
17010
+ });
16500
17011
  }
16501
17012
  const stream = tool.executeStream(input, ctx, { signal });
16502
17013
  const iter = stream[Symbol.asyncIterator]();
@@ -16506,6 +17017,7 @@ ${post.additionalContext}`;
16506
17017
  let lastProgressEmitAt = 0;
16507
17018
  const emitProgress = (ev) => {
16508
17019
  this.opts.events?.emit("tool.progress", {
17020
+ sessionId: ctx.session.id,
16509
17021
  name: tool.name,
16510
17022
  id: toolUseId ?? "<unknown>",
16511
17023
  event: ev
@@ -16561,7 +17073,12 @@ ${progressTail}`;
16561
17073
  await iter.return?.(void 0);
16562
17074
  }
16563
17075
  if (!sawFinal) {
16564
- throw new Error(`tool "${tool.name}" executeStream completed without a 'final' event`);
17076
+ throw new ToolError({
17077
+ message: `tool "${tool.name}" executeStream completed without a 'final' event`,
17078
+ code: "TOOL_EXECUTION_FAILED",
17079
+ toolName: tool.name,
17080
+ context: { reason: "missing_final_event" }
17081
+ });
16565
17082
  }
16566
17083
  return finalOutput;
16567
17084
  }
@@ -16685,6 +17202,15 @@ function classifyToolError(err) {
16685
17202
  if (err instanceof Error && err.message.includes("validation")) {
16686
17203
  return { category: "validation" /* VALIDATION */, retryable: false, detail: "validation" };
16687
17204
  }
17205
+ if (err instanceof WrongStackError) {
17206
+ const wse = err;
17207
+ const category = wse.severity === "warning" ? "transient" /* TRANSIENT */ : "fatal" /* FATAL */;
17208
+ return {
17209
+ category,
17210
+ retryable: wse.recoverable,
17211
+ detail: `${wse.code} [${wse.subsystem}]`
17212
+ };
17213
+ }
16688
17214
  return {
16689
17215
  category: "fatal" /* FATAL */,
16690
17216
  retryable: false,
@@ -16712,11 +17238,11 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
16712
17238
  return content;
16713
17239
  }
16714
17240
  try {
16715
- const dir = path3.join(wstackGlobalRoot(), "tool-output");
17241
+ const dir = path4.join(wstackGlobalRoot(), "tool-output");
16716
17242
  await fsp7.mkdir(dir, { recursive: true });
16717
17243
  const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
16718
17244
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
16719
- const filePath = path3.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
17245
+ const filePath = path4.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
16720
17246
  await fsp7.writeFile(filePath, content, "utf8");
16721
17247
  return content + `
16722
17248
  [full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
@@ -16726,6 +17252,8 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
16726
17252
  }
16727
17253
 
16728
17254
  // src/infrastructure/context-manager.ts
17255
+ init_message_invariants();
17256
+ init_token_estimate();
16729
17257
  var CONTEXT_MANAGER_TOOL_NAME = "context_manager";
16730
17258
  function roughEstimate(messages) {
16731
17259
  return estimateMessageTokens(messages);
@@ -16981,6 +17509,10 @@ function createContextManagerTool(opts = {}) {
16981
17509
  };
16982
17510
  }
16983
17511
  var contextManagerTool = createContextManagerTool();
17512
+
17513
+ // src/infrastructure/logger.ts
17514
+ init_color();
17515
+ init_term();
16984
17516
  var LEVEL_RANK2 = {
16985
17517
  error: 0,
16986
17518
  warn: 1,
@@ -17044,7 +17576,7 @@ var DefaultLogger = class _DefaultLogger {
17044
17576
  this.stderr = opts.stderr !== false;
17045
17577
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
17046
17578
  if (this.file) {
17047
- const dir = path3.dirname(this.file);
17579
+ const dir = path4.dirname(this.file);
17048
17580
  this._tail = this._tail.then(async () => {
17049
17581
  await fsp7.mkdir(dir, { recursive: true });
17050
17582
  }).catch(() => void 0);
@@ -17346,18 +17878,18 @@ init_atomic_write();
17346
17878
  function modePrompt(id) {
17347
17879
  for (const dir of modePromptDirCandidates()) {
17348
17880
  try {
17349
- return readFileSync(path3.join(dir, `${id}.md`), "utf8").trimEnd();
17881
+ return readFileSync(path4.join(dir, `${id}.md`), "utf8").trimEnd();
17350
17882
  } catch {
17351
17883
  }
17352
17884
  }
17353
17885
  return "";
17354
17886
  }
17355
17887
  function modePromptDirCandidates() {
17356
- const here = path3.dirname(fileURLToPath(import.meta.url));
17888
+ const here = path4.dirname(fileURLToPath(import.meta.url));
17357
17889
  const candidates = [
17358
- path3.resolve(here, "../../instructions/modes"),
17359
- path3.resolve(here, "../instructions/modes"),
17360
- path3.resolve(here, "instructions/modes")
17890
+ path4.resolve(here, "../../instructions/modes"),
17891
+ path4.resolve(here, "../instructions/modes"),
17892
+ path4.resolve(here, "instructions/modes")
17361
17893
  ];
17362
17894
  return candidates.sort((a, b) => Number(!isDirectory3(a)) - Number(!isDirectory3(b)));
17363
17895
  }
@@ -17474,7 +18006,7 @@ var DEFAULT_MODES = [
17474
18006
  description: "Current-data research \u2014 search web, verify, inject findings into context",
17475
18007
  prompt: modePrompt("research-web"),
17476
18008
  tags: ["research", "web", "current-data", "up-to-date"],
17477
- toolPreferences: ["web_search", "web_fetch", "search", "fetch", "context_manager"],
18009
+ toolPreferences: ["search/fetch", "search/fetch", "search", "fetch", "context_manager"],
17478
18010
  suggestedSkills: ["research-web", "tech-stack", "node-modern", "security-scanner", "react-modern"]
17479
18011
  }
17480
18012
  ];
@@ -17526,7 +18058,7 @@ var DefaultModeStore = class {
17526
18058
  }
17527
18059
  async loadActiveMode() {
17528
18060
  try {
17529
- const configPath = path3.join(this.configDir, "mode.json");
18061
+ const configPath = path4.join(this.configDir, "mode.json");
17530
18062
  const content = await fsp7.readFile(configPath, "utf8");
17531
18063
  const data = JSON.parse(content);
17532
18064
  this.activeModeId = data.activeMode ?? null;
@@ -17537,7 +18069,7 @@ var DefaultModeStore = class {
17537
18069
  async saveActiveMode() {
17538
18070
  try {
17539
18071
  await fsp7.mkdir(this.configDir, { recursive: true });
17540
- const configPath = path3.join(this.configDir, "mode.json");
18072
+ const configPath = path4.join(this.configDir, "mode.json");
17541
18073
  await atomicWrite(
17542
18074
  configPath,
17543
18075
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -17552,11 +18084,11 @@ async function loadProjectModes(modesDir) {
17552
18084
  const entries = await fsp7.readdir(modesDir);
17553
18085
  for (const entry of entries) {
17554
18086
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
17555
- const filePath = path3.join(modesDir, entry);
17556
- const stat9 = await fsp7.stat(filePath);
17557
- if (!stat9.isFile()) continue;
18087
+ const filePath = path4.join(modesDir, entry);
18088
+ const stat10 = await fsp7.stat(filePath);
18089
+ if (!stat10.isFile()) continue;
17558
18090
  const content = await fsp7.readFile(filePath, "utf8");
17559
- const id = path3.basename(entry, path3.extname(entry));
18091
+ const id = path4.basename(entry, path4.extname(entry));
17560
18092
  modes.push({
17561
18093
  id,
17562
18094
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -17572,7 +18104,7 @@ async function loadProjectModes(modesDir) {
17572
18104
  async function loadUserModes(modesDir) {
17573
18105
  const modes = [];
17574
18106
  try {
17575
- const manifestPath = path3.join(modesDir, "modes.json");
18107
+ const manifestPath = path4.join(modesDir, "modes.json");
17576
18108
  const content = await fsp7.readFile(manifestPath, "utf8");
17577
18109
  const manifest = JSON.parse(content);
17578
18110
  for (const mode of manifest.modes) {
@@ -17585,6 +18117,9 @@ async function loadUserModes(modesDir) {
17585
18117
 
17586
18118
  // src/models/models-registry.ts
17587
18119
  init_atomic_write();
18120
+ init_error();
18121
+ init_merge_models_payload();
18122
+ init_errors();
17588
18123
  var DEFAULT_URL = "https://models.dev/api.json";
17589
18124
  var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
17590
18125
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -17618,6 +18153,14 @@ var DefaultModelsRegistry = class {
17618
18153
  payload;
17619
18154
  /** Memoised overlay payload (in-memory / fetched / file). */
17620
18155
  overlayPayload;
18156
+ /**
18157
+ * Extra providers injected at runtime via `mergeOverlay()` — e.g. an
18158
+ * openai-compatible server (omniroute, LiteLLM, …) auto-discovered from its
18159
+ * `/v1/models` endpoint at boot. Applied LAST (on top of base + curated
18160
+ * overlay) and re-applied across `refresh()` so the discovered catalog
18161
+ * survives a models.dev refetch.
18162
+ */
18163
+ extraOverlay;
17621
18164
  fetchedAt;
17622
18165
  cacheFile;
17623
18166
  url;
@@ -17642,7 +18185,7 @@ var DefaultModelsRegistry = class {
17642
18185
  this.overlay = opts.overlay;
17643
18186
  this.overlayUrl = opts.overlayUrl;
17644
18187
  this.overlayFile = opts.overlayFile;
17645
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
18188
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path4.join(path4.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
17646
18189
  }
17647
18190
  async load(opts = {}) {
17648
18191
  if (this.payload && !opts.force) return this.payload;
@@ -17653,9 +18196,22 @@ var DefaultModelsRegistry = class {
17653
18196
  }
17654
18197
  const overlay = await this.loadOverlay(opts);
17655
18198
  const base = await this.loadBase(opts, Object.keys(overlay).length > 0);
17656
- this.payload = mergeModelsPayload(base, overlay);
18199
+ this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));
17657
18200
  return this.payload;
17658
18201
  }
18202
+ /**
18203
+ * Merge an additional provider payload on top of the resolved catalog. Used
18204
+ * for runtime-discovered openai-compatible providers. Remembered so it is
18205
+ * re-applied across `refresh()`. A no-op for an empty payload.
18206
+ */
18207
+ mergeOverlay(payload) {
18208
+ if (!hasEntries(payload)) return;
18209
+ this.extraOverlay = this.extraOverlay ? mergeModelsPayload(this.extraOverlay, payload) : payload;
18210
+ if (this.payload) this.payload = mergeModelsPayload(this.payload, this.extraOverlay);
18211
+ }
18212
+ withExtraOverlay(payload) {
18213
+ return this.extraOverlay ? mergeModelsPayload(payload, this.extraOverlay) : payload;
18214
+ }
17659
18215
  /**
17660
18216
  * Load the models.dev base payload: fresh cache → network → stale cache.
17661
18217
  * On total failure, degrade to `{}` (so a non-empty overlay still drives
@@ -17704,7 +18260,11 @@ var DefaultModelsRegistry = class {
17704
18260
  });
17705
18261
  clearTimeout(timeout);
17706
18262
  if (!res.ok) {
17707
- throw new Error(`ModelsRegistry: HTTP ${res.status} fetching ${this.url}`);
18263
+ throw new FetchError({
18264
+ message: `ModelsRegistry: HTTP ${res.status} fetching ${this.url}`,
18265
+ status: res.status,
18266
+ context: { url: this.url, op: "refreshModels" }
18267
+ });
17708
18268
  }
17709
18269
  const json = await res.json();
17710
18270
  this.fetchedAt = /* @__PURE__ */ new Date();
@@ -17718,7 +18278,11 @@ var DefaultModelsRegistry = class {
17718
18278
  } catch (err) {
17719
18279
  clearTimeout(timeout);
17720
18280
  if (err instanceof Error && err.name === "AbortError") {
17721
- throw new Error(`ModelsRegistry: fetch timed out after ${this.refreshTimeoutMs}ms`);
18281
+ throw new FetchError({
18282
+ message: `ModelsRegistry: fetch timed out after ${this.refreshTimeoutMs}ms`,
18283
+ status: 408,
18284
+ context: { url: this.url, op: "refreshModels", timedOut: true }
18285
+ });
17722
18286
  }
17723
18287
  throw err;
17724
18288
  }
@@ -17754,7 +18318,11 @@ var DefaultModelsRegistry = class {
17754
18318
  method: "GET",
17755
18319
  headers: { accept: "application/json" }
17756
18320
  });
17757
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
18321
+ if (!res.ok) throw new FetchError({
18322
+ message: `HTTP ${res.status}`,
18323
+ status: res.status,
18324
+ context: { url: this.url, op: "refreshModels" }
18325
+ });
17758
18326
  const json = await res.json();
17759
18327
  const envelope = {
17760
18328
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -17788,7 +18356,7 @@ var DefaultModelsRegistry = class {
17788
18356
  async refresh() {
17789
18357
  const base = await this.refreshBase();
17790
18358
  const overlay = await this.loadOverlay({ force: true });
17791
- this.payload = mergeModelsPayload(base, overlay);
18359
+ this.payload = this.withExtraOverlay(mergeModelsPayload(base, overlay));
17792
18360
  return this.payload;
17793
18361
  }
17794
18362
  async listProviders() {
@@ -17866,7 +18434,7 @@ var DefaultModelsRegistry = class {
17866
18434
  }
17867
18435
  /** Used by `wstack models refresh` to expose where the cache lives. */
17868
18436
  cacheLocation() {
17869
- return path3.resolve(this.cacheFile);
18437
+ return path4.resolve(this.cacheFile);
17870
18438
  }
17871
18439
  };
17872
18440
  function formatAge(seconds) {
@@ -18048,6 +18616,7 @@ var NoopMetricsSink = class {
18048
18616
  };
18049
18617
 
18050
18618
  // src/observability/health.ts
18619
+ init_error();
18051
18620
  var SEVERITY = {
18052
18621
  healthy: 0,
18053
18622
  degraded: 1,
@@ -18080,9 +18649,9 @@ var DefaultHealthRegistry = class {
18080
18649
  }
18081
18650
  async runOne(check) {
18082
18651
  let timer = null;
18083
- const timeout = new Promise((resolve11) => {
18652
+ const timeout = new Promise((resolve12) => {
18084
18653
  timer = setTimeout(
18085
- () => resolve11({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
18654
+ () => resolve12({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
18086
18655
  this.timeoutMs
18087
18656
  );
18088
18657
  });
@@ -18205,6 +18774,7 @@ function wireMetricsToEvents(events, sink) {
18205
18774
  }
18206
18775
 
18207
18776
  // src/observability/prometheus.ts
18777
+ init_error();
18208
18778
  var NUMBER_FORMAT_INFINITY = "NaN";
18209
18779
  function escapeLabelValue(v) {
18210
18780
  return v.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
@@ -18321,14 +18891,14 @@ async function startMetricsServer(opts) {
18321
18891
  const { createServer } = await import('http');
18322
18892
  server = createServer(listener);
18323
18893
  }
18324
- await new Promise((resolve11, reject) => {
18894
+ await new Promise((resolve12, reject) => {
18325
18895
  const onError = (err) => {
18326
18896
  server.off("listening", onListening);
18327
18897
  reject(err);
18328
18898
  };
18329
18899
  const onListening = () => {
18330
18900
  server.off("error", onError);
18331
- resolve11();
18901
+ resolve12();
18332
18902
  };
18333
18903
  server.once("error", onError);
18334
18904
  server.once("listening", onListening);
@@ -18340,8 +18910,8 @@ async function startMetricsServer(opts) {
18340
18910
  return {
18341
18911
  port: boundPort,
18342
18912
  url: `${protocol}://${host}:${boundPort}${path32}`,
18343
- close: () => new Promise((resolve11, reject) => {
18344
- server.close((err) => err ? reject(err) : resolve11());
18913
+ close: () => new Promise((resolve12, reject) => {
18914
+ server.close((err) => err ? reject(err) : resolve12());
18345
18915
  })
18346
18916
  };
18347
18917
  }
@@ -18607,6 +19177,9 @@ function startOtlpTraceExporter(opts) {
18607
19177
  };
18608
19178
  }
18609
19179
 
19180
+ // src/sdd/critical-path.ts
19181
+ init_expect_defined();
19182
+
18610
19183
  // src/types/task-graph.ts
18611
19184
  function computeTaskProgress(graph) {
18612
19185
  let completed = 0;
@@ -19518,13 +20091,22 @@ var SddBoardProjector = class _SddBoardProjector {
19518
20091
  this.timer = null;
19519
20092
  }
19520
20093
  const snap = this.build();
19521
- this.o.events.emit("sdd.board.snapshot", { runId: this.o.runId, snapshot: snap });
20094
+ const sessionId = this.currentSessionId();
20095
+ this.o.events.emit("sdd.board.snapshot", {
20096
+ ...sessionId ? { sessionId } : {},
20097
+ runId: this.o.runId,
20098
+ snapshot: snap
20099
+ });
19522
20100
  if (this.o.store) {
19523
20101
  const store = this.o.store;
19524
20102
  this.lastSave = this.lastSave.then(() => store.saveSnapshot(snap)).catch(() => {
19525
20103
  });
19526
20104
  }
19527
20105
  }
20106
+ currentSessionId() {
20107
+ const value = typeof this.o.sessionId === "function" ? this.o.sessionId() : this.o.sessionId;
20108
+ return typeof value === "string" && value.length > 0 ? value : void 0;
20109
+ }
19528
20110
  };
19529
20111
 
19530
20112
  // src/sdd/sdd-board-store.ts
@@ -19534,16 +20116,16 @@ var SddBoardStore = class {
19534
20116
  indexPath;
19535
20117
  constructor(opts) {
19536
20118
  this.baseDir = opts.baseDir;
19537
- this.indexPath = path3.join(this.baseDir, "_index.json");
20119
+ this.indexPath = path4.join(this.baseDir, "_index.json");
19538
20120
  }
19539
20121
  snapshotPath(runId) {
19540
- return path3.join(this.baseDir, `${this.safe(runId)}.json`);
20122
+ return path4.join(this.baseDir, `${this.safe(runId)}.json`);
19541
20123
  }
19542
20124
  eventsPath(runId) {
19543
- return path3.join(this.baseDir, `${this.safe(runId)}.events.jsonl`);
20125
+ return path4.join(this.baseDir, `${this.safe(runId)}.events.jsonl`);
19544
20126
  }
19545
20127
  controlPath(runId) {
19546
- return path3.join(this.baseDir, `${this.safe(runId)}.control.jsonl`);
20128
+ return path4.join(this.baseDir, `${this.safe(runId)}.control.jsonl`);
19547
20129
  }
19548
20130
  async saveSnapshot(snapshot) {
19549
20131
  await ensureDir(this.baseDir);
@@ -19649,6 +20231,9 @@ var SddBoardStore = class {
19649
20231
  };
19650
20232
 
19651
20233
  // src/sdd/spec-builder.ts
20234
+ init_expect_defined();
20235
+ init_error();
20236
+ init_errors();
19652
20237
  function buildQuestioningPrompt(session, min, max) {
19653
20238
  const answered = session.answers.length;
19654
20239
  const remaining = Math.max(0, min - answered);
@@ -20202,6 +20787,8 @@ var AISpecBuilder = class {
20202
20787
  };
20203
20788
 
20204
20789
  // src/sdd/task-tracker.ts
20790
+ init_errors();
20791
+ init_error();
20205
20792
  var TaskTracker = class {
20206
20793
  constructor(opts) {
20207
20794
  this.opts = opts;
@@ -21026,10 +21613,15 @@ function isExplanatoryText(text) {
21026
21613
  const lower = text.toLowerCase();
21027
21614
  return lower.startsWith("i'") || lower.startsWith("i will") || lower.startsWith("let me") || lower.startsWith("here's my") || lower.startsWith("here is my") || lower.startsWith("i'm going to") || lower.startsWith("first, let me") || lower.startsWith("sure") || lower.startsWith("of course") || lower.startsWith("okay") || lower.startsWith("ok,") || lower.startsWith("sounds good") || lower.startsWith("no problem") || text.split("\n").length < 3 && !text.includes(".");
21028
21615
  }
21616
+
21617
+ // src/worktree/worktree-manager.ts
21618
+ init_child_env();
21619
+ init_error();
21029
21620
  var MAX_SLUG = 40;
21030
21621
  var WorktreeManager = class {
21031
21622
  projectRoot;
21032
21623
  events;
21624
+ sessionIdSource;
21033
21625
  gitBin;
21034
21626
  runGit;
21035
21627
  /** Keyed by ownerId. */
@@ -21038,6 +21630,7 @@ var WorktreeManager = class {
21038
21630
  constructor(opts) {
21039
21631
  this.projectRoot = resolve(opts.projectRoot);
21040
21632
  this.events = opts.events;
21633
+ this.sessionIdSource = opts.sessionId;
21041
21634
  this.gitBin = opts.gitBin ?? "git";
21042
21635
  this.runGit = opts.run ?? ((args, cwd) => this.defaultRun(args, cwd));
21043
21636
  }
@@ -21631,7 +22224,15 @@ ${check.stderr}`);
21631
22224
  if (patch) Object.assign(handle, patch);
21632
22225
  }
21633
22226
  emit(event, payload) {
21634
- this.events?.emit(event, payload);
22227
+ const sessionId = this.currentSessionId();
22228
+ this.events?.emit(
22229
+ event,
22230
+ sessionId && payload && typeof payload === "object" ? { ...payload, sessionId } : payload
22231
+ );
22232
+ }
22233
+ currentSessionId() {
22234
+ const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
22235
+ return typeof value === "string" && value.length > 0 ? value : void 0;
21635
22236
  }
21636
22237
  defaultRun(args, cwd) {
21637
22238
  return new Promise((res) => {
@@ -21784,6 +22385,10 @@ async function applySddLifecycle(op, opts) {
21784
22385
  }
21785
22386
  }
21786
22387
 
22388
+ // src/sdd/sdd-parallel-run.ts
22389
+ init_expect_defined();
22390
+ init_errors();
22391
+
21787
22392
  // src/sdd/sdd-task-decomposer.ts
21788
22393
  var SddTaskDecomposer = class {
21789
22394
  constructor(tracker, _graph, opts = {}) {
@@ -21902,6 +22507,7 @@ var SddParallelRun = class {
21902
22507
  this.maxFailedSweeps = Math.max(0, opts.maxFailedRetrySweeps ?? 2);
21903
22508
  this.runId = opts.runId ?? `sdd-${randomUUID().slice(0, 8)}`;
21904
22509
  this.events = opts.events;
22510
+ this.sessionIdSource = opts.sessionId;
21905
22511
  this.maxTotalWaves = opts.maxTotalWaves ?? opts.graph.nodes.size * (this.maxRetries + 2) + 10;
21906
22512
  this.maxWallClockMs = opts.maxWallClockMs;
21907
22513
  this.maxRecoveryRounds = Math.max(0, opts.maxRecoveryRounds ?? 0);
@@ -21930,6 +22536,7 @@ var SddParallelRun = class {
21930
22536
  retryMap = /* @__PURE__ */ new Map();
21931
22537
  runId;
21932
22538
  events;
22539
+ sessionIdSource;
21933
22540
  maxTotalWaves;
21934
22541
  maxWallClockMs;
21935
22542
  maxRecoveryRounds;
@@ -21962,7 +22569,15 @@ var SddParallelRun = class {
21962
22569
  round = 0;
21963
22570
  /** Type-safe emit on the optional EventBus (no-op when unwired). */
21964
22571
  emit(event, payload) {
21965
- this.events?.emit(event, payload);
22572
+ const sessionId = this.currentSessionId();
22573
+ this.events?.emit(
22574
+ event,
22575
+ sessionId ? { ...payload, sessionId } : payload
22576
+ );
22577
+ }
22578
+ currentSessionId() {
22579
+ const value = typeof this.sessionIdSource === "function" ? this.sessionIdSource() : this.sessionIdSource;
22580
+ return typeof value === "string" && value.length > 0 ? value : void 0;
21966
22581
  }
21967
22582
  // -------------------------------------------------------------------
21968
22583
  // Public API
@@ -23094,7 +23709,7 @@ var SpecStore = class {
23094
23709
  indexPath;
23095
23710
  constructor(opts) {
23096
23711
  this.baseDir = opts.baseDir;
23097
- this.indexPath = path3.join(this.baseDir, "_index.json");
23712
+ this.indexPath = path4.join(this.baseDir, "_index.json");
23098
23713
  }
23099
23714
  async save(spec) {
23100
23715
  await ensureDir(this.baseDir);
@@ -23163,7 +23778,7 @@ var SpecStore = class {
23163
23778
  return updated;
23164
23779
  }
23165
23780
  filePath(id) {
23166
- return path3.join(this.baseDir, `${id}.json`);
23781
+ return path4.join(this.baseDir, `${id}.json`);
23167
23782
  }
23168
23783
  async readIndex() {
23169
23784
  try {
@@ -23316,6 +23931,7 @@ function templateToMarkdown(template, title) {
23316
23931
  }
23317
23932
 
23318
23933
  // src/sdd/spec-versioning.ts
23934
+ init_assert_never();
23319
23935
  var SpecVersioning = class {
23320
23936
  versions = /* @__PURE__ */ new Map();
23321
23937
  /** Record a new version of a spec. */
@@ -23487,6 +24103,7 @@ function startSddRun(opts) {
23487
24103
  graph: opts.graph,
23488
24104
  agent: opts.agent,
23489
24105
  projectRoot: opts.projectRoot,
24106
+ sessionId: opts.sessionId,
23490
24107
  parallelSlots: opts.parallelSlots,
23491
24108
  taskTimeoutMs: opts.taskTimeoutMs,
23492
24109
  taskIdleTimeoutMs: opts.taskIdleTimeoutMs,
@@ -23509,6 +24126,7 @@ function startSddRun(opts) {
23509
24126
  tracker: opts.tracker,
23510
24127
  events: opts.events,
23511
24128
  store: opts.boardStore,
24129
+ sessionId: opts.sessionId,
23512
24130
  specId: opts.graph.specId,
23513
24131
  defaultModel: opts.defaultModel,
23514
24132
  defaultProvider: opts.defaultProvider,
@@ -23585,6 +24203,7 @@ function startSddRun(opts) {
23585
24203
  }
23586
24204
 
23587
24205
  // src/sdd/task-flow.ts
24206
+ init_errors();
23588
24207
  var TaskFlow = class {
23589
24208
  constructor(opts) {
23590
24209
  this.opts = opts;
@@ -23793,7 +24412,7 @@ var TaskGraphStore = class {
23793
24412
  indexPath;
23794
24413
  constructor(opts) {
23795
24414
  this.baseDir = opts.baseDir;
23796
- this.indexPath = path3.join(this.baseDir, "_index.json");
24415
+ this.indexPath = path4.join(this.baseDir, "_index.json");
23797
24416
  }
23798
24417
  async save(graph) {
23799
24418
  await ensureDir(this.baseDir);
@@ -23831,7 +24450,7 @@ var TaskGraphStore = class {
23831
24450
  }
23832
24451
  }
23833
24452
  filePath(id) {
23834
- return path3.join(this.baseDir, `${id}.json`);
24453
+ return path4.join(this.baseDir, `${id}.json`);
23835
24454
  }
23836
24455
  async readIndex() {
23837
24456
  try {
@@ -23872,6 +24491,7 @@ var TaskGraphStore = class {
23872
24491
  };
23873
24492
 
23874
24493
  // src/sdd/task-visualizer.ts
24494
+ init_string();
23875
24495
  var STATUS_ICON = {
23876
24496
  pending: "\u25CB",
23877
24497
  in_progress: "\u25D0",
@@ -24027,7 +24647,7 @@ function makeCommandVerifier(options = {}) {
24027
24647
  return async function verifyTask(info) {
24028
24648
  const cmd = info.task.metadata?.[metadataKey];
24029
24649
  if (typeof cmd !== "string" || !cmd.trim()) return { ok: true };
24030
- return await new Promise((resolve11) => {
24650
+ return await new Promise((resolve12) => {
24031
24651
  const isWindows = process.platform === "win32";
24032
24652
  const [shell, ...shellArgs] = isWindows ? ["cmd", "/d", "/c"] : ["sh", "-c"];
24033
24653
  const child = spawn(shell, [...shellArgs, cmd], {
@@ -24040,24 +24660,25 @@ function makeCommandVerifier(options = {}) {
24040
24660
  const timer = setTimeout(() => {
24041
24661
  timedOut = true;
24042
24662
  child.kill();
24043
- resolve11({ ok: false, reason: `verification timed out: ${cmd}` });
24663
+ resolve12({ ok: false, reason: `verification timed out: ${cmd}` });
24044
24664
  }, timeoutMs);
24045
24665
  child.on("exit", (code) => {
24046
24666
  clearTimeout(timer);
24047
24667
  if (timedOut) return;
24048
- resolve11(
24668
+ resolve12(
24049
24669
  code === 0 ? { ok: true } : { ok: false, reason: `verification failed (exit ${code}): ${cmd}` }
24050
24670
  );
24051
24671
  });
24052
24672
  child.on("error", (err) => {
24053
24673
  clearTimeout(timer);
24054
24674
  if (timedOut) return;
24055
- resolve11({ ok: false, reason: `verification spawn error: ${String(err)}` });
24675
+ resolve12({ ok: false, reason: `verification spawn error: ${String(err)}` });
24056
24676
  });
24057
24677
  });
24058
24678
  };
24059
24679
  }
24060
24680
  init_atomic_write();
24681
+ init_glob_match();
24061
24682
 
24062
24683
  // src/utils/lru-cache.ts
24063
24684
  var LruCache = class {
@@ -24093,6 +24714,10 @@ var LruCache = class {
24093
24714
  return this.store.size;
24094
24715
  }
24095
24716
  };
24717
+
24718
+ // src/security/permission-policy.ts
24719
+ init_safe_json();
24720
+ init_tool_subject();
24096
24721
  var DESTRUCTIVE_BASH_PATTERNS = [
24097
24722
  /\bgit\s+(?:clean\s+-[^\s]*[xdf]|reset\s+--hard)\b/i,
24098
24723
  /\b(?:drop|truncate)\s+(?:table|database|schema)\b/i,
@@ -24115,9 +24740,9 @@ function getInputString(input, key) {
24115
24740
  function pathLooksInsideProject(rawPath, projectRoot) {
24116
24741
  if (!projectRoot) return false;
24117
24742
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
24118
- const resolved = path3.resolve(projectRoot, rawPath);
24119
- const relative3 = path3.relative(projectRoot, resolved);
24120
- return !!relative3 && !relative3.startsWith("..") && !path3.isAbsolute(relative3);
24743
+ const resolved = path4.resolve(projectRoot, rawPath);
24744
+ const relative4 = path4.relative(projectRoot, resolved);
24745
+ return !!relative4 && !relative4.startsWith("..") && !path4.isAbsolute(relative4);
24121
24746
  }
24122
24747
  function tokenizeShell(command) {
24123
24748
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -24127,7 +24752,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
24127
24752
  if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
24128
24753
  if (token.includes("*")) return true;
24129
24754
  if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
24130
- if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
24755
+ if (path4.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
24131
24756
  return false;
24132
24757
  }
24133
24758
  function hasDangerousDeleteTarget(tokens, start, projectRoot) {
@@ -24597,13 +25222,27 @@ var PATTERNS = [
24597
25222
  // Min 12 chars: some OAuth providers issue shorter-lived tokens (< 20
24598
25223
  // chars). A 12-char base64 string has ~71 bits of entropy — above the
24599
25224
  // threshold where random strings are unlikely to produce false matches.
24600
- regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?:$|[^A-Za-z0-9_.~+/-])/g
25225
+ // The trailing boundary is a NON-consuming lookahead: two adjacent bearer
25226
+ // tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
25227
+ // redacted. A consuming trailing delimiter would eat the separator the
25228
+ // next match needs for its leading anchor, leaking the second token.
25229
+ regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g
24601
25230
  },
24602
25231
  {
24603
25232
  type: "high_entropy_env",
24604
25233
  // Anchored with alternation instead of lookbehind to avoid backtracking.
24605
25234
  // Value bounded at 512 chars.
24606
- regex: /(?:^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?:\s|$)/g
25235
+ // The trailing boundary is a NON-consuming lookahead so two secrets
25236
+ // separated by a single delimiter (one space OR one newline, e.g.
25237
+ // `printenv` / `.env` dumps: `API_KEY=… \n SESSION_TOKEN=…`) are BOTH
25238
+ // redacted. A consuming trailing `\s` would swallow the separator the
25239
+ // next match needs for its leading anchor, so every other secret would
25240
+ // leak in plaintext.
25241
+ // The leading delimiter is CAPTURED (group 1) and re-emitted by the
25242
+ // replacement so the separator between adjacent secrets is preserved
25243
+ // rather than collapsed. Capture groups are therefore: 1=leading
25244
+ // delimiter, 2=key name, 3=value.
25245
+ regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g
24607
25246
  }
24608
25247
  ];
24609
25248
  var SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== "high_entropy_env");
@@ -24611,6 +25250,7 @@ var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`
24611
25250
  var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
24612
25251
  var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
24613
25252
  var SCRUB_CHUNK_BYTES = 64 * 1024;
25253
+ var SCRUB_OVERLAP_BYTES = 1024;
24614
25254
  function hasCredentialAnchors(text) {
24615
25255
  return text.includes("-----BEGIN") || // Private keys (most unique → cheap reject)
24616
25256
  text.includes("sk-") || // Anthropic + OpenAI keys
@@ -24645,8 +25285,16 @@ var DefaultSecretScrubber = class {
24645
25285
  while (i < text.length) {
24646
25286
  let end = Math.min(i + SCRUB_CHUNK_BYTES, text.length);
24647
25287
  if (end < text.length) {
24648
- const nl = text.lastIndexOf("\n", end);
24649
- if (nl > i + SCRUB_CHUNK_BYTES / 2) end = nl + 1;
25288
+ const limit = Math.min(end + SCRUB_OVERLAP_BYTES, text.length);
25289
+ let safe = -1;
25290
+ for (let j = end; j < limit; j++) {
25291
+ const ch = text.charCodeAt(j);
25292
+ if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {
25293
+ safe = j;
25294
+ break;
25295
+ }
25296
+ }
25297
+ end = safe === -1 ? end : safe + 1;
24650
25298
  }
24651
25299
  out.push(this.scrubOne(text.slice(i, end)));
24652
25300
  i = end;
@@ -24664,8 +25312,8 @@ var DefaultSecretScrubber = class {
24664
25312
  return replacement !== void 0 ? replacement : match;
24665
25313
  }
24666
25314
  );
24667
- out = out.replace(HIGH_ENTROPY_REGEX, (_match, group1, _group2) => {
24668
- return `${group1}=[REDACTED:high_entropy_env]`;
25315
+ out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
25316
+ return `${lead}${key}=[REDACTED:high_entropy_env]`;
24669
25317
  });
24670
25318
  return out;
24671
25319
  }
@@ -24694,6 +25342,9 @@ var DefaultSecretScrubber = class {
24694
25342
  }
24695
25343
  };
24696
25344
 
25345
+ // src/security/secret-vault.ts
25346
+ init_errors();
25347
+
24697
25348
  // src/types/secret-vault.ts
24698
25349
  var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
24699
25350
  function encryptedPrefixForVersion(version) {
@@ -24702,6 +25353,7 @@ function encryptedPrefixForVersion(version) {
24702
25353
 
24703
25354
  // src/security/secret-vault.ts
24704
25355
  init_atomic_write();
25356
+ init_deep_merge();
24705
25357
  var KEY_BYTES = 32;
24706
25358
  var IV_BYTES = 12;
24707
25359
  var TAG_BYTES = 16;
@@ -24789,8 +25441,8 @@ function unwrapDataKey(buf, keyFile) {
24789
25441
  function checkKeyFilePermissions(keyFile) {
24790
25442
  if (process.platform === "win32") return;
24791
25443
  try {
24792
- const stat9 = fs11.statSync(keyFile);
24793
- const actualMode = stat9.mode & 511;
25444
+ const stat10 = fs12.statSync(keyFile);
25445
+ const actualMode = stat10.mode & 511;
24794
25446
  if (actualMode !== KEY_FILE_MODE) {
24795
25447
  console.warn(JSON.stringify({
24796
25448
  level: "warn",
@@ -24878,16 +25530,16 @@ var DefaultSecretVault = class {
24878
25530
  const oldVersion = this._keyVersion;
24879
25531
  const newKey = randomBytes(KEY_BYTES);
24880
25532
  const newVersion = oldVersion + 1;
24881
- fs11.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
25533
+ fs12.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
24882
25534
  const passphrase = getVaultPassphrase();
24883
25535
  if (passphrase) {
24884
- fs11.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
25536
+ fs12.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
24885
25537
  } else {
24886
25538
  const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
24887
25539
  KEY_FILE_MAGIC.copy(keyFileBuf, 0);
24888
25540
  keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
24889
25541
  newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
24890
- fs11.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
25542
+ fs12.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
24891
25543
  }
24892
25544
  checkKeyFilePermissions(this.keyFile);
24893
25545
  this.key = newKey;
@@ -24905,7 +25557,7 @@ var DefaultSecretVault = class {
24905
25557
  const passphrase = getVaultPassphrase();
24906
25558
  if (!passphrase || !this.key) return;
24907
25559
  try {
24908
- fs11.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
25560
+ fs12.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
24909
25561
  mode: 384
24910
25562
  });
24911
25563
  checkKeyFilePermissions(this.keyFile);
@@ -24915,7 +25567,7 @@ var DefaultSecretVault = class {
24915
25567
  loadOrCreateKey() {
24916
25568
  if (this.key) return this.key;
24917
25569
  try {
24918
- const buf = fs11.readFileSync(this.keyFile);
25570
+ const buf = fs12.readFileSync(this.keyFile);
24919
25571
  if (isWrappedKeyFile(buf)) {
24920
25572
  const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
24921
25573
  this.key = key2;
@@ -24962,15 +25614,15 @@ var DefaultSecretVault = class {
24962
25614
  } catch (err) {
24963
25615
  if (err.code !== "ENOENT") throw err;
24964
25616
  }
24965
- fs11.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
25617
+ fs12.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
24966
25618
  const key = randomBytes(KEY_BYTES);
24967
25619
  const passphrase = getVaultPassphrase();
24968
25620
  const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
24969
25621
  try {
24970
- fs11.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
25622
+ fs12.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
24971
25623
  } catch (err) {
24972
25624
  if (err.code !== "EEXIST") throw err;
24973
- const buf = fs11.readFileSync(this.keyFile);
25625
+ const buf = fs12.readFileSync(this.keyFile);
24974
25626
  if (isWrappedKeyFile(buf)) {
24975
25627
  const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
24976
25628
  this.key = winnerKey;
@@ -25061,7 +25713,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
25061
25713
  }
25062
25714
  const merged = deepMerge(current, patch ?? {});
25063
25715
  const encrypted = encryptConfigSecrets(merged, vault);
25064
- await fsp7.mkdir(path3.dirname(configPath), { recursive: true });
25716
+ await fsp7.mkdir(path4.dirname(configPath), { recursive: true });
25065
25717
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
25066
25718
  await restrictFilePermissions(configPath);
25067
25719
  }
@@ -25164,7 +25816,7 @@ var DefaultAttachmentStore = class {
25164
25816
  let data = input.data;
25165
25817
  if (this.spoolDir && bytes >= this.spoolThreshold) {
25166
25818
  await fsp7.mkdir(this.spoolDir, { recursive: true });
25167
- spooledPath = path3.join(this.spoolDir, `${id}.bin`);
25819
+ spooledPath = path4.join(this.spoolDir, `${id}.bin`);
25168
25820
  await atomicWrite(spooledPath, input.data, {
25169
25821
  encoding: input.kind === "image" ? "base64" : "utf8"
25170
25822
  });
@@ -25282,6 +25934,7 @@ function mergeAdjacentText(blocks) {
25282
25934
  return out;
25283
25935
  }
25284
25936
  init_atomic_write();
25937
+ init_error();
25285
25938
 
25286
25939
  // src/types/context-window.ts
25287
25940
  var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
@@ -25338,6 +25991,9 @@ function isContextWindowModeId(id) {
25338
25991
  var DEFAULT_TUI_THINKING_WORD = "thinking";
25339
25992
 
25340
25993
  // src/storage/config-loader.ts
25994
+ init_errors();
25995
+ init_safe_json();
25996
+ init_deep_merge();
25341
25997
  function storageErrorString(err) {
25342
25998
  if (err instanceof Error) {
25343
25999
  const code = err.code;
@@ -25656,8 +26312,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
25656
26312
  return out;
25657
26313
  }
25658
26314
  function samePath(a, b) {
25659
- let ra = path3.resolve(a);
25660
- let rb = path3.resolve(b);
26315
+ let ra = path4.resolve(a);
26316
+ let rb = path4.resolve(b);
25661
26317
  if (process.platform === "win32" || process.platform === "darwin") {
25662
26318
  ra = ra.toLowerCase();
25663
26319
  rb = rb.toLowerCase();
@@ -25944,8 +26600,8 @@ var DefaultConfigLoader = class {
25944
26600
  const t0 = Date.now();
25945
26601
  let mtimeMs = null;
25946
26602
  try {
25947
- const stat9 = await fsp7.stat(file);
25948
- mtimeMs = stat9.mtimeMs;
26603
+ const stat10 = await fsp7.stat(file);
26604
+ mtimeMs = stat10.mtimeMs;
25949
26605
  const cached = this.jsonCache.get(file);
25950
26606
  if (cached && cached.mtimeMs === mtimeMs) {
25951
26607
  return structuredClone(cached.value);
@@ -26138,6 +26794,8 @@ function runConfigMigrations(input, targetVersion, migrations) {
26138
26794
  var DEFAULT_CONFIG_MIGRATIONS = [];
26139
26795
 
26140
26796
  // src/storage/config-store.ts
26797
+ init_errors();
26798
+ init_error();
26141
26799
  function stripEphemeralFields(cfg) {
26142
26800
  const env = cfg._envSource;
26143
26801
  if (!env?.size) return cfg;
@@ -26208,6 +26866,9 @@ function deepFreeze(obj) {
26208
26866
  return Object.freeze(obj);
26209
26867
  }
26210
26868
 
26869
+ // src/storage/memory-store.ts
26870
+ init_error();
26871
+
26211
26872
  // src/types/memory.ts
26212
26873
  var MEMORY_TYPE_LABELS = {
26213
26874
  fact: "Fact",
@@ -26288,9 +26949,80 @@ function isMemoryType(s) {
26288
26949
  function isPriority(s) {
26289
26950
  return s === "critical" || s === "high" || s === "medium" || s === "low";
26290
26951
  }
26952
+ function buildInvertedIndex(entries) {
26953
+ const wordMap = /* @__PURE__ */ new Map();
26954
+ const tagMap = /* @__PURE__ */ new Map();
26955
+ const indexed = new Array(entries.length);
26956
+ for (let i = 0; i < entries.length; i++) {
26957
+ const e = entries[i];
26958
+ const words = e.text.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
26959
+ const tags = (e.tags ?? []).map((t) => t.toLowerCase());
26960
+ indexed[i] = { entry: e, words, tags };
26961
+ for (const w of words) {
26962
+ const arr = wordMap.get(w);
26963
+ if (arr) arr.push(i);
26964
+ else wordMap.set(w, [i]);
26965
+ }
26966
+ for (const t of tags) {
26967
+ const arr = tagMap.get(t);
26968
+ if (arr) arr.push(i);
26969
+ else tagMap.set(t, [i]);
26970
+ }
26971
+ }
26972
+ return { wordMap, tagMap, entries: indexed, mtimeMs: 0 };
26973
+ }
26974
+ var MIN_SUBSTRING_NEEDLE_LEN = 3;
26975
+ function searchIndex(index, query, limit) {
26976
+ const needles = query.toLowerCase().split(/\s+/).filter((n) => n.length > 0);
26977
+ if (needles.length === 0) return [];
26978
+ const scores = /* @__PURE__ */ new Map();
26979
+ for (const n of needles) {
26980
+ let matched = false;
26981
+ const wordExact = index.wordMap.get(n);
26982
+ if (wordExact) {
26983
+ for (const idx of wordExact) scores.set(idx, (scores.get(idx) ?? 0) + 1);
26984
+ matched = true;
26985
+ }
26986
+ const tagExact = index.tagMap.get(n);
26987
+ if (tagExact) {
26988
+ for (const idx of tagExact) scores.set(idx, (scores.get(idx) ?? 0) + 2);
26989
+ matched = true;
26990
+ }
26991
+ if (matched || n.length < MIN_SUBSTRING_NEEDLE_LEN) continue;
26992
+ for (const [word, indices] of index.wordMap) {
26993
+ if (word.includes(n) || n.includes(word)) {
26994
+ for (const idx of indices) {
26995
+ scores.set(idx, (scores.get(idx) ?? 0) + 1);
26996
+ }
26997
+ }
26998
+ }
26999
+ for (const [tag, indices] of index.tagMap) {
27000
+ if (tag.includes(n) || n.includes(tag)) {
27001
+ for (const idx of indices) {
27002
+ scores.set(idx, (scores.get(idx) ?? 0) + 2);
27003
+ }
27004
+ }
27005
+ }
27006
+ }
27007
+ if (scores.size === 0) return [];
27008
+ const scored = Array.from(scores.entries());
27009
+ scored.sort((a, b) => b[1] - a[1]);
27010
+ const result = [];
27011
+ const max = limit ? Math.min(limit, scored.length) : scored.length;
27012
+ for (let i = 0; i < max; i++) {
27013
+ result.push(index.entries[scored[i][0]].entry);
27014
+ }
27015
+ return result;
27016
+ }
26291
27017
  var FileMemoryBackend = class {
26292
27018
  kind = "file";
26293
27019
  files;
27020
+ /** Cache of parsed entries per file path. */
27021
+ entryCache = /* @__PURE__ */ new Map();
27022
+ /** Inverted index per file path. */
27023
+ indexCache = /* @__PURE__ */ new Map();
27024
+ /** File mtime cache for invalidation. */
27025
+ mtimeCache = /* @__PURE__ */ new Map();
26294
27026
  constructor(opts) {
26295
27027
  this.files = {
26296
27028
  "project-agents": opts.paths.inProjectAgentsFile,
@@ -26301,9 +27033,56 @@ var FileMemoryBackend = class {
26301
27033
  resolveFile(filePath, scope) {
26302
27034
  return filePath || this.files[scope];
26303
27035
  }
27036
+ async getMtime(file) {
27037
+ try {
27038
+ const stat10 = await fsp7.stat(file);
27039
+ return stat10.mtimeMs;
27040
+ } catch {
27041
+ return 0;
27042
+ }
27043
+ }
27044
+ invalidateCache(file) {
27045
+ this.entryCache.delete(file);
27046
+ this.indexCache.delete(file);
27047
+ this.mtimeCache.delete(file);
27048
+ }
27049
+ /**
27050
+ * Load (and cache) the parsed entries for a file. Callers that have already
27051
+ * stat'd the file this tick (e.g. `getIndex`) can pass the known `mtime` to
27052
+ * avoid a redundant `fs.stat` — otherwise it's fetched here.
27053
+ */
27054
+ async loadEntries(file, scope, mtime) {
27055
+ const resolvedMtime = mtime ?? await this.getMtime(file);
27056
+ const cachedMtime = this.mtimeCache.get(file);
27057
+ if (cachedMtime === resolvedMtime && this.entryCache.has(file)) {
27058
+ return this.entryCache.get(file);
27059
+ }
27060
+ const raw = await this.readAll(scope, file);
27061
+ if (!raw.trim()) {
27062
+ this.entryCache.set(file, []);
27063
+ this.mtimeCache.set(file, resolvedMtime);
27064
+ return [];
27065
+ }
27066
+ const entries = parseEntries(raw, scope);
27067
+ this.entryCache.set(file, entries);
27068
+ this.mtimeCache.set(file, resolvedMtime);
27069
+ return entries;
27070
+ }
27071
+ async getIndex(file, scope) {
27072
+ const mtime = await this.getMtime(file);
27073
+ const cached = this.indexCache.get(file);
27074
+ if (cached && cached.mtimeMs === mtime) {
27075
+ return cached;
27076
+ }
27077
+ const entries = await this.loadEntries(file, scope, mtime);
27078
+ const index = buildInvertedIndex(entries);
27079
+ index.mtimeMs = mtime;
27080
+ this.indexCache.set(file, index);
27081
+ return index;
27082
+ }
26304
27083
  async remember(scope, entry, filePath) {
26305
27084
  const file = this.resolveFile(filePath, scope);
26306
- await ensureDir(path3.dirname(file));
27085
+ await ensureDir(path4.dirname(file));
26307
27086
  let existing = "";
26308
27087
  try {
26309
27088
  existing = await fsp7.readFile(file, "utf8");
@@ -26317,6 +27096,7 @@ var FileMemoryBackend = class {
26317
27096
  const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# Agent Memory
26318
27097
  ${line}`;
26319
27098
  await atomicWrite(file, next);
27099
+ this.invalidateCache(file);
26320
27100
  }
26321
27101
  async forget(scope, query, filePath) {
26322
27102
  const file = this.resolveFile(filePath, scope);
@@ -26353,6 +27133,7 @@ ${line}`;
26353
27133
  await atomicWrite(file, lines.join("\n"));
26354
27134
  }
26355
27135
  }
27136
+ this.invalidateCache(file);
26356
27137
  return removed;
26357
27138
  });
26358
27139
  }
@@ -26365,30 +27146,19 @@ ${line}`;
26365
27146
  }
26366
27147
  }
26367
27148
  async list(scope, filePath, limit) {
26368
- const raw = await this.readAll(scope, filePath);
26369
- if (!raw.trim()) return [];
26370
- const entries = parseEntries(raw, scope);
27149
+ const file = this.resolveFile(filePath, scope);
27150
+ const entries = await this.loadEntries(file, scope);
26371
27151
  return limit ? entries.slice(0, limit) : entries;
26372
27152
  }
26373
27153
  async search(scope, query, filePath, limit) {
26374
- const entries = await this.list(scope, filePath);
26375
- const needle = query.toLowerCase().split(/\s+/);
26376
- const scored = entries.map((e) => {
26377
- const words = e.text.toLowerCase().split(/\s+/);
26378
- let score = 0;
26379
- for (const n of needle) {
26380
- if (words.some((w) => w.includes(n))) score += 1;
26381
- if (e.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
26382
- }
26383
- return { entry: e, score };
26384
- });
26385
- scored.sort((a, b) => b.score - a.score);
26386
- const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
26387
- return limit ? matched.slice(0, limit) : matched;
27154
+ const file = this.resolveFile(filePath, scope);
27155
+ const index = await this.getIndex(file, scope);
27156
+ return searchIndex(index, query, limit);
26388
27157
  }
26389
27158
  async clear(scope, filePath) {
26390
27159
  const file = this.resolveFile(filePath, scope);
26391
27160
  await atomicWrite(file, "");
27161
+ this.invalidateCache(file);
26392
27162
  }
26393
27163
  async consolidate(scope, filePath) {
26394
27164
  const file = this.resolveFile(filePath, scope);
@@ -26423,18 +27193,19 @@ ${line}`;
26423
27193
  } catch {
26424
27194
  return 0;
26425
27195
  }
27196
+ this.invalidateCache(file);
26426
27197
  return removed;
26427
27198
  }
26428
27199
  };
26429
27200
  async function pruneConsolidateBackups(file) {
26430
- const dir = path3.dirname(file);
26431
- const base = path3.basename(file);
27201
+ const dir = path4.dirname(file);
27202
+ const base = path4.basename(file);
26432
27203
  const prefix = `${base}.bak.`;
26433
27204
  const backups = (await fsp7.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
26434
27205
  await Promise.all(
26435
27206
  backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {
26436
27207
  try {
26437
- await fsp7.unlink(path3.join(dir, name));
27208
+ await fsp7.unlink(path4.join(dir, name));
26438
27209
  } catch {
26439
27210
  }
26440
27211
  })
@@ -26489,9 +27260,10 @@ var DefaultMemoryStore = class {
26489
27260
  /** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
26490
27261
  _scoreCache = /* @__PURE__ */ new Map();
26491
27262
  /**
26492
- * Per-entry cached lowercase strings — computed once per scoreRelevant() call,
26493
- * stored here so repeated scoring of the same entries avoids re-computation.
26494
- * Cleared on every mutation (remember/forget/consolidate/clear).
27263
+ * Per-entry cached lowercase strings — lazily allocated once and reused
27264
+ * across scoreRelevant() calls so repeated scoring of the same entries skips
27265
+ * re-lowercasing. Keyed by entry object identity; cleared (set to null) on
27266
+ * every mutation (remember/forget/consolidate/clear) via invalidateScoreCaches().
26495
27267
  */
26496
27268
  _cachedLower = null;
26497
27269
  constructor(opts) {
@@ -26641,7 +27413,7 @@ ${body.trim()}`);
26641
27413
  const t0 = Date.now();
26642
27414
  try {
26643
27415
  await this.backend.remember(scope, entry, filePath);
26644
- this._scoreCache.clear();
27416
+ this.invalidateScoreCaches();
26645
27417
  const dur = Date.now() - t0;
26646
27418
  this.events?.emit("storage.write", {
26647
27419
  sessionId: "~memory~",
@@ -26692,6 +27464,15 @@ ${body.trim()}`);
26692
27464
  });
26693
27465
  });
26694
27466
  }
27467
+ /**
27468
+ * Drop the relevance caches after a mutation. Both the per-context score
27469
+ * cache and the per-entry lowercase cache are keyed on entry objects that a
27470
+ * mutation invalidates, so they must be cleared together when entries change.
27471
+ */
27472
+ invalidateScoreCaches() {
27473
+ this._scoreCache.clear();
27474
+ this._cachedLower = null;
27475
+ }
26695
27476
  /**
26696
27477
  * Score and rank memories by relevance to the current context.
26697
27478
  * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
@@ -26709,18 +27490,19 @@ ${body.trim()}`);
26709
27490
  const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
26710
27491
  const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
26711
27492
  const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
26712
- this._cachedLower = /* @__PURE__ */ new WeakMap();
27493
+ this._cachedLower ??= /* @__PURE__ */ new WeakMap();
27494
+ const lowerCache = this._cachedLower;
26713
27495
  const scored = [];
26714
27496
  for (const entry of all) {
26715
27497
  let score = 0;
26716
27498
  const reasons = [];
26717
- let cachedLower = this._cachedLower.get(entry);
27499
+ let cachedLower = lowerCache.get(entry);
26718
27500
  if (!cachedLower) {
26719
27501
  cachedLower = {
26720
27502
  textLower: entry.text.toLowerCase(),
26721
27503
  tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
26722
27504
  };
26723
- this._cachedLower.set(entry, cachedLower);
27505
+ lowerCache.set(entry, cachedLower);
26724
27506
  }
26725
27507
  const { textLower, tagsLower } = cachedLower;
26726
27508
  let taskHits = 0;
@@ -26818,7 +27600,7 @@ ${body.trim()}`);
26818
27600
  let removed = 0;
26819
27601
  try {
26820
27602
  removed = await this.backend.forget(scope, query, filePath);
26821
- this._scoreCache.clear();
27603
+ this.invalidateScoreCaches();
26822
27604
  const dur = Date.now() - t0;
26823
27605
  this.events?.emit("storage.write", {
26824
27606
  sessionId: "~memory~",
@@ -26862,7 +27644,7 @@ ${body.trim()}`);
26862
27644
  let removed = 0;
26863
27645
  try {
26864
27646
  removed = await this.backend.consolidate(scope, filePath);
26865
- this._scoreCache.clear();
27647
+ this.invalidateScoreCaches();
26866
27648
  const dur = Date.now() - t0;
26867
27649
  this.events?.emit("storage.write", {
26868
27650
  sessionId: "~memory~",
@@ -26904,7 +27686,7 @@ ${body.trim()}`);
26904
27686
  const t0 = Date.now();
26905
27687
  try {
26906
27688
  await this.backend.clear(scope, filePath);
26907
- this._scoreCache.clear();
27689
+ this.invalidateScoreCaches();
26908
27690
  const dur = Date.now() - t0;
26909
27691
  this.events?.emit("storage.write", {
26910
27692
  sessionId: "~memory~",
@@ -26971,6 +27753,7 @@ ${body.trim()}`);
26971
27753
  })
26972
27754
  )
26973
27755
  );
27756
+ this.invalidateScoreCaches();
26974
27757
  }
26975
27758
  /**
26976
27759
  * Return a new MemoryStore proxy that carries `traceId` on every storage
@@ -27009,6 +27792,7 @@ function labelOf(scope) {
27009
27792
 
27010
27793
  // src/storage/plan-store.ts
27011
27794
  init_atomic_write();
27795
+ init_error();
27012
27796
  async function loadPlan(filePath, events) {
27013
27797
  const t0 = Date.now();
27014
27798
  let raw;
@@ -27304,6 +28088,7 @@ ${cat}:`);
27304
28088
 
27305
28089
  // src/storage/queue-store.ts
27306
28090
  init_atomic_write();
28091
+ init_error();
27307
28092
  var QueueStore = class {
27308
28093
  file;
27309
28094
  // Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't
@@ -27311,7 +28096,7 @@ var QueueStore = class {
27311
28096
  events;
27312
28097
  traceId;
27313
28098
  constructor(opts) {
27314
- this.file = path3.join(opts.dir, "queue.json");
28099
+ this.file = path4.join(opts.dir, "queue.json");
27315
28100
  this.events = opts.events;
27316
28101
  this.traceId = opts.traceId;
27317
28102
  }
@@ -27488,7 +28273,7 @@ var RecoveryLock = class {
27488
28273
  sessionStore;
27489
28274
  probe;
27490
28275
  constructor(opts) {
27491
- this.file = path3.join(opts.dir, LOCK_FILE);
28276
+ this.file = path4.join(opts.dir, LOCK_FILE);
27492
28277
  this.pid = opts.pid ?? process.pid;
27493
28278
  this.hostname = opts.hostname ?? os.hostname();
27494
28279
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -27549,7 +28334,7 @@ var RecoveryLock = class {
27549
28334
  * null return before calling this.
27550
28335
  */
27551
28336
  async write(sessionId) {
27552
- await ensureDir(path3.dirname(this.file));
28337
+ await ensureDir(path4.dirname(this.file));
27553
28338
  const lock = {
27554
28339
  v: 1,
27555
28340
  sessionId,
@@ -27829,6 +28614,11 @@ function resolveSessionLoggingConfig(cfg) {
27829
28614
  }
27830
28615
  };
27831
28616
  }
28617
+
28618
+ // src/storage/session-reader.ts
28619
+ init_expect_defined();
28620
+ init_regex_guard();
28621
+ init_session_scoped_path();
27832
28622
  var DefaultSessionReader = class _DefaultSessionReader {
27833
28623
  store;
27834
28624
  eventCache = /* @__PURE__ */ new Map();
@@ -27843,11 +28633,11 @@ var DefaultSessionReader = class _DefaultSessionReader {
27843
28633
  if (!rootDir) {
27844
28634
  return await this.store.load(sessionId);
27845
28635
  }
27846
- const sessionPath = path3.join(rootDir, `${sessionId}.jsonl`);
28636
+ const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
27847
28637
  let mtimeMs = null;
27848
28638
  try {
27849
- const stat9 = await fsp7.stat(sessionPath);
27850
- mtimeMs = stat9.mtimeMs;
28639
+ const stat10 = await fsp7.stat(sessionPath);
28640
+ mtimeMs = stat10.mtimeMs;
27851
28641
  } catch {
27852
28642
  this.eventCache.delete(sessionId);
27853
28643
  this.eventCacheMtimes.delete(sessionId);
@@ -28206,6 +28996,7 @@ function renderPlainText(meta, events) {
28206
28996
 
28207
28997
  // src/storage/todos-checkpoint.ts
28208
28998
  init_atomic_write();
28999
+ init_error();
28209
29000
  async function loadTodosCheckpoint(filePath, events, traceId) {
28210
29001
  const t0 = Date.now();
28211
29002
  let raw;
@@ -28346,6 +29137,6 @@ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
28346
29137
  };
28347
29138
  }
28348
29139
 
28349
- export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CODEX_MODELS, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultDesignKitLoader, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultPromptLoader, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddBoardProjector, SddBoardStore, SddInterviewDriver, SddParallelRun, SddRunRegistry, SddSupervisor, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, _resetDesignKitLoaderMemo, _resetDesignRulesCache, activateDesign, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, applySddLifecycle, applyTokenOverrides, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBoardSnapshot, buildBoardTasks, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, cleanupSddWorktrees, cleanupStaleSddWorktrees, clearActiveKit, clearPersistedActiveKit, clearPlan, codexModelMeta, colorToHex, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, describeCatalogModel, designProjectDir, destroySddProject, detectFrontendFile, detectFrontendIntent, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, extractVerificationCommand, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getDesignKitLoader, getDesignState, getPlanTemplate, getTemplate, githubServer, googleMapsServer, hasConflictMarkers, installDesignStudioMiddleware, isColorToken, isExplanatoryText, listPlanTemplates, listTemplates, loadActiveKit, loadDirectorState, loadPlan, loadProjectDesignRules, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeCommandVerifier, makeDesignDetectToolCallMiddleware, makeDesignDetectUserInputMiddleware, makeDesignStudioRequestMiddleware, makeDesignVerifyToolCallMiddleware, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeLlmConflictResolver, makeLlmSubtaskGenerator, makePreferSideConflictResolver, makeRollUpTool, makeSpawnTool, makeTerminateTool, materializeTokens, migratePlaintextSecrets, miniMaxVisionServer, oklchToHex, parseOklch, playwrightServer, recordKitChoice, recordOverrides, removePlanItem, renderProgress, renderPrometheus, renderPrompt, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveBundledDesignKitsDir, resolveConflictText, resolveProviderModelList, resolveSessionLoggingConfig, rewriteConfigEncrypted, rollbackSddRunFromDisk, rosterSummaryFromConfigs, runConfigMigrations, runDesignVerify, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setActiveKit, setDesignOverrides, setPlanItemStatus, shortIdMap, slackServer, sshManagerServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startSddRun, templateToMarkdown, verifyFiles, wireMetricsToEvents, zaiVisionServer };
29140
+ export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CODEX_MODELS, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultDesignKitLoader, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultPromptLoader, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddBoardProjector, SddBoardStore, SddInterviewDriver, SddParallelRun, SddRunRegistry, SddSupervisor, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, _resetDesignKitLoaderMemo, _resetDesignRulesCache, activateDesign, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, applySddLifecycle, applyTokenOverrides, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBoardSnapshot, buildBoardTasks, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, cleanupSddWorktrees, cleanupStaleSddWorktrees, clearActiveKit, clearPersistedActiveKit, clearPlan, codexModelMeta, colorToHex, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, describeCatalogModel, designProjectDir, destroySddProject, detectFrontendFile, detectFrontendIntent, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, extractVerificationCommand, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getDesignKitLoader, getDesignState, getPlanTemplate, getTemplate, githubServer, googleMapsServer, hasConflictMarkers, installDesignStudioMiddleware, isColorToken, isExplanatoryText, listPlanTemplates, listTemplates, loadActiveKit, loadDirectorState, loadPlan, loadProjectDesignRules, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeCommandVerifier, makeDesignDetectToolCallMiddleware, makeDesignDetectUserInputMiddleware, makeDesignStudioRequestMiddleware, makeDesignVerifyToolCallMiddleware, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetTool, makeLLMClassifier, makeLlmConflictResolver, makeLlmSubtaskGenerator, makePreferSideConflictResolver, makeRollUpTool, makeSpawnTool, makeTerminateTool, materializeTokens, migratePlaintextSecrets, miniMaxVisionServer, oklchToHex, parseOklch, playwrightServer, recordKitChoice, recordOverrides, removePlanItem, renderProgress, renderPrometheus, renderPrompt, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveBundledDesignKitsDir, resolveConflictText, resolveProviderModelList, resolveSessionLoggingConfig, rewriteConfigEncrypted, rollbackSddRunFromDisk, rosterSummaryFromConfigs, runConfigMigrations, runDesignVerify, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setActiveKit, setDesignOverrides, setPlanItemStatus, shortIdMap, slackServer, sshManagerServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startSddRun, templateToMarkdown, verifyFiles, wireMetricsToEvents, zaiVisionServer };
28350
29141
  //# sourceMappingURL=index.js.map
28351
29142
  //# sourceMappingURL=index.js.map