@wrongstack/core 0.265.1 → 0.267.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-STJ3JwwK.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-CzPGP3jA.d.ts} +25 -7
  3. package/dist/{brain-BXd_61kQ.d.ts → brain-Cdg77tVN.d.ts} +73 -1
  4. package/dist/{compactor-B8pOf45Y.d.ts → compactor-iMZ84CXq.d.ts} +19 -1
  5. package/dist/{config-BMCj_XDs.d.ts → config-Du3pYYln.d.ts} +54 -3
  6. package/dist/{context-MRk5PhNv.d.ts → context-dT5Ueund.d.ts} +65 -1
  7. package/dist/coordination/index.d.ts +17 -17
  8. package/dist/coordination/index.js +138 -114
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +1729 -781
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -15
  14. package/dist/execution/index.js +1119 -229
  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 +6 -6
  18. package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-SulMTowG.d.ts} +28 -11
  19. package/dist/{goal-store-DtLMySNb.d.ts → goal-store-CABDwdFE.d.ts} +1 -1
  20. package/dist/{index-CEDeNodM.d.ts → index-Bms0m4oy.d.ts} +5 -5
  21. package/dist/{index-B-ch8K9C.d.ts → index-DtCVWel4.d.ts} +8 -8
  22. package/dist/index-IEuxQd-E.d.ts +82 -0
  23. package/dist/index.d.ts +118 -45
  24. package/dist/index.js +3083 -1602
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/index.d.ts +6 -6
  27. package/dist/infrastructure/index.js +72 -1
  28. package/dist/infrastructure/index.js.map +1 -1
  29. package/dist/kernel/index.d.ts +9 -9
  30. package/dist/kernel/index.js.map +1 -1
  31. package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-C2cBTxUR.d.ts} +3 -3
  32. package/dist/models/index.d.ts +5 -5
  33. package/dist/models/index.js +30 -1
  34. package/dist/models/index.js.map +1 -1
  35. package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-BqGZNJQ-.d.ts} +1 -1
  36. package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-B8R43uPz.d.ts} +1 -1
  37. package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-CnXa5oTH.d.ts} +14 -9
  38. package/dist/observability/index.d.ts +2 -2
  39. package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-DdNnw9BQ.d.ts} +11 -9
  40. package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-COIMLCQL.d.ts} +3 -3
  41. package/dist/{permission-B9SB45lp.d.ts → permission-B75JAi3-.d.ts} +1 -1
  42. package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-DlR9eJAM.d.ts} +2 -2
  43. package/dist/{pipeline-DPDxH_7m.d.ts → pipeline-BfD2k1rT.d.ts} +2 -2
  44. package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-DSIKCXZN.d.ts} +5 -5
  45. package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-BNRsNuJx.d.ts} +40 -3
  46. package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-CX7iIvox.d.ts} +3 -3
  47. package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-BilV1ujH.d.ts} +1 -1
  48. package/dist/sdd/index.d.ts +8 -8
  49. package/dist/sdd/index.js +12 -12
  50. package/dist/sdd/index.js.map +1 -1
  51. package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-gkvEZZfE.d.ts} +2 -2
  52. package/dist/security/index.d.ts +5 -67
  53. package/dist/security/index.js +96 -76
  54. package/dist/security/index.js.map +1 -1
  55. package/dist/{selector-CzHh_igB.d.ts → selector-Bc7eWtT3.d.ts} +1 -1
  56. package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-D-araDEz.d.ts} +1 -1
  57. package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-D7Dapswh.d.ts} +1 -1
  58. package/dist/storage/index.d.ts +11 -11
  59. package/dist/storage/index.js +81 -84
  60. package/dist/storage/index.js.map +1 -1
  61. package/dist/tools/index.d.ts +4 -2
  62. package/dist/tools/index.js.map +1 -1
  63. package/dist/types/index.d.ts +19 -19
  64. package/dist/types/index.js +1265 -400
  65. package/dist/types/index.js.map +1 -1
  66. package/dist/utils/index.d.ts +454 -406
  67. package/dist/utils/index.js +2191 -1201
  68. package/dist/utils/index.js.map +1 -1
  69. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import * as crypto2 from 'crypto';
2
2
  import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash } from 'crypto';
3
3
  import * as fsp2 from 'fs/promises';
4
- import * as path3 from 'path';
4
+ import * as path4 from 'path';
5
5
  import { isAbsolute, resolve } from 'path';
6
6
  import * as fs4 from 'fs';
7
7
  import * as os from 'os';
@@ -33,9 +33,9 @@ __export(atomic_write_exports, {
33
33
  withFileLock: () => withFileLock
34
34
  });
35
35
  async function atomicWrite(targetPath, content, opts = {}) {
36
- const dir = path3.dirname(targetPath);
36
+ const dir = path4.dirname(targetPath);
37
37
  await fsp2.mkdir(dir, { recursive: true });
38
- const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
38
+ const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
39
39
  try {
40
40
  if (typeof content === "string") {
41
41
  await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -74,9 +74,9 @@ async function ensureDir(dir) {
74
74
  await fsp2.mkdir(dir, { recursive: true });
75
75
  }
76
76
  async function withFileLock(targetPath, fn, opts = {}) {
77
- const dir = path3.dirname(targetPath);
77
+ const dir = path4.dirname(targetPath);
78
78
  await fsp2.mkdir(dir, { recursive: true });
79
- const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
79
+ const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
80
80
  const timeoutMs = opts.timeoutMs ?? 5e3;
81
81
  const staleMs = opts.staleMs ?? 3e4;
82
82
  const started = Date.now();
@@ -105,7 +105,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
105
105
  if (Date.now() - started >= timeoutMs) {
106
106
  throw new Error(`Timed out waiting for file lock: ${targetPath}`);
107
107
  }
108
- await new Promise((resolve5) => setTimeout(resolve5, 25));
108
+ await new Promise((resolve6) => setTimeout(resolve6, 25));
109
109
  }
110
110
  }
111
111
  try {
@@ -138,7 +138,7 @@ async function renameWithRetry(from, to) {
138
138
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
139
139
  throw err;
140
140
  }
141
- await new Promise((resolve5) => setTimeout(resolve5, delays[i]));
141
+ await new Promise((resolve6) => setTimeout(resolve6, delays[i]));
142
142
  }
143
143
  }
144
144
  throw lastErr;
@@ -234,7 +234,7 @@ var DefaultLogger = class _DefaultLogger {
234
234
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
235
235
  if (this.file) {
236
236
  try {
237
- fs4.mkdirSync(path3.dirname(this.file), { recursive: true });
237
+ fs4.mkdirSync(path4.dirname(this.file), { recursive: true });
238
238
  } catch {
239
239
  }
240
240
  }
@@ -436,32 +436,268 @@ function isEmptyMessage(msg) {
436
436
  return msg.content.length === 0;
437
437
  }
438
438
 
439
+ // src/utils/assert-never.ts
440
+ function assertNever(x, message) {
441
+ const err = new Error(
442
+ `Unhandled case: ${JSON.stringify(x)}`
443
+ );
444
+ err.name = "AssertNeverError";
445
+ throw err;
446
+ }
447
+
439
448
  // src/utils/index.ts
440
449
  init_atomic_write();
450
+ var MAX_DIGEST_CHARS = 4e3;
451
+ function createContextEvidenceState() {
452
+ return {
453
+ sessionGoals: [],
454
+ implicitFacts: [],
455
+ activeErrors: [],
456
+ toolCalls: [],
457
+ fileGraph: {},
458
+ repeatedReads: [],
459
+ updatedAt: Date.now()
460
+ };
461
+ }
462
+ function buildContextEvidenceDigest(ctx) {
463
+ const state = ensureEvidence(ctx);
464
+ const lines = [];
465
+ if (state.currentIntent?.text) {
466
+ lines.push(`intent: ${state.currentIntent.text}`);
467
+ }
468
+ const goals = state.sessionGoals.slice(-3);
469
+ if (goals.length > 0) {
470
+ lines.push("session_goals:");
471
+ for (const goal of goals) lines.push(`- ${goal}`);
472
+ }
473
+ const activeErrors = state.activeErrors.slice(-5);
474
+ if (activeErrors.length > 0) {
475
+ lines.push("active_errors:");
476
+ for (const err of activeErrors) lines.push(`- ${err}`);
477
+ }
478
+ const files = Object.values(state.fileGraph).sort((a, b) => b.writes - a.writes || b.reads - a.reads || a.path.localeCompare(b.path)).slice(0, 12);
479
+ if (files.length > 0) {
480
+ lines.push("dependency_graph:");
481
+ for (const file of files) {
482
+ const actions = [
483
+ file.reads > 0 ? `read ${file.reads}x` : "",
484
+ file.writes > 0 ? `write ${file.writes}x` : ""
485
+ ].filter(Boolean).join(", ");
486
+ const refs = file.referenced ? "; referenced by assistant" : "";
487
+ const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : "";
488
+ lines.push(`- ${file.path} (${actions || "seen"}${refs}${via})`);
489
+ }
490
+ }
491
+ const referenced = state.toolCalls.filter((tool) => tool.status === "referenced").slice(-10);
492
+ const recentSeen = state.toolCalls.filter((tool) => tool.status === "seen").slice(-5);
493
+ const trail = [...referenced, ...recentSeen];
494
+ if (trail.length > 0) {
495
+ lines.push("tool_trail:");
496
+ for (const tool of trail) {
497
+ const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : "";
498
+ const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(", ")}` : "";
499
+ const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(", ")}` : "";
500
+ lines.push(
501
+ `- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`
502
+ );
503
+ }
504
+ }
505
+ const facts = state.implicitFacts.slice(-8);
506
+ if (facts.length > 0) {
507
+ lines.push("implicit_facts:");
508
+ for (const fact of facts) lines.push(`- ${fact}`);
509
+ }
510
+ const digest = lines.join("\n");
511
+ if (digest.length <= MAX_DIGEST_CHARS) return digest;
512
+ return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;
513
+ }
514
+ function repeatedReadPressure(ctx) {
515
+ return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);
516
+ }
517
+ function ensureEvidence(ctx) {
518
+ if (!ctx.contextEvidence) {
519
+ ctx.contextEvidence = createContextEvidenceState();
520
+ }
521
+ return ctx.contextEvidence;
522
+ }
523
+
524
+ // src/utils/deep-merge.ts
525
+ var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
526
+ "__proto__",
527
+ "constructor",
528
+ "prototype",
529
+ "__defineGetter__",
530
+ "__defineSetter__",
531
+ "__lookupGetter__",
532
+ "__lookupSetter__"
533
+ ]);
534
+ function isPrimitiveArray(a) {
535
+ return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
536
+ }
537
+ function deepMerge(base, patch, options = {}) {
538
+ const {
539
+ conflictResolution = "prefer-patch",
540
+ arrayMode = "replace",
541
+ protectProto = true,
542
+ onNonPrimitiveArrayReplace
543
+ } = options;
544
+ if (typeof base !== "object" || base === null) {
545
+ return conflictResolution === "prefer-patch" ? patch : base;
546
+ }
547
+ if (typeof patch !== "object" || patch === null) {
548
+ return conflictResolution === "prefer-patch" ? patch : base;
549
+ }
550
+ if (Array.isArray(base) && Array.isArray(patch)) {
551
+ if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
552
+ return [.../* @__PURE__ */ new Set([...base, ...patch])];
553
+ }
554
+ return conflictResolution === "prefer-patch" ? patch : base;
555
+ }
556
+ if (Array.isArray(base) || Array.isArray(patch)) {
557
+ return conflictResolution === "prefer-patch" ? patch : base;
558
+ }
559
+ const baseObj = base;
560
+ const patchObj = patch;
561
+ const out = { ...baseObj };
562
+ for (const [k, v] of Object.entries(patchObj)) {
563
+ if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
564
+ const existing = out[k];
565
+ if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
566
+ out[k] = deepMerge(existing, v, options);
567
+ } else if (Array.isArray(v) && Array.isArray(existing)) {
568
+ if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
569
+ onNonPrimitiveArrayReplace(k, existing.length, v.length);
570
+ }
571
+ out[k] = deepMerge(existing, v, options);
572
+ } else if (v !== void 0) {
573
+ if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
574
+ const existingLen = Array.isArray(existing) ? existing.length : 0;
575
+ onNonPrimitiveArrayReplace(k, existingLen, v.length);
576
+ }
577
+ out[k] = v;
578
+ }
579
+ }
580
+ return out;
581
+ }
441
582
 
442
583
  // src/utils/error.ts
443
584
  function toErrorMessage(err) {
444
585
  return err instanceof Error ? err.message : String(err);
445
586
  }
446
-
447
- // src/utils/safe-json.ts
448
- function safeParse(input, maxBytes = 5e6) {
449
- if (input.length > maxBytes) {
450
- return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
587
+ var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
588
+ var IS_WINDOWS = process.platform === "win32";
589
+ var SEP = IS_WINDOWS ? "\\" : "/";
590
+ function isGlob(p) {
591
+ for (const c of p) {
592
+ if (GLOB_CHARS.has(c)) return true;
451
593
  }
452
- try {
453
- return { ok: true, value: JSON.parse(input) };
454
- } catch (err) {
455
- return {
456
- ok: false,
457
- error: toErrorMessage(err)
458
- };
594
+ return false;
595
+ }
596
+ function globToRegex(pat) {
597
+ let i = 0;
598
+ let re = "^";
599
+ while (i < pat.length) {
600
+ const c = expectDefined(pat[i]);
601
+ if (c === "*") {
602
+ if (pat[i + 1] === "*") {
603
+ re += ".*";
604
+ i += 2;
605
+ if (pat[i] === "/") i++;
606
+ } else {
607
+ re += "[^/\\\\]*";
608
+ i++;
609
+ }
610
+ } else if (c === "?") {
611
+ re += "[^/\\\\]";
612
+ i++;
613
+ } else if (c === "[") {
614
+ let cls = "[";
615
+ i++;
616
+ if (pat[i] === "!" || pat[i] === "^") {
617
+ cls += "^";
618
+ i++;
619
+ }
620
+ while (i < pat.length && pat[i] !== "]") {
621
+ const ch = pat[i] ?? "";
622
+ if (ch === "\\") cls += "\\\\";
623
+ else if (ch === "]" || ch === "^") cls += `\\${ch}`;
624
+ else cls += ch;
625
+ i++;
626
+ }
627
+ cls += "]";
628
+ re += cls;
629
+ i++;
630
+ } else {
631
+ re += c.replace(/[.+^${}()|\\]/g, "\\$&");
632
+ i++;
633
+ }
459
634
  }
635
+ return new RegExp(re + "$");
460
636
  }
461
-
462
- // src/utils/string.ts
463
- function truncate(s, max) {
464
- return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
637
+ function baseDir(pat) {
638
+ let i = pat.length - 1;
639
+ while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
640
+ const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
641
+ return cut < 0 ? "." : pat.slice(0, cut);
642
+ }
643
+ async function expandGlob(pattern) {
644
+ if (!isGlob(pattern)) return [pattern];
645
+ const results = /* @__PURE__ */ new Set();
646
+ const abs = isAbsolute(pattern);
647
+ const base = abs ? baseDir(pattern) : baseDir(pattern);
648
+ const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
649
+ async function walk3(dir, pat) {
650
+ let entries;
651
+ try {
652
+ entries = await fsp2.readdir(dir);
653
+ } catch {
654
+ return;
655
+ }
656
+ const firstGlob = pat.search(/[*?[[]/);
657
+ if (firstGlob < 0) {
658
+ const re = globToRegex(pat);
659
+ for (const e of entries) {
660
+ if (re.test(e)) {
661
+ const full = `${dir}${SEP}${e}`;
662
+ results.add(abs ? resolve(full) : full);
663
+ }
664
+ }
665
+ return;
666
+ }
667
+ const before = pat.slice(0, firstGlob);
668
+ const rest = pat.slice(firstGlob);
669
+ if (before.endsWith("**")) {
670
+ await walk3(dir, rest);
671
+ for (const e of entries) {
672
+ const full = `${dir}${SEP}${e}`;
673
+ try {
674
+ const stat6 = await fsp2.stat(full);
675
+ if (stat6.isDirectory()) await walk3(full, rest);
676
+ } catch {
677
+ }
678
+ }
679
+ } else if (before === "") {
680
+ const re = globToRegex(rest);
681
+ for (const e of entries) {
682
+ if (re.test(e)) {
683
+ const full = `${dir}${SEP}${e}`;
684
+ results.add(abs ? resolve(full) : full);
685
+ }
686
+ }
687
+ } else {
688
+ const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
689
+ if (entries.includes(seg)) {
690
+ const full = `${dir}${SEP}${seg}`;
691
+ try {
692
+ const stat6 = await fsp2.stat(full);
693
+ if (stat6.isDirectory()) await walk3(full, rest);
694
+ } catch {
695
+ }
696
+ }
697
+ }
698
+ }
699
+ await walk3(base === "." ? "." : base, relPat);
700
+ return [...results];
465
701
  }
466
702
 
467
703
  // src/utils/glob-match.ts
@@ -539,232 +775,440 @@ function matchGlob(pattern, input) {
539
775
  function matchAny(patterns, input) {
540
776
  return patterns.some((p) => matchGlob(p, input));
541
777
  }
542
- function projectHash(absRoot) {
543
- return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
544
- }
545
- function projectSlug(absRoot) {
546
- const base = slugify(path3.basename(absRoot));
547
- const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
548
- return `${base}-${hash}`;
549
- }
550
- function slugify(name) {
551
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
552
- }
553
- function wstackGlobalRoot() {
554
- const fromEnv = process.env["WRONGSTACK_HOME"];
555
- if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
556
- return path3.join(os.homedir(), ".wrongstack");
557
- }
558
- function resolveWstackPaths(opts) {
559
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
560
- const hash = projectHash(opts.projectRoot);
561
- const slug = projectSlug(opts.projectRoot);
562
- const projectDir = path3.join(globalRoot, "projects", slug);
563
- return {
564
- globalRoot,
565
- configDir: globalRoot,
566
- globalConfig: path3.join(globalRoot, "config.json"),
567
- secretsKey: path3.join(globalRoot, ".key"),
568
- globalMemory: path3.join(globalRoot, "memory.md"),
569
- globalSkills: path3.join(globalRoot, "skills"),
570
- globalPrompts: path3.join(globalRoot, "prompts"),
571
- cacheDir: path3.join(globalRoot, "cache"),
572
- modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
573
- modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
574
- historyFile: path3.join(globalRoot, "history"),
575
- logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
576
- projectDir,
577
- projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
578
- projectMemory: path3.join(projectDir, "memory.md"),
579
- projectSessions: path3.join(projectDir, "sessions"),
580
- projectTrust: path3.join(projectDir, "trust.json"),
581
- projectMeta: path3.join(projectDir, "meta.json"),
582
- projectLocalConfig: path3.join(projectDir, "config.local.json"),
583
- inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
584
- inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
585
- inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
586
- inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
587
- projectHash: hash,
588
- projectSlug: slug,
589
- projectGoal: path3.join(projectDir, "goal.json"),
590
- projectSpecs: path3.join(projectDir, "specs"),
591
- projectTaskGraphs: path3.join(projectDir, "task-graphs"),
592
- projectSddSession: path3.join(projectDir, "sdd-session.json"),
593
- projectPlan: path3.join(projectDir, "plan.json"),
594
- projectAutophase: path3.join(projectDir, "autophase"),
595
- syncConfig: path3.join(globalRoot, "sync.json"),
596
- projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
597
- };
598
- }
599
-
600
- // src/utils/sleep.ts
601
- function sleep(ms) {
602
- return new Promise((resolve5) => setTimeout(resolve5, ms));
603
- }
604
778
 
605
- // src/utils/assert-never.ts
606
- function assertNever(x, message) {
607
- const err = new Error(
608
- `Unhandled case: ${JSON.stringify(x)}`
609
- );
610
- err.name = "AssertNeverError";
611
- throw err;
779
+ // src/utils/json-repair.ts
780
+ function completePartialObject(s) {
781
+ if (!s.trim().startsWith("{")) return s;
782
+ if (tryParse(s).ok) return s;
783
+ return repairTruncated(s);
612
784
  }
613
-
614
- // src/utils/deep-merge.ts
615
- var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
616
- "__proto__",
617
- "constructor",
618
- "prototype",
619
- "__defineGetter__",
620
- "__defineSetter__",
621
- "__lookupGetter__",
622
- "__lookupSetter__"
623
- ]);
624
- function isPrimitiveArray(a) {
625
- return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
626
- }
627
- function deepMerge(base, patch, options = {}) {
628
- const {
629
- conflictResolution = "prefer-patch",
630
- arrayMode = "replace",
631
- protectProto = true,
632
- onNonPrimitiveArrayReplace
633
- } = options;
634
- if (typeof base !== "object" || base === null) {
635
- return conflictResolution === "prefer-patch" ? patch : base;
636
- }
637
- if (typeof patch !== "object" || patch === null) {
638
- return conflictResolution === "prefer-patch" ? patch : base;
785
+ function repairTruncated(s) {
786
+ const stack = [];
787
+ let inString = false;
788
+ let escaped = false;
789
+ let sawKey = false;
790
+ let prevSig = "";
791
+ let contentEnd = 0;
792
+ let stringBraceDepth = 0;
793
+ for (let i = 0; i < s.length; i++) {
794
+ const ch = expectDefined(s[i]);
795
+ if (inString) {
796
+ contentEnd = i + 1;
797
+ if (escaped) {
798
+ escaped = false;
799
+ continue;
800
+ }
801
+ if (ch === "\\") {
802
+ escaped = true;
803
+ continue;
804
+ }
805
+ if (ch === '"') {
806
+ inString = false;
807
+ prevSig = '"';
808
+ stringBraceDepth = 0;
809
+ continue;
810
+ }
811
+ if (ch === "{") stringBraceDepth++;
812
+ else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
813
+ continue;
814
+ }
815
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
816
+ contentEnd = i + 1;
817
+ if (ch === '"') {
818
+ inString = true;
819
+ sawKey = true;
820
+ stringBraceDepth = 0;
821
+ prevSig = '"';
822
+ } else if (ch === "{" || ch === "[") {
823
+ stack.push(ch);
824
+ prevSig = ch;
825
+ } else if (ch === "}" || ch === "]") {
826
+ stack.pop();
827
+ prevSig = ch;
828
+ } else {
829
+ prevSig = ch;
830
+ }
639
831
  }
640
- if (Array.isArray(base) && Array.isArray(patch)) {
641
- if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
642
- return [.../* @__PURE__ */ new Set([...base, ...patch])];
832
+ if (!sawKey && !inString) return s;
833
+ let result = s.slice(0, contentEnd);
834
+ if (inString) {
835
+ if (escaped) {
836
+ result = result.slice(0, -1);
837
+ } else if (endsWithInvalidEscape(result)) {
838
+ result = result.slice(0, -2);
643
839
  }
644
- return conflictResolution === "prefer-patch" ? patch : base;
840
+ if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
841
+ result += '"';
842
+ } else if (prevSig === ":") {
843
+ result += "null";
645
844
  }
646
- if (Array.isArray(base) || Array.isArray(patch)) {
647
- return conflictResolution === "prefer-patch" ? patch : base;
845
+ for (let k = stack.length - 1; k >= 0; k--) {
846
+ result += stack[k] === "{" ? "}" : "]";
648
847
  }
649
- const baseObj = base;
650
- const patchObj = patch;
651
- const out = { ...baseObj };
652
- for (const [k, v] of Object.entries(patchObj)) {
653
- if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
654
- const existing = out[k];
655
- if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
656
- out[k] = deepMerge(existing, v, options);
657
- } else if (Array.isArray(v) && Array.isArray(existing)) {
658
- if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
659
- onNonPrimitiveArrayReplace(k, existing.length, v.length);
660
- }
661
- out[k] = deepMerge(existing, v, options);
662
- } else if (v !== void 0) {
663
- if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
664
- const existingLen = Array.isArray(existing) ? existing.length : 0;
665
- onNonPrimitiveArrayReplace(k, existingLen, v.length);
666
- }
667
- out[k] = v;
668
- }
848
+ if (!tryParse(result).ok) {
849
+ const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
850
+ if (tryParse(patched).ok) result = patched;
851
+ }
852
+ return result;
853
+ }
854
+ var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
855
+ function endsWithInvalidEscape(str) {
856
+ const last = str[str.length - 1];
857
+ if (str[str.length - 2] !== "\\" || last === void 0) return false;
858
+ if (VALID_ESCAPE.has(last)) return false;
859
+ let backslashes = 0;
860
+ for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
861
+ return backslashes % 2 === 1;
862
+ }
863
+ function tryParse(s) {
864
+ try {
865
+ return { ok: true, value: JSON.parse(s) };
866
+ } catch {
867
+ return { ok: false };
669
868
  }
670
- return out;
671
869
  }
672
870
 
673
- // src/utils/tool-output-serializer.ts
674
- function createToolOutputSerializer(opts = {}) {
675
- const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
676
- function serialize(value) {
677
- if (typeof value === "string") return value;
678
- if (value === null || value === void 0) return "";
679
- if (typeof value === "object") {
680
- if (Array.isArray(value)) return value.map(serialize).join("\n");
681
- if ("text" in value) {
682
- const t = value.text;
683
- return typeof t === "string" ? t : JSON.stringify(value, null, 2);
684
- }
685
- try {
686
- return JSON.stringify(value, null, 2);
687
- } catch {
688
- return String(value);
689
- }
871
+ // src/utils/json-schema-validate.ts
872
+ function validateAgainstSchema(value, schema) {
873
+ const errors = [];
874
+ walk(value, schema, "", errors);
875
+ return { ok: errors.length === 0, errors };
876
+ }
877
+ function walk(value, schema, path21, errors) {
878
+ if (schema.enum !== void 0) {
879
+ if (!schema.enum.some((e) => deepEqual(e, value))) {
880
+ errors.push({
881
+ path: path21 || "<root>",
882
+ message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
883
+ });
884
+ return;
690
885
  }
691
- return String(value);
692
886
  }
693
- function enforceCap(text, remainingBudget) {
694
- if (remainingBudget <= 0) {
695
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
887
+ if (typeof schema.type === "string") {
888
+ if (!checkType(value, schema.type)) {
889
+ errors.push({
890
+ path: path21 || "<root>",
891
+ message: `expected ${schema.type}, got ${describeType(value)}`
892
+ });
893
+ return;
696
894
  }
697
- const textBytes = Buffer.byteLength(text, "utf8");
698
- if (textBytes <= remainingBudget) {
699
- return { text, newBudget: remainingBudget - textBytes };
895
+ }
896
+ if (schema.type === "object" && isPlainObject(value)) {
897
+ const obj = value;
898
+ for (const req of schema.required ?? []) {
899
+ if (!(req in obj)) {
900
+ errors.push({ path: joinPath(path21, req), message: "required property missing" });
901
+ }
700
902
  }
701
- const marker = `
702
- \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
703
- `;
704
- const markerBytes = Buffer.byteLength(marker, "utf8");
705
- const available = remainingBudget - markerBytes;
706
- if (available <= 0) {
707
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
903
+ if (schema.properties) {
904
+ for (const [key, subSchema] of Object.entries(schema.properties)) {
905
+ if (key in obj) {
906
+ walk(obj[key], subSchema, joinPath(path21, key), errors);
907
+ }
908
+ }
708
909
  }
709
- const half = Math.floor(available / 2);
710
- const first = text.slice(0, half);
711
- const second = text.slice(text.length - half);
712
- return { text: `${first}${marker}${second}`, newBudget: 0 };
713
- }
714
- return { serialize, enforceCap, capBytes };
715
- }
716
-
717
- // src/utils/token-estimate.ts
718
- var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
719
- var CALIBRATION_GLOBAL_KEY = "__global__";
720
- var _cals = /* @__PURE__ */ new Map();
721
- function calState(key) {
722
- let state = _cals.get(key);
723
- if (!state) {
724
- state = { ratio: 1, count: 0, prevEst: 0 };
725
- _cals.set(key, state);
726
910
  }
727
- return state;
728
- }
729
- var MIN_SAMPLES_FOR_CALIBRATION = 3;
730
- var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
731
- var ESTIMATE_CACHE_MAX_SIZE = 1e4;
732
- function getCachedEstimate(key, compute) {
733
- const existing = ESTIMATE_CACHE.get(key);
734
- if (existing !== void 0) return existing;
735
- if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
736
- for (const k of ESTIMATE_CACHE.keys()) {
737
- if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
738
- ESTIMATE_CACHE.delete(k);
911
+ if (schema.type === "array" && Array.isArray(value) && schema.items) {
912
+ for (let i = 0; i < value.length; i++) {
913
+ walk(value[i], schema.items, `${path21}[${i}]`, errors);
739
914
  }
740
915
  }
741
- const estimate = compute(key);
742
- ESTIMATE_CACHE.set(key, estimate);
743
- return estimate;
744
916
  }
745
- function estimateToolInputTokens(input) {
746
- if (typeof input === "string") return RoughTokenEstimate(input);
747
- if (input === null || typeof input !== "object") {
748
- return RoughTokenEstimate(String(input));
917
+ function checkType(value, type) {
918
+ switch (type) {
919
+ case "string":
920
+ return typeof value === "string";
921
+ case "number":
922
+ return typeof value === "number" && !Number.isNaN(value);
923
+ case "integer":
924
+ return typeof value === "number" && Number.isInteger(value);
925
+ case "boolean":
926
+ return typeof value === "boolean";
927
+ case "null":
928
+ return value === null;
929
+ case "array":
930
+ return Array.isArray(value);
931
+ case "object":
932
+ return isPlainObject(value);
933
+ default:
934
+ return true;
749
935
  }
750
- return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
751
936
  }
752
- function estimateToolResultTokens(content) {
753
- if (typeof content === "string") return RoughTokenEstimate(content);
754
- return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
937
+ function isPlainObject(v) {
938
+ return typeof v === "object" && v !== null && !Array.isArray(v);
755
939
  }
756
- function estimateTextTokens(text) {
757
- return RoughTokenEstimate(text);
940
+ function describeType(v) {
941
+ if (v === null) return "null";
942
+ if (Array.isArray(v)) return "array";
943
+ return typeof v;
758
944
  }
759
- function computeMessageTokens(msg) {
760
- if (typeof msg.content === "string") return estimateTextTokens(msg.content);
761
- let total = 0;
762
- for (const b of msg.content) {
763
- if (b.type === "text") total += estimateTextTokens(b.text);
764
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
765
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
766
- else total += RoughTokenEstimate(JSON.stringify(b));
767
- }
945
+ function joinPath(parent, key) {
946
+ if (!parent) return key;
947
+ return `${parent}.${key}`;
948
+ }
949
+ function deepEqual(a, b) {
950
+ if (a === b) return true;
951
+ if (typeof a !== typeof b) return false;
952
+ if (a === null || b === null) return a === b;
953
+ if (Array.isArray(a) && Array.isArray(b)) {
954
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
955
+ }
956
+ if (typeof a === "object" && typeof b === "object") {
957
+ const ak = Object.keys(a);
958
+ const bk = Object.keys(b);
959
+ if (ak.length !== bk.length) return false;
960
+ return ak.every(
961
+ (k) => deepEqual(a[k], b[k])
962
+ );
963
+ }
964
+ return false;
965
+ }
966
+
967
+ // src/utils/merge-models-payload.ts
968
+ function mergeModelsPayload(base, overlay) {
969
+ const out = {};
970
+ for (const [id, provider] of Object.entries(base)) {
971
+ out[id] = cloneProvider(provider);
972
+ }
973
+ for (const [id, ovProvider] of Object.entries(overlay)) {
974
+ const existing = out[id];
975
+ out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
976
+ }
977
+ return out;
978
+ }
979
+ function mergeProvider(base, overlay) {
980
+ const models = {};
981
+ for (const [mid, m] of Object.entries(base.models ?? {})) {
982
+ models[mid] = { ...m };
983
+ }
984
+ for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
985
+ const existing = models[mid];
986
+ models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
987
+ }
988
+ return {
989
+ ...base,
990
+ // Overlay scalar fields win when explicitly provided; otherwise keep base.
991
+ ...stripUndefined({
992
+ id: overlay.id,
993
+ name: overlay.name,
994
+ npm: overlay.npm,
995
+ api: overlay.api,
996
+ env: overlay.env,
997
+ doc: overlay.doc
998
+ }),
999
+ models
1000
+ };
1001
+ }
1002
+ function mergeModel(base, overlay) {
1003
+ const merged = { ...base, ...overlay };
1004
+ if (base.limit || overlay.limit) {
1005
+ merged.limit = { ...base.limit, ...overlay.limit };
1006
+ }
1007
+ if (base.cost || overlay.cost) {
1008
+ merged.cost = { ...base.cost, ...overlay.cost };
1009
+ }
1010
+ if (base.modalities || overlay.modalities) {
1011
+ merged.modalities = { ...base.modalities, ...overlay.modalities };
1012
+ }
1013
+ return merged;
1014
+ }
1015
+ function cloneProvider(p) {
1016
+ const models = {};
1017
+ for (const [mid, m] of Object.entries(p.models ?? {})) {
1018
+ models[mid] = { ...m };
1019
+ }
1020
+ return { ...p, models };
1021
+ }
1022
+ function stripUndefined(obj) {
1023
+ const out = {};
1024
+ for (const [k, v] of Object.entries(obj)) {
1025
+ if (v !== void 0) out[k] = v;
1026
+ }
1027
+ return out;
1028
+ }
1029
+
1030
+ // src/utils/regex-guard.ts
1031
+ var MAX_PATTERN_LEN = 512;
1032
+ var DANGEROUS_PATTERNS = [
1033
+ /(\([^)]*[+*][^)]*\))[+*]/,
1034
+ // (a+)+, (.*)+, etc
1035
+ /(\(\?:[^)]*[+*][^)]*\))[+*]/
1036
+ // same, with non-capturing group
1037
+ ];
1038
+ function compileUserRegex(pattern, flags) {
1039
+ if (typeof pattern !== "string") {
1040
+ return { ok: false, reason: "pattern must be a string" };
1041
+ }
1042
+ if (pattern.length === 0) {
1043
+ return { ok: false, reason: "pattern is empty" };
1044
+ }
1045
+ if (pattern.length > MAX_PATTERN_LEN) {
1046
+ return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
1047
+ }
1048
+ for (const rx of DANGEROUS_PATTERNS) {
1049
+ if (rx.test(pattern)) {
1050
+ return {
1051
+ ok: false,
1052
+ reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
1053
+ };
1054
+ }
1055
+ }
1056
+ try {
1057
+ return { ok: true, regex: new RegExp(pattern, flags) };
1058
+ } catch (err) {
1059
+ return {
1060
+ ok: false,
1061
+ reason: err instanceof Error ? err.message : "invalid regex"
1062
+ };
1063
+ }
1064
+ }
1065
+
1066
+ // src/utils/safe-json.ts
1067
+ function safeParse(input, maxBytes = 5e6) {
1068
+ if (input.length > maxBytes) {
1069
+ return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
1070
+ }
1071
+ try {
1072
+ return { ok: true, value: JSON.parse(input) };
1073
+ } catch (err) {
1074
+ return {
1075
+ ok: false,
1076
+ error: toErrorMessage(err)
1077
+ };
1078
+ }
1079
+ }
1080
+
1081
+ // src/utils/sleep.ts
1082
+ function sleep(ms) {
1083
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
1084
+ }
1085
+
1086
+ // src/utils/string.ts
1087
+ function truncate(s, max) {
1088
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
1089
+ }
1090
+
1091
+ // src/utils/tool-wire-compact.ts
1092
+ var TOOL_DESCRIPTION_MAX_CHARS = 640;
1093
+ var SCHEMA_DESCRIPTION_MAX_CHARS = 180;
1094
+ var compactCache = /* @__PURE__ */ new WeakMap();
1095
+ function compactToolDefinitionForWire(tool, opts = {}) {
1096
+ const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
1097
+ if (useDefaultOptions && typeof tool === "object" && tool !== null) {
1098
+ const cached = compactCache.get(tool);
1099
+ if (cached) return cached;
1100
+ }
1101
+ const compact = {
1102
+ name: tool.name,
1103
+ description: compactDescription(
1104
+ tool.description ?? "",
1105
+ opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS
1106
+ ),
1107
+ inputSchema: compactSchemaDescriptions(
1108
+ tool.inputSchema,
1109
+ opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
1110
+ )
1111
+ };
1112
+ if (useDefaultOptions && typeof tool === "object" && tool !== null) {
1113
+ compactCache.set(tool, compact);
1114
+ }
1115
+ return compact;
1116
+ }
1117
+ function compactSchemaDescriptions(schema, maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS) {
1118
+ const compact = compactSchemaNode(schema, maxDescriptionChars);
1119
+ return isRecord(compact) ? compact : { type: "object", properties: {} };
1120
+ }
1121
+ function compactSchemaNode(node, maxDescriptionChars) {
1122
+ if (Array.isArray(node)) {
1123
+ return node.map((item) => compactSchemaNode(item, maxDescriptionChars));
1124
+ }
1125
+ if (!isRecord(node)) return node;
1126
+ const out = {};
1127
+ for (const [key, value] of Object.entries(node)) {
1128
+ if (key === "description" && typeof value === "string") {
1129
+ out[key] = compactDescription(value, maxDescriptionChars);
1130
+ } else {
1131
+ out[key] = compactSchemaNode(value, maxDescriptionChars);
1132
+ }
1133
+ }
1134
+ return out;
1135
+ }
1136
+ function compactDescription(text, maxChars) {
1137
+ const normalized = text.replace(/\s+/g, " ").trim();
1138
+ if (normalized.length <= maxChars) return normalized;
1139
+ if (maxChars <= 20) return normalized.slice(0, maxChars);
1140
+ const hardLimit = maxChars - 12;
1141
+ const boundary = findSemanticBoundary(normalized, hardLimit);
1142
+ const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();
1143
+ return `${head} ...`;
1144
+ }
1145
+ function findSemanticBoundary(text, limit) {
1146
+ const punctuation = Math.max(
1147
+ text.lastIndexOf(". ", limit),
1148
+ text.lastIndexOf("; ", limit),
1149
+ text.lastIndexOf(": ", limit)
1150
+ );
1151
+ if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;
1152
+ const comma = text.lastIndexOf(", ", limit);
1153
+ if (comma >= Math.floor(limit * 0.6)) return comma + 1;
1154
+ const space = text.lastIndexOf(" ", limit);
1155
+ return space >= Math.floor(limit * 0.6) ? space : limit;
1156
+ }
1157
+ function isRecord(value) {
1158
+ return !!value && typeof value === "object" && !Array.isArray(value);
1159
+ }
1160
+
1161
+ // src/utils/token-estimate.ts
1162
+ var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1163
+ var CALIBRATION_GLOBAL_KEY = "__global__";
1164
+ var _cals = /* @__PURE__ */ new Map();
1165
+ function calState(key) {
1166
+ let state = _cals.get(key);
1167
+ if (!state) {
1168
+ state = { ratio: 1, count: 0, prevEst: 0 };
1169
+ _cals.set(key, state);
1170
+ }
1171
+ return state;
1172
+ }
1173
+ var MIN_SAMPLES_FOR_CALIBRATION = 3;
1174
+ var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1175
+ var ESTIMATE_CACHE_MAX_SIZE = 1e4;
1176
+ function getCachedEstimate(key, compute) {
1177
+ const existing = ESTIMATE_CACHE.get(key);
1178
+ if (existing !== void 0) return existing;
1179
+ if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
1180
+ for (const k of ESTIMATE_CACHE.keys()) {
1181
+ if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
1182
+ ESTIMATE_CACHE.delete(k);
1183
+ }
1184
+ }
1185
+ const estimate = compute(key);
1186
+ ESTIMATE_CACHE.set(key, estimate);
1187
+ return estimate;
1188
+ }
1189
+ function estimateToolInputTokens(input) {
1190
+ if (typeof input === "string") return RoughTokenEstimate(input);
1191
+ if (input === null || typeof input !== "object") {
1192
+ return RoughTokenEstimate(String(input));
1193
+ }
1194
+ return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
1195
+ }
1196
+ function estimateToolResultTokens(content) {
1197
+ if (typeof content === "string") return RoughTokenEstimate(content);
1198
+ return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
1199
+ }
1200
+ function estimateTextTokens(text) {
1201
+ return RoughTokenEstimate(text);
1202
+ }
1203
+ function computeMessageTokens(msg) {
1204
+ if (typeof msg.content === "string") return estimateTextTokens(msg.content);
1205
+ let total = 0;
1206
+ for (const b of msg.content) {
1207
+ if (b.type === "text") total += estimateTextTokens(b.text);
1208
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
1209
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
1210
+ else total += RoughTokenEstimate(JSON.stringify(b));
1211
+ }
768
1212
  return total;
769
1213
  }
770
1214
  function estimateMessageTokens(messages) {
@@ -781,7 +1225,8 @@ function estimateMessageTokens(messages) {
781
1225
  function estimateToolDefTokens(tool) {
782
1226
  const cached = tool._estDefTokens;
783
1227
  if (typeof cached === "number" && cached > 0) return cached;
784
- return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
1228
+ const compact = compactToolDefinitionForWire(tool);
1229
+ return RoughTokenEstimate(tool.name) + RoughTokenEstimate(compact.description) + RoughTokenEstimate(JSON.stringify(compact.inputSchema));
785
1230
  }
786
1231
  function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
787
1232
  let messagesTokens = 0;
@@ -858,406 +1303,702 @@ function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrat
858
1303
  return result;
859
1304
  }
860
1305
 
861
- // src/utils/json-schema-validate.ts
862
- function validateAgainstSchema(value, schema) {
863
- const errors = [];
864
- walk(value, schema, "", errors);
865
- return { ok: errors.length === 0, errors };
866
- }
867
- function walk(value, schema, path19, errors) {
868
- if (schema.enum !== void 0) {
869
- if (!schema.enum.some((e) => deepEqual(e, value))) {
870
- errors.push({
871
- path: path19 || "<root>",
872
- message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
873
- });
874
- return;
1306
+ // src/utils/tool-output-serializer.ts
1307
+ var DEFAULT_LIST_LIMIT = 500;
1308
+ var LOG_ENTRY_LIMIT = 200;
1309
+ var INLINE_LIMIT = 240;
1310
+ var GREP_FILE_LIMIT = 80;
1311
+ var GREP_MATCHES_PER_FILE = 3;
1312
+ var DIFF_INLINE_LINE_LIMIT = 260;
1313
+ var DIFF_HUNK_LIMIT = 8;
1314
+ var DIFF_HUNK_CONTEXT = 14;
1315
+ function createToolOutputSerializer(opts = {}) {
1316
+ const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
1317
+ function serialize(value, context = {}) {
1318
+ if (typeof value === "string") return value;
1319
+ if (value === null || value === void 0) return "";
1320
+ if (typeof value === "object") {
1321
+ if (Array.isArray(value)) return value.map((item) => serialize(item)).join("\n");
1322
+ if (context.toolName) {
1323
+ const compact = renderToolObject(context.toolName, value, context.input);
1324
+ if (compact !== void 0) return compact;
1325
+ return renderGenericToolObject(context.toolName, value);
1326
+ }
1327
+ if ("text" in value) {
1328
+ const t = value.text;
1329
+ return typeof t === "string" ? t : JSON.stringify(value, null, 2);
1330
+ }
1331
+ try {
1332
+ return JSON.stringify(value, null, 2);
1333
+ } catch {
1334
+ return String(value);
1335
+ }
875
1336
  }
1337
+ return String(value);
876
1338
  }
877
- if (typeof schema.type === "string") {
878
- if (!checkType(value, schema.type)) {
879
- errors.push({
880
- path: path19 || "<root>",
881
- message: `expected ${schema.type}, got ${describeType(value)}`
882
- });
883
- return;
1339
+ function enforceCap(text, remainingBudget) {
1340
+ if (remainingBudget <= 0) {
1341
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
884
1342
  }
885
- }
886
- if (schema.type === "object" && isPlainObject(value)) {
887
- const obj = value;
888
- for (const req of schema.required ?? []) {
889
- if (!(req in obj)) {
890
- errors.push({ path: joinPath(path19, req), message: "required property missing" });
891
- }
892
- }
893
- if (schema.properties) {
894
- for (const [key, subSchema] of Object.entries(schema.properties)) {
895
- if (key in obj) {
896
- walk(obj[key], subSchema, joinPath(path19, key), errors);
897
- }
898
- }
1343
+ const textBytes = Buffer.byteLength(text, "utf8");
1344
+ if (textBytes <= remainingBudget) {
1345
+ return { text, newBudget: remainingBudget - textBytes };
899
1346
  }
900
- }
901
- if (schema.type === "array" && Array.isArray(value) && schema.items) {
902
- for (let i = 0; i < value.length; i++) {
903
- walk(value[i], schema.items, `${path19}[${i}]`, errors);
1347
+ const marker = `
1348
+ \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
1349
+ `;
1350
+ const markerBytes = Buffer.byteLength(marker, "utf8");
1351
+ const available = remainingBudget - markerBytes;
1352
+ if (available <= 0) {
1353
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
904
1354
  }
1355
+ const half = Math.floor(available / 2);
1356
+ const first = text.slice(0, half);
1357
+ const second = text.slice(text.length - half);
1358
+ return { text: `${first}${marker}${second}`, newBudget: 0 };
905
1359
  }
1360
+ return { serialize, enforceCap, capBytes };
906
1361
  }
907
- function checkType(value, type) {
908
- switch (type) {
909
- case "string":
910
- return typeof value === "string";
911
- case "number":
912
- return typeof value === "number" && !Number.isNaN(value);
913
- case "integer":
914
- return typeof value === "number" && Number.isInteger(value);
915
- case "boolean":
916
- return typeof value === "boolean";
917
- case "null":
918
- return value === null;
919
- case "array":
920
- return Array.isArray(value);
921
- case "object":
922
- return isPlainObject(value);
923
- default:
924
- return true;
925
- }
926
- }
927
- function isPlainObject(v) {
928
- return typeof v === "object" && v !== null && !Array.isArray(v);
929
- }
930
- function describeType(v) {
931
- if (v === null) return "null";
932
- if (Array.isArray(v)) return "array";
933
- return typeof v;
934
- }
935
- function joinPath(parent, key) {
936
- if (!parent) return key;
937
- return `${parent}.${key}`;
938
- }
939
- function deepEqual(a, b) {
940
- if (a === b) return true;
941
- if (typeof a !== typeof b) return false;
942
- if (a === null || b === null) return a === b;
943
- if (Array.isArray(a) && Array.isArray(b)) {
944
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
945
- }
946
- if (typeof a === "object" && typeof b === "object") {
947
- const ak = Object.keys(a);
948
- const bk = Object.keys(b);
949
- if (ak.length !== bk.length) return false;
950
- return ak.every(
951
- (k) => deepEqual(a[k], b[k])
952
- );
953
- }
954
- return false;
955
- }
956
-
957
- // src/utils/regex-guard.ts
958
- var MAX_PATTERN_LEN = 512;
959
- var DANGEROUS_PATTERNS = [
960
- /(\([^)]*[+*][^)]*\))[+*]/,
961
- // (a+)+, (.*)+, etc
962
- /(\(\?:[^)]*[+*][^)]*\))[+*]/
963
- // same, with non-capturing group
964
- ];
965
- function compileUserRegex(pattern, flags) {
966
- if (typeof pattern !== "string") {
967
- return { ok: false, reason: "pattern must be a string" };
968
- }
969
- if (pattern.length === 0) {
970
- return { ok: false, reason: "pattern is empty" };
971
- }
972
- if (pattern.length > MAX_PATTERN_LEN) {
973
- return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
974
- }
975
- for (const rx of DANGEROUS_PATTERNS) {
976
- if (rx.test(pattern)) {
977
- return {
978
- ok: false,
979
- reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
980
- };
981
- }
982
- }
983
- try {
984
- return { ok: true, regex: new RegExp(pattern, flags) };
985
- } catch (err) {
986
- return {
987
- ok: false,
988
- reason: err instanceof Error ? err.message : "invalid regex"
989
- };
1362
+ function renderToolObject(toolName, obj, input) {
1363
+ if (toolName === "read" && typeof obj["text"] === "string") {
1364
+ return joinSections([
1365
+ renderHeader(
1366
+ `read: ${stringFromInput(input, "path") ?? stringField(obj, "path") ?? "<unknown>"}`,
1367
+ {
1368
+ offset: numberFromInput(input, "offset"),
1369
+ limit: numberFromInput(input, "limit"),
1370
+ total_lines: obj["total_lines"],
1371
+ encoding: obj["encoding"],
1372
+ truncated: obj["truncated"],
1373
+ cached: obj["cached"],
1374
+ note: obj["note"]
1375
+ }
1376
+ ),
1377
+ obj["text"]
1378
+ ]);
990
1379
  }
991
- }
992
- var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
993
- var IS_WINDOWS = process.platform === "win32";
994
- var SEP = IS_WINDOWS ? "\\" : "/";
995
- function isGlob(p) {
996
- for (const c of p) {
997
- if (GLOB_CHARS.has(c)) return true;
1380
+ if (toolName === "grep" && Array.isArray(obj["matches"])) {
1381
+ const matches = stringArrayField(obj, "matches");
1382
+ return joinSections([
1383
+ renderHeader(`grep: ${stringFromInput(input, "pattern") ?? "<pattern>"}`, {
1384
+ path: stringFromInput(input, "path"),
1385
+ glob: stringFromInput(input, "glob"),
1386
+ mode: stringFromInput(input, "output_mode"),
1387
+ count: obj["count"],
1388
+ shown: matches.length,
1389
+ truncated: obj["truncated"],
1390
+ used: obj["used"]
1391
+ }),
1392
+ renderGrepMatches(matches, stringFromInput(input, "output_mode"))
1393
+ ]);
998
1394
  }
999
- return false;
1000
- }
1001
- function globToRegex(pat) {
1002
- let i = 0;
1003
- let re = "^";
1004
- while (i < pat.length) {
1005
- const c = expectDefined(pat[i]);
1006
- if (c === "*") {
1007
- if (pat[i + 1] === "*") {
1008
- re += ".*";
1009
- i += 2;
1010
- if (pat[i] === "/") i++;
1011
- } else {
1012
- re += "[^/\\\\]*";
1013
- i++;
1014
- }
1015
- } else if (c === "?") {
1016
- re += "[^/\\\\]";
1017
- i++;
1018
- } else if (c === "[") {
1019
- let cls = "[";
1020
- i++;
1021
- if (pat[i] === "!" || pat[i] === "^") {
1022
- cls += "^";
1023
- i++;
1024
- }
1025
- while (i < pat.length && pat[i] !== "]") {
1026
- const ch = pat[i] ?? "";
1027
- if (ch === "\\") cls += "\\\\";
1028
- else if (ch === "]" || ch === "^") cls += `\\${ch}`;
1029
- else cls += ch;
1030
- i++;
1031
- }
1032
- cls += "]";
1033
- re += cls;
1034
- i++;
1035
- } else {
1036
- re += c.replace(/[.+^${}()|\\]/g, "\\$&");
1037
- i++;
1038
- }
1395
+ if (toolName === "patch" && Array.isArray(obj["files"])) {
1396
+ const files = stringArrayField(obj, "files");
1397
+ return joinSections([
1398
+ renderHeader("patch", {
1399
+ applied: obj["applied"],
1400
+ rejected: obj["rejected"],
1401
+ files: files.length,
1402
+ dry_run: obj["dry_run"]
1403
+ }),
1404
+ typeof obj["message"] === "string" ? `message:
1405
+ ${obj["message"]}` : void 0,
1406
+ files.length > 0 ? `files:
1407
+ ${renderStringList(files)}` : void 0
1408
+ ]);
1039
1409
  }
1040
- return new RegExp(re + "$");
1041
- }
1042
- function baseDir(pat) {
1043
- let i = pat.length - 1;
1044
- while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
1045
- const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
1046
- return cut < 0 ? "." : pat.slice(0, cut);
1047
- }
1048
- async function expandGlob(pattern) {
1049
- if (!isGlob(pattern)) return [pattern];
1050
- const results = /* @__PURE__ */ new Set();
1051
- const abs = isAbsolute(pattern);
1052
- const base = abs ? baseDir(pattern) : baseDir(pattern);
1053
- const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
1054
- async function walk3(dir, pat) {
1055
- let entries;
1056
- try {
1057
- entries = await fsp2.readdir(dir);
1058
- } catch {
1059
- return;
1060
- }
1061
- const firstGlob = pat.search(/[*?[[]/);
1062
- if (firstGlob < 0) {
1063
- const re = globToRegex(pat);
1064
- for (const e of entries) {
1065
- if (re.test(e)) {
1066
- const full = `${dir}${SEP}${e}`;
1067
- results.add(abs ? resolve(full) : full);
1068
- }
1069
- }
1070
- return;
1071
- }
1072
- const before = pat.slice(0, firstGlob);
1073
- const rest = pat.slice(firstGlob);
1074
- if (before.endsWith("**")) {
1075
- await walk3(dir, rest);
1076
- for (const e of entries) {
1077
- const full = `${dir}${SEP}${e}`;
1078
- try {
1079
- const stat6 = await fsp2.stat(full);
1080
- if (stat6.isDirectory()) await walk3(full, rest);
1081
- } catch {
1082
- }
1083
- }
1084
- } else if (before === "") {
1085
- const re = globToRegex(rest);
1086
- for (const e of entries) {
1087
- if (re.test(e)) {
1088
- const full = `${dir}${SEP}${e}`;
1089
- results.add(abs ? resolve(full) : full);
1410
+ if (toolName === "glob" && Array.isArray(obj["files"])) {
1411
+ const files = stringArrayField(obj, "files");
1412
+ return joinSections([
1413
+ renderHeader(
1414
+ `${toolName}: ${stringFromInput(input, "pattern") ?? stringFromInput(input, "files") ?? stringFromInput(input, "path") ?? ""}`.trim(),
1415
+ {
1416
+ path: stringFromInput(input, "path"),
1417
+ files: files.length,
1418
+ truncated: obj["truncated"]
1090
1419
  }
1091
- }
1092
- } else {
1093
- const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
1094
- if (entries.includes(seg)) {
1095
- const full = `${dir}${SEP}${seg}`;
1096
- try {
1097
- const stat6 = await fsp2.stat(full);
1098
- if (stat6.isDirectory()) await walk3(full, rest);
1099
- } catch {
1420
+ ),
1421
+ renderStringList(files, "(no files)")
1422
+ ]);
1423
+ }
1424
+ if (toolName === "tree" && typeof obj["tree"] === "string") {
1425
+ return joinSections([
1426
+ renderHeader(
1427
+ `tree: ${stringField(obj, "path") ?? stringFromInput(input, "path") ?? "<cwd>"}`,
1428
+ {
1429
+ total_files: obj["total_files"],
1430
+ total_dirs: obj["total_dirs"],
1431
+ truncated: obj["truncated"]
1100
1432
  }
1101
- }
1102
- }
1433
+ ),
1434
+ obj["tree"]
1435
+ ]);
1103
1436
  }
1104
- await walk3(base === "." ? "." : base, relPat);
1105
- return [...results];
1106
- }
1107
-
1108
- // src/utils/json-repair.ts
1109
- function completePartialObject(s) {
1110
- if (!s.trim().startsWith("{")) return s;
1111
- if (tryParse(s).ok) return s;
1112
- return repairTruncated(s);
1113
- }
1114
- function repairTruncated(s) {
1115
- const stack = [];
1116
- let inString = false;
1117
- let escaped = false;
1118
- let sawKey = false;
1119
- let prevSig = "";
1120
- let contentEnd = 0;
1121
- let stringBraceDepth = 0;
1122
- for (let i = 0; i < s.length; i++) {
1123
- const ch = expectDefined(s[i]);
1124
- if (inString) {
1125
- contentEnd = i + 1;
1126
- if (escaped) {
1127
- escaped = false;
1128
- continue;
1129
- }
1130
- if (ch === "\\") {
1131
- escaped = true;
1132
- continue;
1133
- }
1134
- if (ch === '"') {
1135
- inString = false;
1136
- prevSig = '"';
1137
- stringBraceDepth = 0;
1138
- continue;
1139
- }
1140
- if (ch === "{") stringBraceDepth++;
1141
- else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
1142
- continue;
1143
- }
1144
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
1145
- contentEnd = i + 1;
1146
- if (ch === '"') {
1147
- inString = true;
1148
- sawKey = true;
1149
- stringBraceDepth = 0;
1150
- prevSig = '"';
1151
- } else if (ch === "{" || ch === "[") {
1152
- stack.push(ch);
1153
- prevSig = ch;
1154
- } else if (ch === "}" || ch === "]") {
1155
- stack.pop();
1156
- prevSig = ch;
1157
- } else {
1158
- prevSig = ch;
1159
- }
1437
+ if (toolName === "fetch" && typeof obj["content"] === "string") {
1438
+ return joinSections([
1439
+ renderHeader(
1440
+ `fetch: ${stringField(obj, "url") ?? stringFromInput(input, "url") ?? "<url>"}`,
1441
+ {
1442
+ status: obj["status"],
1443
+ content_type: obj["content_type"]
1444
+ }
1445
+ ),
1446
+ obj["content"]
1447
+ ]);
1160
1448
  }
1161
- if (!sawKey && !inString) return s;
1162
- let result = s.slice(0, contentEnd);
1163
- if (inString) {
1164
- if (escaped) {
1165
- result = result.slice(0, -1);
1166
- } else if (endsWithInvalidEscape(result)) {
1167
- result = result.slice(0, -2);
1168
- }
1169
- if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
1170
- result += '"';
1171
- } else if (prevSig === ":") {
1172
- result += "null";
1449
+ if (toolName === "replace" && Array.isArray(obj["results"])) {
1450
+ const results = obj["results"].filter(isRecord2);
1451
+ const sections = [
1452
+ renderHeader("replace", {
1453
+ files_modified: obj["files_modified"],
1454
+ total_replacements: obj["total_replacements"],
1455
+ dry_run: obj["dry_run"]
1456
+ })
1457
+ ];
1458
+ for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {
1459
+ sections.push(
1460
+ joinSections([
1461
+ renderHeader(`file: ${stringField(r, "path") ?? "<unknown>"}`, {
1462
+ replacements: r["replacements"]
1463
+ }),
1464
+ typeof r["diff"] === "string" ? r["diff"] : void 0
1465
+ ])
1466
+ );
1467
+ }
1468
+ if (results.length > DEFAULT_LIST_LIMIT) {
1469
+ sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);
1470
+ }
1471
+ return joinSections(sections);
1472
+ }
1473
+ if (typeof obj["diff"] === "string") {
1474
+ const diff = obj["diff"];
1475
+ return joinSections([
1476
+ renderHeader(toolName, {
1477
+ path: obj["path"],
1478
+ replacements: obj["replacements"],
1479
+ bytes_written: obj["bytes_written"],
1480
+ created: obj["created"],
1481
+ note: obj["note"],
1482
+ files: Array.isArray(obj["files"]) ? obj["files"].length : void 0,
1483
+ truncated: obj["truncated"],
1484
+ mode: obj["mode"]
1485
+ }),
1486
+ compactDiff(diff)
1487
+ ]);
1173
1488
  }
1174
- for (let k = stack.length - 1; k >= 0; k--) {
1175
- result += stack[k] === "{" ? "}" : "]";
1489
+ if (toolName === "test" && typeof obj["output"] === "string") {
1490
+ return renderTestOutput(obj, input);
1176
1491
  }
1177
- if (!tryParse(result).ok) {
1178
- const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
1179
- if (tryParse(patched).ok) result = patched;
1492
+ if ((toolName === "typecheck" || toolName === "lint" || toolName === "format") && typeof obj["output"] === "string") {
1493
+ return renderVerifierOutput(toolName, obj, input);
1180
1494
  }
1181
- return result;
1182
- }
1183
- var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
1184
- function endsWithInvalidEscape(str) {
1185
- const last = str[str.length - 1];
1186
- if (str[str.length - 2] !== "\\" || last === void 0) return false;
1187
- if (VALID_ESCAPE.has(last)) return false;
1188
- let backslashes = 0;
1189
- for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
1190
- return backslashes % 2 === 1;
1191
- }
1192
- function tryParse(s) {
1193
- try {
1194
- return { ok: true, value: JSON.parse(s) };
1195
- } catch {
1196
- return { ok: false };
1495
+ if (hasCommandOutputShape(obj)) {
1496
+ return renderCommandOutput(toolName, obj, input);
1197
1497
  }
1198
- }
1199
-
1200
- // src/utils/merge-models-payload.ts
1201
- function mergeModelsPayload(base, overlay) {
1202
- const out = {};
1203
- for (const [id, provider] of Object.entries(base)) {
1204
- out[id] = cloneProvider(provider);
1498
+ if (toolName === "json" && typeof obj["formatted"] === "string") {
1499
+ return joinSections([
1500
+ renderHeader("json", {
1501
+ type: obj["type"],
1502
+ keys: Array.isArray(obj["keys"]) ? obj["keys"].length : void 0,
1503
+ query: stringFromInput(input, "query"),
1504
+ error: obj["error"]
1505
+ }),
1506
+ obj["formatted"]
1507
+ ]);
1205
1508
  }
1206
- for (const [id, ovProvider] of Object.entries(overlay)) {
1207
- const existing = out[id];
1208
- out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
1509
+ if (toolName === "logs" && Array.isArray(obj["entries"])) {
1510
+ const entries = obj["entries"].filter(isRecord2);
1511
+ const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {
1512
+ const ts = stringField(entry, "timestamp") ?? "";
1513
+ const level = stringField(entry, "level") ?? "info";
1514
+ const message = stringField(entry, "message") ?? "";
1515
+ const source = stringField(entry, "source");
1516
+ return [ts, level, source, message].filter(Boolean).join(" ");
1517
+ });
1518
+ if (entries.length > LOG_ENTRY_LIMIT) {
1519
+ lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);
1520
+ }
1521
+ return joinSections([
1522
+ renderHeader(`logs: ${stringField(obj, "source") ?? "<source>"}`, {
1523
+ total: obj["total"],
1524
+ shown: Math.min(entries.length, LOG_ENTRY_LIMIT),
1525
+ truncated: obj["truncated"],
1526
+ stream_mode: obj["stream_mode"]
1527
+ }),
1528
+ lines.length > 0 ? lines.join("\n") : "(no log entries)"
1529
+ ]);
1209
1530
  }
1210
- return out;
1211
- }
1212
- function mergeProvider(base, overlay) {
1213
- const models = {};
1214
- for (const [mid, m] of Object.entries(base.models ?? {})) {
1215
- models[mid] = { ...m };
1531
+ if (toolName === "audit" && Array.isArray(obj["vulnerabilities"])) {
1532
+ const vulns = obj["vulnerabilities"].filter(isRecord2);
1533
+ const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {
1534
+ const severity = stringField(v, "severity") ?? "unknown";
1535
+ const pkg = stringField(v, "package") ?? "<package>";
1536
+ const title = stringField(v, "title") ?? "";
1537
+ const url = stringField(v, "url");
1538
+ return [severity, pkg, title, url].filter(Boolean).join(" | ");
1539
+ });
1540
+ if (vulns.length > DEFAULT_LIST_LIMIT) {
1541
+ lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);
1542
+ }
1543
+ return joinSections([
1544
+ renderHeader("audit", {
1545
+ exit_code: obj["exit_code"],
1546
+ total: obj["total"],
1547
+ summary: obj["summary"],
1548
+ truncated: obj["truncated"]
1549
+ }),
1550
+ lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
1551
+ ]);
1216
1552
  }
1217
- for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
1218
- const existing = models[mid];
1219
- models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
1553
+ if (toolName === "outdated" && Array.isArray(obj["packages"])) {
1554
+ const packages = obj["packages"].filter(isRecord2);
1555
+ const lines = packages.slice(0, DEFAULT_LIST_LIMIT).map(
1556
+ (p) => [
1557
+ stringField(p, "name") ?? "<package>",
1558
+ `current=${stringField(p, "current") ?? "unknown"}`,
1559
+ `wanted=${stringField(p, "wanted") ?? "unknown"}`,
1560
+ `latest=${stringField(p, "latest") ?? "unknown"}`,
1561
+ stringField(p, "type")
1562
+ ].filter(Boolean).join(" | ")
1563
+ );
1564
+ if (packages.length > DEFAULT_LIST_LIMIT) {
1565
+ lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);
1566
+ }
1567
+ return joinSections([
1568
+ renderHeader("outdated", {
1569
+ exit_code: obj["exit_code"],
1570
+ total: obj["total"],
1571
+ truncated: obj["truncated"]
1572
+ }),
1573
+ lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
1574
+ ]);
1220
1575
  }
1221
- return {
1222
- ...base,
1223
- // Overlay scalar fields win when explicitly provided; otherwise keep base.
1224
- ...stripUndefined({
1225
- id: overlay.id,
1226
- name: overlay.name,
1227
- npm: overlay.npm,
1228
- api: overlay.api,
1229
- env: overlay.env,
1230
- doc: overlay.doc
1231
- }),
1232
- models
1233
- };
1576
+ return void 0;
1234
1577
  }
1235
- function mergeModel(base, overlay) {
1236
- const merged = { ...base, ...overlay };
1237
- if (base.limit || overlay.limit) {
1238
- merged.limit = { ...base.limit, ...overlay.limit };
1578
+ function renderTestOutput(obj, input) {
1579
+ const exitCode = numberField(obj, "exit_code") ?? 0;
1580
+ const failed = numberField(obj, "failed") ?? 0;
1581
+ const output = stringField(obj, "output") ?? "";
1582
+ const header = renderHeader(`test: ${stringField(obj, "runner") ?? "runner"}`, {
1583
+ exit_code: obj["exit_code"],
1584
+ tests_run: obj["tests_run"],
1585
+ passed: obj["passed"],
1586
+ failed: obj["failed"],
1587
+ duration_ms: obj["duration_ms"],
1588
+ truncated: obj["truncated"],
1589
+ files: inputListSummary(input, "files"),
1590
+ grep: stringFromInput(input, "grep")
1591
+ });
1592
+ if (exitCode === 0 && failed === 0) {
1593
+ return joinSections([
1594
+ header,
1595
+ joinSections([
1596
+ "report:",
1597
+ `status=passed`,
1598
+ `tests_run=${obj["tests_run"] ?? 0}`,
1599
+ `passed=${obj["passed"] ?? 0}`,
1600
+ `failed=${obj["failed"] ?? 0}`,
1601
+ `duration_ms=${obj["duration_ms"] ?? 0}`,
1602
+ extractSpoolNote(output)
1603
+ ])
1604
+ ]);
1239
1605
  }
1240
- if (base.cost || overlay.cost) {
1241
- merged.cost = { ...base.cost, ...overlay.cost };
1606
+ return joinSections([
1607
+ header,
1608
+ `error_context:
1609
+ ${compactFailureOutput(output || "(no runner output)")}`
1610
+ ]);
1611
+ }
1612
+ function renderVerifierOutput(toolName, obj, input) {
1613
+ const exitCode = numberField(obj, "exit_code") ?? 0;
1614
+ const errors = numberField(obj, "errors") ?? 0;
1615
+ const warnings = numberField(obj, "warnings") ?? 0;
1616
+ const output = stringField(obj, "output") ?? "";
1617
+ const changed = numberField(obj, "files_changed") ?? 0;
1618
+ const header = renderHeader(toolName, {
1619
+ exit_code: obj["exit_code"],
1620
+ errors: obj["errors"],
1621
+ warnings: obj["warnings"],
1622
+ files_checked: obj["files_checked"],
1623
+ files_changed: obj["files_changed"],
1624
+ fix_applied: obj["fix_applied"],
1625
+ fixer: obj["fixer"],
1626
+ linter: obj["linter"],
1627
+ project: obj["project"],
1628
+ truncated: obj["truncated"],
1629
+ files: inputListSummary(input, "files"),
1630
+ cwd: stringFromInput(input, "cwd")
1631
+ });
1632
+ if (exitCode === 0 && errors === 0 && (toolName !== "format" || changed === 0)) {
1633
+ return joinSections([
1634
+ header,
1635
+ joinSections([
1636
+ "report:",
1637
+ "status=passed",
1638
+ `errors=${errors}`,
1639
+ `warnings=${warnings}`,
1640
+ toolName === "format" ? `files_changed=${changed}` : void 0,
1641
+ extractSpoolNote(output)
1642
+ ])
1643
+ ]);
1242
1644
  }
1243
- if (base.modalities || overlay.modalities) {
1244
- merged.modalities = { ...base.modalities, ...overlay.modalities };
1645
+ if (exitCode === 0 && toolName === "format") {
1646
+ return joinSections([
1647
+ header,
1648
+ joinSections([
1649
+ "report:",
1650
+ "status=changed",
1651
+ `files_changed=${changed}`,
1652
+ extractSpoolNote(output)
1653
+ ])
1654
+ ]);
1245
1655
  }
1246
- return merged;
1656
+ return joinSections([
1657
+ header,
1658
+ `error_context:
1659
+ ${compactFailureOutput(output || "(no verifier output)")}`
1660
+ ]);
1247
1661
  }
1248
- function cloneProvider(p) {
1249
- const models = {};
1250
- for (const [mid, m] of Object.entries(p.models ?? {})) {
1251
- models[mid] = { ...m };
1662
+ function renderGrepMatches(matches, mode) {
1663
+ if (matches.length === 0) return "(no matches)";
1664
+ if (mode === "files_with_matches") return renderStringList(matches, "(no files)");
1665
+ if (mode === "count") return renderStringList(matches, "(no counts)");
1666
+ const groups = /* @__PURE__ */ new Map();
1667
+ const passthrough = [];
1668
+ for (const match of matches) {
1669
+ const parsed = parseGrepContentLine(match);
1670
+ if (!parsed) {
1671
+ passthrough.push(match);
1672
+ continue;
1673
+ }
1674
+ const list = groups.get(parsed.file) ?? [];
1675
+ list.push(`${parsed.line}:${parsed.text}`);
1676
+ groups.set(parsed.file, list);
1252
1677
  }
1253
- return { ...p, models };
1678
+ if (groups.size === 0) return renderStringList(matches, "(no matches)");
1679
+ const sections = [];
1680
+ let fileIndex = 0;
1681
+ for (const [file, lines] of groups) {
1682
+ fileIndex++;
1683
+ if (fileIndex > GREP_FILE_LIMIT) break;
1684
+ const shown = lines.slice(0, GREP_MATCHES_PER_FILE);
1685
+ sections.push(
1686
+ `${file} (${lines.length} match(es), showing ${shown.length})
1687
+ ${shown.join("\n")}`
1688
+ );
1689
+ }
1690
+ if (groups.size > GREP_FILE_LIMIT) {
1691
+ sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);
1692
+ }
1693
+ if (passthrough.length > 0) {
1694
+ sections.push(`ungrouped:
1695
+ ${renderStringList(passthrough, "", 50)}`);
1696
+ }
1697
+ return sections.join("\n");
1698
+ }
1699
+ function parseGrepContentLine(line) {
1700
+ const match = /^(.+?):(\d+):(.*)$/.exec(line);
1701
+ if (!match?.[1] || !match[2]) return void 0;
1702
+ return { file: match[1], line: match[2], text: match[3] ?? "" };
1703
+ }
1704
+ function compactDiff(diff) {
1705
+ const lines = diff.split(/\r?\n/);
1706
+ if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;
1707
+ const fileCount = Math.max(
1708
+ new Set(
1709
+ lines.map(
1710
+ (line) => /^diff --git\s+a\/(.+?)\s+b\//.exec(line)?.[1] ?? /^---\s+(.+)/.exec(line)?.[1]
1711
+ ).filter(Boolean)
1712
+ ).size,
1713
+ 0
1714
+ );
1715
+ const hunks = lines.filter((line) => line.startsWith("@@")).length;
1716
+ const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
1717
+ const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
1718
+ const selected = /* @__PURE__ */ new Set();
1719
+ let hunkCount = 0;
1720
+ for (let i = 0; i < lines.length; i++) {
1721
+ const line = lines[i] ?? "";
1722
+ if (line.startsWith("diff --git") || line.startsWith("--- ") || line.startsWith("+++ ")) {
1723
+ selected.add(i);
1724
+ continue;
1725
+ }
1726
+ if (!line.startsWith("@@")) continue;
1727
+ if (hunkCount >= DIFF_HUNK_LIMIT) continue;
1728
+ hunkCount++;
1729
+ for (let j = i; j <= Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT); j++) {
1730
+ selected.add(j);
1731
+ }
1732
+ }
1733
+ if (selected.size === 0) {
1734
+ return joinSections([
1735
+ renderHeader("diff_summary", {
1736
+ files: fileCount,
1737
+ hunks,
1738
+ added,
1739
+ removed,
1740
+ lines: lines.length
1741
+ }),
1742
+ lines.slice(0, DIFF_INLINE_LINE_LIMIT).join("\n"),
1743
+ `[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`
1744
+ ]);
1745
+ }
1746
+ const excerpt = [];
1747
+ let previous = -1;
1748
+ for (const index of [...selected].sort((a, b) => a - b)) {
1749
+ if (index > previous + 1) {
1750
+ const omitted = previous === -1 ? index : index - previous - 1;
1751
+ excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
1752
+ }
1753
+ excerpt.push(lines[index] ?? "");
1754
+ previous = index;
1755
+ }
1756
+ const trailing = lines.length - previous - 1;
1757
+ if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
1758
+ return joinSections([
1759
+ renderHeader("diff_summary", {
1760
+ files: fileCount,
1761
+ hunks,
1762
+ shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),
1763
+ added,
1764
+ removed,
1765
+ lines: lines.length
1766
+ }),
1767
+ excerpt.join("\n")
1768
+ ]);
1769
+ }
1770
+ function compactFailureOutput(output) {
1771
+ const lines = output.split(/\r?\n/);
1772
+ if (lines.length <= 260) return output.trimEnd();
1773
+ const selected = /* @__PURE__ */ new Set();
1774
+ const marker = /\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\b/i;
1775
+ let markerHits = 0;
1776
+ for (let i = 0; i < lines.length; i++) {
1777
+ if (!marker.test(lines[i] ?? "")) continue;
1778
+ markerHits++;
1779
+ for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {
1780
+ selected.add(j);
1781
+ }
1782
+ }
1783
+ if (markerHits === 0) {
1784
+ return lines.slice(-220).join("\n").trimEnd();
1785
+ }
1786
+ const ordered = [...selected].sort((a, b) => a - b);
1787
+ const out = [];
1788
+ let previous = -1;
1789
+ for (const index of ordered) {
1790
+ if (index > previous + 1) {
1791
+ const omitted = previous === -1 ? index : index - previous - 1;
1792
+ out.push(`[serializer omitted ${omitted} line(s)]`);
1793
+ }
1794
+ out.push(lines[index] ?? "");
1795
+ previous = index;
1796
+ }
1797
+ return out.join("\n").trimEnd();
1798
+ }
1799
+ function extractSpoolNote(output) {
1800
+ return output.split(/\r?\n/).find((line) => line.startsWith("[output truncated") && line.includes("full"));
1801
+ }
1802
+ function hasCommandOutputShape(obj) {
1803
+ return typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string" || typeof obj["output"] === "string" || typeof obj["exitCode"] === "number" || typeof obj["exit_code"] === "number";
1804
+ }
1805
+ function renderCommandOutput(toolName, obj, input) {
1806
+ const command = stringField(obj, "command") ?? stringFromInput(input, "command");
1807
+ const args = stringArrayField(obj, "args");
1808
+ const commandLine = command ? [command, ...args].join(" ") : void 0;
1809
+ const output = stringField(obj, "output");
1810
+ const stdout = stringField(obj, "stdout");
1811
+ const stderr = stringField(obj, "stderr");
1812
+ return joinSections([
1813
+ renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {
1814
+ exit_code: obj["exit_code"] ?? obj["exitCode"],
1815
+ timed_out: obj["timed_out"],
1816
+ pid: obj["pid"],
1817
+ allowed: obj["allowed"],
1818
+ truncated: obj["truncated"],
1819
+ runner: obj["runner"],
1820
+ linter: obj["linter"],
1821
+ fixer: obj["fixer"],
1822
+ project: obj["project"],
1823
+ tests_run: obj["tests_run"],
1824
+ passed: obj["passed"],
1825
+ failed: obj["failed"],
1826
+ duration_ms: obj["duration_ms"],
1827
+ errors: obj["errors"],
1828
+ warnings: obj["warnings"],
1829
+ files_checked: obj["files_checked"],
1830
+ files_changed: obj["files_changed"],
1831
+ fix_applied: obj["fix_applied"]
1832
+ }),
1833
+ stringField(obj, "error") ? `error:
1834
+ ${stringField(obj, "error")}` : void 0,
1835
+ output ? `output:
1836
+ ${output}` : void 0,
1837
+ stdout ? `stdout:
1838
+ ${stdout}` : void 0,
1839
+ stderr ? `stderr:
1840
+ ${stderr}` : void 0
1841
+ ]);
1842
+ }
1843
+ function renderGenericToolObject(toolName, obj) {
1844
+ const scalars = {};
1845
+ const blocks = [];
1846
+ for (const [key, value] of Object.entries(obj)) {
1847
+ if (value === void 0) continue;
1848
+ if (isScalar(value)) {
1849
+ const inline = String(value);
1850
+ if (inline.length <= INLINE_LIMIT && !inline.includes("\n")) {
1851
+ scalars[key] = value;
1852
+ } else {
1853
+ blocks.push(`${key}:
1854
+ ${inline}`);
1855
+ }
1856
+ continue;
1857
+ }
1858
+ if (Array.isArray(value)) {
1859
+ if (value.every((item) => typeof item === "string")) {
1860
+ blocks.push(`${key}:
1861
+ ${renderStringList(value)}`);
1862
+ } else {
1863
+ blocks.push(`${key}:
1864
+ ${renderUnknownList(value)}`);
1865
+ }
1866
+ continue;
1867
+ }
1868
+ blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);
1869
+ }
1870
+ return joinSections([renderHeader(toolName, scalars), ...blocks]);
1254
1871
  }
1255
- function stripUndefined(obj) {
1256
- const out = {};
1257
- for (const [k, v] of Object.entries(obj)) {
1258
- if (v !== void 0) out[k] = v;
1872
+ function renderHeader(label, fields) {
1873
+ const parts = Object.entries(fields).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);
1874
+ return parts.length > 0 ? `${label} (${parts.join(" ")})` : label;
1875
+ }
1876
+ function renderStringList(items, empty = "", limit = DEFAULT_LIST_LIMIT) {
1877
+ if (items.length === 0) return empty;
1878
+ const shown = items.slice(0, limit);
1879
+ const omitted = items.length - shown.length;
1880
+ return [
1881
+ ...shown,
1882
+ ...omitted > 0 ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`] : []
1883
+ ].join("\n");
1884
+ }
1885
+ function renderUnknownList(items, limit = DEFAULT_LIST_LIMIT) {
1886
+ const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1e3));
1887
+ const omitted = items.length - shown.length;
1888
+ if (omitted > 0)
1889
+ shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);
1890
+ return shown.join("\n");
1891
+ }
1892
+ function joinSections(sections) {
1893
+ return sections.map((section) => typeof section === "string" ? section.trimEnd() : void 0).filter((section) => !!section).join("\n");
1894
+ }
1895
+ function formatInlineValue(value) {
1896
+ if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(",")}]`;
1897
+ if (isScalar(value)) return String(value);
1898
+ return oneLineJson(value);
1899
+ }
1900
+ function clipInline(value, max = INLINE_LIMIT) {
1901
+ const compact = value.replace(/\s+/g, " ").trim();
1902
+ return compact.length <= max ? compact : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;
1903
+ }
1904
+ function oneLineJson(value) {
1905
+ try {
1906
+ return JSON.stringify(value);
1907
+ } catch {
1908
+ return String(value);
1259
1909
  }
1260
- return out;
1910
+ }
1911
+ function stringField(obj, key) {
1912
+ const value = obj[key];
1913
+ return typeof value === "string" ? value : void 0;
1914
+ }
1915
+ function numberField(obj, key) {
1916
+ const value = obj[key];
1917
+ return typeof value === "number" ? value : void 0;
1918
+ }
1919
+ function stringArrayField(obj, key) {
1920
+ const value = obj[key];
1921
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
1922
+ }
1923
+ function stringFromInput(input, key) {
1924
+ if (!isRecord2(input)) return void 0;
1925
+ const value = input[key];
1926
+ return typeof value === "string" ? value : void 0;
1927
+ }
1928
+ function numberFromInput(input, key) {
1929
+ if (!isRecord2(input)) return void 0;
1930
+ const value = input[key];
1931
+ return typeof value === "number" ? value : void 0;
1932
+ }
1933
+ function inputListSummary(input, key) {
1934
+ if (!isRecord2(input)) return void 0;
1935
+ const value = input[key];
1936
+ if (typeof value === "string") return value;
1937
+ if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(",");
1938
+ return void 0;
1939
+ }
1940
+ function isRecord2(value) {
1941
+ return !!value && typeof value === "object" && !Array.isArray(value);
1942
+ }
1943
+ function isScalar(value) {
1944
+ return value === null || ["string", "number", "boolean"].includes(typeof value);
1945
+ }
1946
+ function projectHash(absRoot) {
1947
+ return createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 12);
1948
+ }
1949
+ function projectSlug(absRoot) {
1950
+ const base = slugify(path4.basename(absRoot));
1951
+ const hash = createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 6);
1952
+ return `${base}-${hash}`;
1953
+ }
1954
+ function slugify(name) {
1955
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
1956
+ }
1957
+ function wstackGlobalRoot() {
1958
+ const fromEnv = process.env["WRONGSTACK_HOME"];
1959
+ if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
1960
+ return path4.join(os.homedir(), ".wrongstack");
1961
+ }
1962
+ function resolveWstackPaths(opts) {
1963
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
1964
+ const hash = projectHash(opts.projectRoot);
1965
+ const slug = projectSlug(opts.projectRoot);
1966
+ const projectDir = path4.join(globalRoot, "projects", slug);
1967
+ return {
1968
+ globalRoot,
1969
+ configDir: globalRoot,
1970
+ globalConfig: path4.join(globalRoot, "config.json"),
1971
+ secretsKey: path4.join(globalRoot, ".key"),
1972
+ globalMemory: path4.join(globalRoot, "memory.md"),
1973
+ globalSkills: path4.join(globalRoot, "skills"),
1974
+ globalPrompts: path4.join(globalRoot, "prompts"),
1975
+ cacheDir: path4.join(globalRoot, "cache"),
1976
+ modelsCache: path4.join(globalRoot, "cache", "models.dev.json"),
1977
+ modelsOverlayCache: path4.join(globalRoot, "cache", "models-overlay.json"),
1978
+ historyFile: path4.join(globalRoot, "history"),
1979
+ logFile: path4.join(globalRoot, "logs", "wrongstack.log"),
1980
+ projectDir,
1981
+ projectCodebaseIndex: path4.join(projectDir, "codebase-index"),
1982
+ projectMemory: path4.join(projectDir, "memory.md"),
1983
+ projectSessions: path4.join(projectDir, "sessions"),
1984
+ projectTrust: path4.join(projectDir, "trust.json"),
1985
+ projectMeta: path4.join(projectDir, "meta.json"),
1986
+ projectLocalConfig: path4.join(projectDir, "config.local.json"),
1987
+ inProjectConfig: path4.join(opts.projectRoot, ".wrongstack", "config.json"),
1988
+ inProjectAgentsFile: path4.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
1989
+ inProjectSkills: path4.join(opts.projectRoot, ".wrongstack", "skills"),
1990
+ inProjectWorktrees: path4.join(opts.projectRoot, ".wrongstack", "worktrees"),
1991
+ projectHash: hash,
1992
+ projectSlug: slug,
1993
+ projectGoal: path4.join(projectDir, "goal.json"),
1994
+ projectSpecs: path4.join(projectDir, "specs"),
1995
+ projectTaskGraphs: path4.join(projectDir, "task-graphs"),
1996
+ projectSddSession: path4.join(projectDir, "sdd-session.json"),
1997
+ projectPlan: path4.join(projectDir, "plan.json"),
1998
+ projectAutophase: path4.join(projectDir, "autophase"),
1999
+ syncConfig: path4.join(globalRoot, "sync.json"),
2000
+ projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
2001
+ };
1261
2002
  }
1262
2003
 
1263
2004
  // src/storage/session-store.ts
@@ -1339,11 +2080,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
1339
2080
  }
1340
2081
  /** Absolute path to the session index file. */
1341
2082
  get indexFile() {
1342
- return path3.join(this.dir, "_index.jsonl");
2083
+ return path4.join(this.dir, "_index.jsonl");
1343
2084
  }
1344
2085
  /** Join session ID to its absolute path within the store directory. */
1345
2086
  sessionPath(id, ext) {
1346
- return path3.join(this.dir, `${id}${ext}`);
2087
+ return path4.join(this.dir, `${id}${ext}`);
1347
2088
  }
1348
2089
  /**
1349
2090
  * Ensure the directory implied by the session ID exists. When the ID
@@ -1351,7 +2092,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1351
2092
  * subdirectory so sessions group naturally by day.
1352
2093
  */
1353
2094
  async ensureShardDir(id) {
1354
- const dirPath = path3.dirname(path3.join(this.dir, id));
2095
+ const dirPath = path4.dirname(path4.join(this.dir, id));
1355
2096
  await ensureDir(dirPath);
1356
2097
  return dirPath;
1357
2098
  }
@@ -1359,7 +2100,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1359
2100
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1360
2101
  const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
1361
2102
  const shardDir = await this.ensureShardDir(id);
1362
- const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
2103
+ const file = path4.join(shardDir, `${path4.basename(id)}.jsonl`);
1363
2104
  const t0 = Date.now();
1364
2105
  let handle;
1365
2106
  try {
@@ -1421,7 +2162,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1421
2162
  // Shard directory (sessions/<date>/) — must match create() so the
1422
2163
  // .summary.json sidecar lands next to the JSONL instead of the
1423
2164
  // sessions root (where summaryFor() would never find it).
1424
- dir: path3.dirname(file),
2165
+ dir: path4.dirname(file),
1425
2166
  filePath: file,
1426
2167
  secretScrubber: this.secretScrubber,
1427
2168
  onClose: (s) => this.appendToIndex(s)
@@ -1645,7 +2386,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1645
2386
  continue;
1646
2387
  if (entry.isDirectory()) {
1647
2388
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
1648
- ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
2389
+ ids.push(...await this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1));
1649
2390
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
1650
2391
  if (entry.name === "_index.jsonl") continue;
1651
2392
  const base = entry.name.replace(/\.jsonl$/, "");
@@ -1696,14 +2437,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
1696
2437
  async deleteSession(id) {
1697
2438
  const jsonlPath = this.sessionPath(id, ".jsonl");
1698
2439
  const summaryPath = this.sessionPath(id, ".summary.json");
1699
- const shardDir = path3.dirname(path3.join(this.dir, id));
1700
- const base = path3.basename(id);
1701
- const sessDir = path3.join(shardDir, base);
2440
+ const shardDir = path4.dirname(path4.join(this.dir, id));
2441
+ const base = path4.basename(id);
2442
+ const sessDir = path4.join(shardDir, base);
1702
2443
  const deletions = [
1703
2444
  fsp2.unlink(jsonlPath),
1704
2445
  fsp2.unlink(summaryPath),
1705
- fsp2.unlink(path3.join(shardDir, `${base}.plan.json`)),
1706
- fsp2.unlink(path3.join(shardDir, `${base}.todos.json`))
2446
+ fsp2.unlink(path4.join(shardDir, `${base}.plan.json`)),
2447
+ fsp2.unlink(path4.join(shardDir, `${base}.todos.json`))
1707
2448
  ];
1708
2449
  const results = await Promise.allSettled(deletions);
1709
2450
  for (const r of results) {
@@ -1739,14 +2480,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
1739
2480
  let deleted = 0;
1740
2481
  let activeSessionId = null;
1741
2482
  try {
1742
- const raw = await fsp2.readFile(path3.join(this.dir, "active.json"), "utf8");
2483
+ const raw = await fsp2.readFile(path4.join(this.dir, "active.json"), "utf8");
1743
2484
  const active = JSON.parse(raw);
1744
2485
  activeSessionId = active.sessionId ?? null;
1745
2486
  } catch {
1746
2487
  }
1747
2488
  const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
1748
2489
  const pruneFile = async (dir, name, prefix) => {
1749
- const jsonlPath = path3.join(dir, name);
2490
+ const jsonlPath = path4.join(dir, name);
1750
2491
  try {
1751
2492
  const stat6 = await fsp2.stat(jsonlPath);
1752
2493
  if (stat6.mtimeMs >= cutoff) return;
@@ -1766,7 +2507,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1766
2507
  continue;
1767
2508
  }
1768
2509
  if (!entry.isDirectory()) continue;
1769
- const dateDir = path3.join(this.dir, entry.name);
2510
+ const dateDir = path4.join(this.dir, entry.name);
1770
2511
  const files = await fsp2.readdir(dateDir, { withFileTypes: true }).catch(() => []);
1771
2512
  for (const file of files) {
1772
2513
  if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
@@ -1778,7 +2519,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
1778
2519
  }
1779
2520
  for (const entry of entries) {
1780
2521
  if (!entry.isDirectory()) continue;
1781
- const dateDir = path3.join(this.dir, entry.name);
2522
+ const dateDir = path4.join(this.dir, entry.name);
1782
2523
  try {
1783
2524
  const remaining = await fsp2.readdir(dateDir);
1784
2525
  if (remaining.length === 0) {
@@ -1953,7 +2694,7 @@ var FileSessionWriter = class _FileSessionWriter {
1953
2694
  this.meta = meta;
1954
2695
  this.events = events;
1955
2696
  this.resumed = opts.resumed ?? false;
1956
- this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
2697
+ this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
1957
2698
  this.filePath = opts.filePath ?? "";
1958
2699
  this.secretScrubber = opts.secretScrubber;
1959
2700
  this.onCloseCb = opts.onClose;
@@ -2457,7 +3198,7 @@ var QueueStore = class {
2457
3198
  events;
2458
3199
  traceId;
2459
3200
  constructor(opts) {
2460
- this.file = path3.join(opts.dir, "queue.json");
3201
+ this.file = path4.join(opts.dir, "queue.json");
2461
3202
  this.events = opts.events;
2462
3203
  this.traceId = opts.traceId;
2463
3204
  }
@@ -2644,7 +3385,7 @@ var DefaultAttachmentStore = class {
2644
3385
  let data = input.data;
2645
3386
  if (this.spoolDir && bytes >= this.spoolThreshold) {
2646
3387
  await fsp2.mkdir(this.spoolDir, { recursive: true });
2647
- spooledPath = path3.join(this.spoolDir, `${id}.bin`);
3388
+ spooledPath = path4.join(this.spoolDir, `${id}.bin`);
2648
3389
  await atomicWrite(spooledPath, input.data, {
2649
3390
  encoding: input.kind === "image" ? "base64" : "utf8"
2650
3391
  });
@@ -2856,7 +3597,7 @@ var FileMemoryBackend = class {
2856
3597
  }
2857
3598
  async remember(scope, entry, filePath) {
2858
3599
  const file = this.resolveFile(filePath, scope);
2859
- await ensureDir(path3.dirname(file));
3600
+ await ensureDir(path4.dirname(file));
2860
3601
  let existing = "";
2861
3602
  try {
2862
3603
  existing = await fsp2.readFile(file, "utf8");
@@ -3472,9 +4213,9 @@ ${body.trim()}`);
3472
4213
  if (!this.persistBackup || scope === "project-agents") return;
3473
4214
  try {
3474
4215
  const content = await this.backend.readAll(scope, this.files[scope]);
3475
- const { writeFile: writeFile4, mkdir: mkdir6 } = await import('fs/promises');
3476
- await mkdir6(this.backupDir, { recursive: true });
3477
- await writeFile4(`${this.backupDir}/${scope}.md`, content, "utf8");
4216
+ const { writeFile: writeFile5, mkdir: mkdir7 } = await import('fs/promises');
4217
+ await mkdir7(this.backupDir, { recursive: true });
4218
+ await writeFile5(`${this.backupDir}/${scope}.md`, content, "utf8");
3478
4219
  } catch {
3479
4220
  }
3480
4221
  }
@@ -3824,7 +4565,7 @@ var DefaultSecretVault = class {
3824
4565
  KEY_FILE_MAGIC.copy(keyFileBuf, 0);
3825
4566
  keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
3826
4567
  newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
3827
- fs4.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
4568
+ fs4.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
3828
4569
  fs4.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
3829
4570
  checkKeyFilePermissions(this.keyFile);
3830
4571
  this.key = newKey;
@@ -3872,7 +4613,7 @@ var DefaultSecretVault = class {
3872
4613
  } catch (err) {
3873
4614
  if (err.code !== "ENOENT") throw err;
3874
4615
  }
3875
- fs4.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
4616
+ fs4.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
3876
4617
  const key = randomBytes(KEY_BYTES);
3877
4618
  try {
3878
4619
  fs4.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
@@ -3962,7 +4703,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
3962
4703
  }
3963
4704
  const merged = deepMerge(current, patch ?? {});
3964
4705
  const encrypted = encryptConfigSecrets(merged, vault);
3965
- await fsp2.mkdir(path3.dirname(configPath), { recursive: true });
4706
+ await fsp2.mkdir(path4.dirname(configPath), { recursive: true });
3966
4707
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3967
4708
  await restrictFilePermissions(configPath);
3968
4709
  }
@@ -4592,7 +5333,7 @@ var RecoveryLock = class {
4592
5333
  sessionStore;
4593
5334
  probe;
4594
5335
  constructor(opts) {
4595
- this.file = path3.join(opts.dir, LOCK_FILE);
5336
+ this.file = path4.join(opts.dir, LOCK_FILE);
4596
5337
  this.pid = opts.pid ?? process.pid;
4597
5338
  this.hostname = opts.hostname ?? os.hostname();
4598
5339
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -4653,7 +5394,7 @@ var RecoveryLock = class {
4653
5394
  * null return before calling this.
4654
5395
  */
4655
5396
  async write(sessionId) {
4656
- await ensureDir(path3.dirname(this.file));
5397
+ await ensureDir(path4.dirname(this.file));
4657
5398
  const lock = {
4658
5399
  v: 1,
4659
5400
  sessionId,
@@ -6073,9 +6814,9 @@ function getInputString(input, key) {
6073
6814
  function pathLooksInsideProject(rawPath, projectRoot) {
6074
6815
  if (!projectRoot) return false;
6075
6816
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
6076
- const resolved = path3.resolve(projectRoot, rawPath);
6077
- const relative2 = path3.relative(projectRoot, resolved);
6078
- return !!relative2 && !relative2.startsWith("..") && !path3.isAbsolute(relative2);
6817
+ const resolved = path4.resolve(projectRoot, rawPath);
6818
+ const relative3 = path4.relative(projectRoot, resolved);
6819
+ return !!relative3 && !relative3.startsWith("..") && !path4.isAbsolute(relative3);
6079
6820
  }
6080
6821
  function tokenizeShell(command) {
6081
6822
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -6085,7 +6826,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
6085
6826
  if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
6086
6827
  if (token.includes("*")) return true;
6087
6828
  if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
6088
- if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
6829
+ if (path4.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
6089
6830
  return false;
6090
6831
  }
6091
6832
  function hasDangerousDeleteTarget(tokens, start, projectRoot) {
@@ -6480,9 +7221,12 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
6480
7221
  const caps = tool.capabilities ?? [];
6481
7222
  const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
6482
7223
  const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
6483
- const blocked = tool.permission === "deny" || isMcp || !hasAllowedCap;
7224
+ const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
7225
+ (c) => !this.allowedCapabilities.includes(c)
7226
+ );
7227
+ const blocked = tool.permission === "deny" || isMcp || !hasAllowedCap || dangerousNotAllowed.length > 0;
6484
7228
  if (blocked) {
6485
- const reason = isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : tool.permission === "deny" ? "tool default deny" : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
7229
+ const reason = isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
6486
7230
  return {
6487
7231
  permission: "deny",
6488
7232
  source: "subagent_guard",
@@ -6742,7 +7486,7 @@ var DefaultSkillLoader = class {
6742
7486
  const entries = await fsp2.readdir(dir, { withFileTypes: true });
6743
7487
  for (const e of entries) {
6744
7488
  if (!e.isDirectory()) continue;
6745
- const skillFile = path3.join(dir, e.name, "SKILL.md");
7489
+ const skillFile = path4.join(dir, e.name, "SKILL.md");
6746
7490
  try {
6747
7491
  const raw = await fsp2.readFile(skillFile, "utf8");
6748
7492
  const meta = parseFrontmatter(raw);
@@ -6813,7 +7557,7 @@ var DefaultSkillLoader = class {
6813
7557
  if (cached !== void 0) return cached;
6814
7558
  const m = await this.find(name);
6815
7559
  if (!m) throw new Error(`Skill "${name}" not found`);
6816
- const savePath = path3.join(path3.dirname(m.path), "SKILL.save.md");
7560
+ const savePath = path4.join(path4.dirname(m.path), "SKILL.save.md");
6817
7561
  let result;
6818
7562
  try {
6819
7563
  result = await fsp2.readFile(savePath, "utf8");
@@ -7127,8 +7871,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
7127
7871
  });
7128
7872
  await Promise.race([
7129
7873
  drainPromise,
7130
- new Promise((resolve5) => {
7131
- drainTimer = setTimeout(resolve5, STREAM_DRAIN_TIMEOUT_MS);
7874
+ new Promise((resolve6) => {
7875
+ drainTimer = setTimeout(resolve6, STREAM_DRAIN_TIMEOUT_MS);
7132
7876
  })
7133
7877
  ]);
7134
7878
  } finally {
@@ -7234,26 +7978,29 @@ async function runProviderWithRetry(opts) {
7234
7978
  description
7235
7979
  });
7236
7980
  }
7237
- await new Promise((resolve5, reject) => {
7981
+ await new Promise((resolve6, reject) => {
7238
7982
  let settled = false;
7983
+ const cleanup = () => {
7984
+ clearTimeout(t);
7985
+ signal.removeEventListener("abort", onAbort);
7986
+ };
7239
7987
  const onAbort = () => {
7240
7988
  if (settled) return;
7241
7989
  settled = true;
7242
- clearTimeout(t);
7990
+ cleanup();
7243
7991
  reject(new Error("aborted"));
7244
7992
  };
7245
7993
  const t = setTimeout(() => {
7246
7994
  if (settled) return;
7247
7995
  settled = true;
7248
- clearTimeout(t);
7249
- signal.removeEventListener("abort", onAbort);
7250
- resolve5();
7996
+ cleanup();
7997
+ resolve6();
7251
7998
  }, delay);
7252
7999
  if (signal.aborted) {
7253
8000
  onAbort();
7254
8001
  return;
7255
8002
  }
7256
- signal.addEventListener("abort", onAbort, { once: true });
8003
+ signal.addEventListener("abort", onAbort);
7257
8004
  });
7258
8005
  attempt++;
7259
8006
  }
@@ -7312,25 +8059,26 @@ function findPreserveStart(messages, preserveK) {
7312
8059
  preserveStart = i;
7313
8060
  }
7314
8061
  }
7315
- let forwardWalkIterations = 0;
7316
- let forwardWalkInnerIterations = 0;
7317
- for (let i = preserveStart; i < messages.length; i++) {
7318
- forwardWalkIterations++;
7319
- const m = messages[i];
7320
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
7321
- const hasToolUse3 = m.content.some((b) => {
7322
- forwardWalkInnerIterations++;
7323
- return b.type === "tool_use";
8062
+ let pairRepairIterations = 0;
8063
+ let pairRepairInnerIterations = 0;
8064
+ while (preserveStart > 0) {
8065
+ pairRepairIterations++;
8066
+ const first = messages[preserveStart];
8067
+ const prev = messages[preserveStart - 1];
8068
+ if (!first || !prev || first.role !== "user" || prev.role !== "assistant") break;
8069
+ if (typeof first.content === "string" || typeof prev.content === "string") break;
8070
+ const resultIds = /* @__PURE__ */ new Set();
8071
+ for (const block of first.content) {
8072
+ pairRepairInnerIterations++;
8073
+ if (block.type === "tool_result") resultIds.add(block.tool_use_id);
8074
+ }
8075
+ if (resultIds.size === 0) break;
8076
+ const hasMatchingUse = prev.content.some((block) => {
8077
+ pairRepairInnerIterations++;
8078
+ return block.type === "tool_use" && resultIds.has(block.id);
7324
8079
  });
7325
- if (hasToolUse3 && i + 1 < messages.length) {
7326
- const next = messages[i + 1];
7327
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => {
7328
- forwardWalkInnerIterations++;
7329
- return b.type === "tool_result";
7330
- })) {
7331
- preserveStart = i + 1;
7332
- }
7333
- }
8080
+ if (!hasMatchingUse) break;
8081
+ preserveStart--;
7334
8082
  }
7335
8083
  if (compactionDebugEnabled()) {
7336
8084
  console.log(
@@ -7340,9 +8088,9 @@ function findPreserveStart(messages, preserveK) {
7340
8088
  messageCount: messages.length,
7341
8089
  preserveK,
7342
8090
  preserveStart,
7343
- forwardWalkIterations,
7344
- forwardWalkInnerIterations,
7345
- forwardWalkInnerPerOuter: forwardWalkIterations > 0 ? forwardWalkInnerIterations / forwardWalkIterations : 0
8091
+ pairRepairIterations,
8092
+ pairRepairInnerIterations,
8093
+ pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
7346
8094
  })
7347
8095
  );
7348
8096
  }
@@ -7359,7 +8107,8 @@ function eliseOldToolResults(messages, opts) {
7359
8107
  if (!msg || !Array.isArray(msg.content)) continue;
7360
8108
  for (const b of msg.content) {
7361
8109
  fastPathInnerIterations++;
7362
- if (b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold) {
8110
+ const oversized = b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold || b.type === "tool_use" && estimateToolInputTokens(b.input) >= opts.eliseThreshold;
8111
+ if (oversized) {
7363
8112
  hasOversized = true;
7364
8113
  break;
7365
8114
  }
@@ -7393,6 +8142,13 @@ function eliseOldToolResults(messages, opts) {
7393
8142
  }
7394
8143
  const original = msg.content;
7395
8144
  const newContent = original.map((b) => {
8145
+ if (b.type === "tool_use") {
8146
+ const tokens2 = estimateToolInputTokens(b.input);
8147
+ if (tokens2 < opts.eliseThreshold) return b;
8148
+ const elidedInput = summarizeToolUseInputElision(b, tokens2);
8149
+ saved += Math.max(0, tokens2 - estimateToolInputTokens(elidedInput));
8150
+ return { ...b, input: elidedInput };
8151
+ }
7396
8152
  if (b.type !== "tool_result") return b;
7397
8153
  const tokens = estimateToolResultTokens(b.content);
7398
8154
  if (tokens < opts.eliseThreshold) return b;
@@ -7400,7 +8156,7 @@ function eliseOldToolResults(messages, opts) {
7400
8156
  const elided = {
7401
8157
  type: "tool_result",
7402
8158
  tool_use_id: b.tool_use_id,
7403
- content: `[elided: ~${tokens} tokens]`,
8159
+ content: summarizeToolResultElision(b, tokens),
7404
8160
  is_error: b.is_error
7405
8161
  };
7406
8162
  return elided;
@@ -7440,6 +8196,65 @@ function eliseOldToolResults(messages, opts) {
7440
8196
  });
7441
8197
  return { messages: changed ? next : messages, saved, changed };
7442
8198
  }
8199
+ function summarizeToolUseInputElision(block, tokens) {
8200
+ const fields = {};
8201
+ for (const [key, value] of Object.entries(block.input ?? {})) {
8202
+ fields[key] = summarizeToolUseInputValue(value);
8203
+ }
8204
+ return {
8205
+ __elided_tool_input: `~${tokens} tokens; original arguments are in the session log`,
8206
+ tool: block.name,
8207
+ fields
8208
+ };
8209
+ }
8210
+ function summarizeToolUseInputValue(value) {
8211
+ if (value === null || value === void 0) return value;
8212
+ if (typeof value === "number" || typeof value === "boolean") return value;
8213
+ if (typeof value === "string") {
8214
+ const oneLine = value.replace(/\s+/g, " ").trim();
8215
+ return oneLine.length <= 160 ? oneLine : `${oneLine.slice(0, 120)}...(${oneLine.length} chars)`;
8216
+ }
8217
+ if (Array.isArray(value)) {
8218
+ return `[array:${value.length}]`;
8219
+ }
8220
+ if (typeof value === "object") {
8221
+ const keys = Object.keys(value);
8222
+ return `[object:${keys.slice(0, 8).join(",")}${keys.length > 8 ? ",..." : ""}]`;
8223
+ }
8224
+ return String(value);
8225
+ }
8226
+ function summarizeToolResultElision(block, tokens) {
8227
+ const parts = [`elided: ~${tokens} tokens`];
8228
+ if (block.name) parts.push(`tool=${block.name}`);
8229
+ const files = extractPathHints(block.content).slice(0, 5);
8230
+ if (files.length > 0) parts.push(`files=${files.join(", ")}`);
8231
+ const error = firstErrorLine(block.content);
8232
+ if (error) parts.push(`error=${error}`);
8233
+ return `[${parts.join("; ")}]`;
8234
+ }
8235
+ function extractPathHints(content) {
8236
+ const text = typeof content === "string" ? content : JSON.stringify(content);
8237
+ const out = /* @__PURE__ */ new Set();
8238
+ const re = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
8239
+ for (const match of text.matchAll(re)) {
8240
+ const clean = match[0]?.replace(/\\/g, "/").replace(/^["'`]+|["'`),;:]+$/g, "");
8241
+ if (clean && clean.length <= 220) out.add(clean);
8242
+ if (out.size >= 5) break;
8243
+ }
8244
+ return [...out];
8245
+ }
8246
+ function firstErrorLine(content) {
8247
+ const text = typeof content === "string" ? content : JSON.stringify(content);
8248
+ for (const line of text.split(/\r?\n/)) {
8249
+ if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i.test(
8250
+ line
8251
+ ))
8252
+ continue;
8253
+ const trimmed = line.replace(/\s+/g, " ").trim();
8254
+ if (trimmed) return trimmed.slice(0, 180);
8255
+ }
8256
+ return void 0;
8257
+ }
7443
8258
  function buildLosslessDigest(messages) {
7444
8259
  const lines = [];
7445
8260
  for (const m of messages) {
@@ -7550,15 +8365,15 @@ function buildSmartDigest(messages) {
7550
8365
  lines.push(`[${m.role}]: ${display}${marker}`);
7551
8366
  }
7552
8367
  if (noiseCount > 0) {
7553
- lines.push(`[system]: ${noiseCount} low-importance turn(s) collapsed (repeated failures / pure tool I/O)`);
8368
+ lines.push(
8369
+ `[system]: ${noiseCount} low-importance turn(s) collapsed (repeated failures / pure tool I/O)`
8370
+ );
7554
8371
  }
7555
8372
  return lines.join("\n");
7556
8373
  }
7557
8374
  function countToolBlocks(m) {
7558
8375
  if (typeof m.content === "string") return 0;
7559
- return m.content.filter(
7560
- (b) => b.type === "tool_use" || b.type === "tool_result"
7561
- ).length;
8376
+ return m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result").length;
7562
8377
  }
7563
8378
  function firstSentence(text) {
7564
8379
  const trimmed = text.trim();
@@ -7627,10 +8442,12 @@ var HybridCompactor = class {
7627
8442
  if (elide.changed) ctx.state.replaceMessages(elide.messages);
7628
8443
  if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
7629
8444
  let collapsedDigest;
8445
+ let evidenceDigest;
7630
8446
  if (opts.aggressive) {
7631
8447
  const phase2 = this.collapseAncientTurns(ctx, preserveK);
7632
8448
  if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
7633
8449
  collapsedDigest = phase2.digest;
8450
+ evidenceDigest = phase2.evidenceDigest;
7634
8451
  }
7635
8452
  const repaired = repairToolUseAdjacency(ctx.messages);
7636
8453
  if (repaired.report.changed) {
@@ -7638,6 +8455,11 @@ var HybridCompactor = class {
7638
8455
  }
7639
8456
  const afterTokens = estimateMessages(ctx.messages);
7640
8457
  const afterFull = this.estimateFullRequest(ctx);
8458
+ const quality = checkCompactionQuality(ctx, {
8459
+ collapsedDigest,
8460
+ evidenceDigest,
8461
+ reduced: beforeTokens > afterTokens || beforeFull > afterFull
8462
+ });
7641
8463
  return {
7642
8464
  before: beforeTokens,
7643
8465
  after: afterTokens,
@@ -7645,6 +8467,8 @@ var HybridCompactor = class {
7645
8467
  fullRequestTokensAfter: afterFull,
7646
8468
  reductions,
7647
8469
  collapsedDigest,
8470
+ evidenceDigest,
8471
+ quality,
7648
8472
  repaired: repaired.report.changed ? {
7649
8473
  removedToolUses: repaired.report.removedToolUses,
7650
8474
  removedToolResults: repaired.report.removedToolResults,
@@ -7686,7 +8510,13 @@ var HybridCompactor = class {
7686
8510
  if (boundary <= 0) return { saved: 0 };
7687
8511
  const removed = messages.slice(0, boundary);
7688
8512
  const removedTokens = estimateMessages(removed);
7689
- const digest = this.smart ? buildSmartDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)` : buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)`;
8513
+ const historyDigest = this.smart ? buildSmartDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted; see session log)` : buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted; see session log)`;
8514
+ const evidenceDigest = buildContextEvidenceDigest(ctx);
8515
+ const digest = evidenceDigest ? `[context_state]
8516
+ ${evidenceDigest}
8517
+
8518
+ [prior_history]
8519
+ ${historyDigest}` : historyDigest;
7690
8520
  const summaryMsg = {
7691
8521
  role: "system",
7692
8522
  content: `[prior_turns_digest: ${digest}]`
@@ -7695,10 +8525,29 @@ var HybridCompactor = class {
7695
8525
  ctx.state.replaceMessages([summaryMsg, ...tail]);
7696
8526
  return {
7697
8527
  saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
7698
- digest
8528
+ digest,
8529
+ evidenceDigest: evidenceDigest || void 0
7699
8530
  };
7700
8531
  }
7701
8532
  };
8533
+ function checkCompactionQuality(ctx, opts) {
8534
+ const evidence = ctx.contextEvidence;
8535
+ const digest = `${opts.collapsedDigest ?? ""}
8536
+ ${opts.evidenceDigest ?? ""}`;
8537
+ const hasIntent = Boolean(evidence?.currentIntent?.text || /\b(intent|goal|session_goals)\b/i.test(digest));
8538
+ const hasPathTrail = Boolean(
8539
+ Object.keys(evidence?.fileGraph ?? {}).length > 0 || (evidence?.toolCalls.length ?? 0) > 0 || /\b(dependency_graph|tool_trail|files=)\b/i.test(digest)
8540
+ );
8541
+ const issues = [];
8542
+ if (opts.reduced && !hasIntent) issues.push("missing intent anchor");
8543
+ if (opts.reduced && !hasPathTrail) issues.push("missing tool/path trail");
8544
+ return {
8545
+ ok: issues.length === 0,
8546
+ hasIntent,
8547
+ hasPathTrail,
8548
+ issues
8549
+ };
8550
+ }
7702
8551
  function readContextWindowPolicy(ctx) {
7703
8552
  const policy = ctx.meta?.["contextWindowPolicy"];
7704
8553
  if (!policy || typeof policy !== "object") return null;
@@ -8057,10 +8906,10 @@ var SelectiveCompactor = class {
8057
8906
  this.maxContext = opts.maxContext ?? 128e3;
8058
8907
  this.preserveK = opts.preserveK ?? 4;
8059
8908
  this.eliseThreshold = opts.eliseThreshold ?? 500;
8060
- this.summarizerModel = opts.summarizerModel ?? opts.selectorModel ?? "unknown";
8061
- if (this.summarizerModel === "unknown" && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
8909
+ this.summarizerModel = opts.summarizerModel ?? opts.selectorModel;
8910
+ if (this.summarizerModel === void 0 && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
8062
8911
  console.warn(
8063
- "[SelectiveCompactor] summarizerModel not set \u2014 will use provider default. Set `summarizerModel` explicitly to silence this warning."
8912
+ "[SelectiveCompactor] summarizerModel not set \u2014 will fall back to ctx.model at summarize time. Set `summarizerModel` explicitly to silence this warning."
8064
8913
  );
8065
8914
  }
8066
8915
  this.summarizerPrompt = opts.summarizerPrompt ?? "You are a context summarizer. Given a list of messages, produce a concise summary that preserves all factual information, decisions, file changes, and state changes. Do not add commentary or opinions.";
@@ -8168,7 +9017,7 @@ var SelectiveCompactor = class {
8168
9017
  Summarize the following message range:`;
8169
9018
  const body = messages.map((m, i) => `[${i}] ${m.role}: ${this.messagePreview(m)}`).join("\n");
8170
9019
  const req = {
8171
- model: this.summarizerModel,
9020
+ model: this.summarizerModel ?? ctx.model,
8172
9021
  system: [{ type: "text", text: systemText }],
8173
9022
  messages: [{ role: "user", content: body }],
8174
9023
  maxTokens: 512
@@ -8235,27 +9084,16 @@ Summarize the following message range:`;
8235
9084
  if (typeof m.content === "string") return m.content.trim().length > 0;
8236
9085
  return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
8237
9086
  }
9087
+ /**
9088
+ * Estimate message-array tokens via the shared `estimateMessages` primitive
9089
+ * so SelectiveCompactor's before/after/load figures agree with the
9090
+ * middleware threshold math and the other compactors. Previously this used a
9091
+ * private `ceil(len/3.5)` walk that diverged from the calibrated shared
9092
+ * estimator, causing the selective `load`/`targetBudget` comparison to mix
9093
+ * two incompatible token scales.
9094
+ */
8238
9095
  estimateTokens(messages) {
8239
- let total = 0;
8240
- for (const m of messages) {
8241
- if (typeof m.content === "string") {
8242
- total += this.roughTokenEstimate(m.content);
8243
- } else {
8244
- for (const b of m.content) {
8245
- if (b.type === "text") total += this.roughTokenEstimate(b.text);
8246
- else if (b.type === "tool_use") total += this.roughTokenEstimate(JSON.stringify(b.input));
8247
- else if (b.type === "tool_result") {
8248
- total += this.roughTokenEstimate(
8249
- typeof b.content === "string" ? b.content : JSON.stringify(b.content)
8250
- );
8251
- }
8252
- }
8253
- }
8254
- }
8255
- return total;
8256
- }
8257
- roughTokenEstimate(text) {
8258
- return Math.max(1, Math.ceil(text.length / 3.5));
9096
+ return estimateMessages(messages);
8259
9097
  }
8260
9098
  };
8261
9099
 
@@ -8446,15 +9284,20 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8446
9284
  this._cachedMsgCount = msgCount;
8447
9285
  this._cachedToolCount = toolCount;
8448
9286
  }
8449
- const load = tokens / this._maxContext;
9287
+ const budget = computeContextWindowBudget(ctx, tokens, this._maxContext);
9288
+ const load = budget.load;
8450
9289
  const policy = this.policyProvider?.(ctx);
8451
9290
  const thresholds = policy?.thresholds ?? {
8452
9291
  warn: this.warnThreshold,
8453
9292
  soft: this.softThreshold,
8454
9293
  hard: this.hardThreshold
8455
9294
  };
9295
+ const repetition = repeatedReadPressure(ctx);
9296
+ const adaptiveThresholds = adaptThresholdsForSignals(thresholds, {
9297
+ repeatedReadCount: repetition
9298
+ });
8456
9299
  const aggressiveOn = policy?.aggressiveOn ?? this.aggressiveOn;
8457
- const level = load >= thresholds.hard ? "hard" : load >= thresholds.soft ? "soft" : load >= thresholds.warn ? "warn" : null;
9300
+ const level = load >= adaptiveThresholds.hard ? "hard" : load >= adaptiveThresholds.soft ? "soft" : load >= adaptiveThresholds.warn ? "warn" : null;
8458
9301
  if (!level) {
8459
9302
  this.lastNoopAttempt = null;
8460
9303
  return next(ctx);
@@ -8463,7 +9306,13 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8463
9306
  return next(ctx);
8464
9307
  }
8465
9308
  const aggressive = level === "hard" ? true : level === "soft" ? aggressiveOn !== "hard" : aggressiveOn === "warn";
8466
- await this.compact(ctx, aggressive, { level, tokens, load });
9309
+ await this.compact(ctx, aggressive, {
9310
+ level,
9311
+ tokens,
9312
+ load,
9313
+ budget,
9314
+ signals: { repeatedReadCount: repetition }
9315
+ });
8467
9316
  return next(ctx);
8468
9317
  };
8469
9318
  }
@@ -8516,6 +9365,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8516
9365
  tokens: pressure.tokens,
8517
9366
  load: pressure.load,
8518
9367
  maxContext: this._maxContext,
9368
+ budget: pressure.budget,
9369
+ signals: pressure.signals,
8519
9370
  report,
8520
9371
  aggressive
8521
9372
  });
@@ -8527,6 +9378,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8527
9378
  level: pressure.level,
8528
9379
  aggressive,
8529
9380
  reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
9381
+ budget: pressure.budget,
9382
+ signals: pressure.signals,
8530
9383
  // Record what was collapsed so the audit trail shows the preserved
8531
9384
  // content, not just token counts. Bounded to keep the log line small;
8532
9385
  // the full original turns are already in the session JSONL.
@@ -8542,6 +9395,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8542
9395
  level: pressure.level,
8543
9396
  tokens: pressure.tokens,
8544
9397
  maxContext: this._maxContext,
9398
+ budget: pressure.budget,
9399
+ signals: pressure.signals,
8545
9400
  load: pressure.load,
8546
9401
  fatal
8547
9402
  });
@@ -8561,8 +9416,37 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
8561
9416
  }
8562
9417
  }
8563
9418
  };
8564
-
8565
- // src/execution/tool-executor.ts
9419
+ function computeContextWindowBudget(ctx, inputTokens, maxContext) {
9420
+ const reservedOutputTokens = readPositiveMetaNumber(ctx, "contextOutputReserveTokens") ?? Math.floor(Math.min(8192, maxContext * 0.08));
9421
+ const reservedSafetyTokens = readPositiveMetaNumber(ctx, "contextSafetyBufferTokens") ?? Math.floor(Math.min(4096, maxContext * 0.02));
9422
+ const availableInputTokens = Math.max(
9423
+ 1,
9424
+ maxContext - reservedOutputTokens - reservedSafetyTokens
9425
+ );
9426
+ const remainingInputTokens = availableInputTokens - inputTokens;
9427
+ return {
9428
+ maxContext,
9429
+ inputTokens,
9430
+ availableInputTokens,
9431
+ remainingInputTokens,
9432
+ reservedOutputTokens,
9433
+ reservedSafetyTokens,
9434
+ load: inputTokens / availableInputTokens,
9435
+ overflowTokens: Math.max(0, -remainingInputTokens)
9436
+ };
9437
+ }
9438
+ function readPositiveMetaNumber(ctx, key) {
9439
+ const value = ctx.meta?.[key];
9440
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
9441
+ }
9442
+ function adaptThresholdsForSignals(thresholds, signals) {
9443
+ if (signals.repeatedReadCount < 3) return thresholds;
9444
+ return {
9445
+ warn: Math.max(0.25, thresholds.warn - 0.08),
9446
+ soft: Math.max(0.35, thresholds.soft - 0.04),
9447
+ hard: thresholds.hard
9448
+ };
9449
+ }
8566
9450
  var ToolExecutor = class _ToolExecutor {
8567
9451
  constructor(registry, opts) {
8568
9452
  this.registry = registry;
@@ -8659,7 +9543,8 @@ ${errorDetails}`,
8659
9543
  let effectivePermission = decision.permission;
8660
9544
  const policy = this.opts.permissionPolicy;
8661
9545
  const yolo = policy.getYolo?.() === true || policy.getYoloDestructive?.() === true;
8662
- if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo) {
9546
+ const authoritativeAuto = decision.source === "yolo";
9547
+ if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo && !authoritativeAuto) {
8663
9548
  effectivePermission = "confirm";
8664
9549
  }
8665
9550
  if (effectivePermission === "deny") {
@@ -8801,9 +9686,10 @@ ${post.additionalContext}`;
8801
9686
  });
8802
9687
  this.opts.renderer?.writeToolCall(tool.name, use.input);
8803
9688
  const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
8804
- const text = this.serializer.serialize(output);
9689
+ const text = this.serializer.serialize(output, { toolName: tool.name, input: use.input });
8805
9690
  const scrubbed = this.opts.secretScrubber.scrub(text);
8806
- const { text: capped, newBudget } = this.serializer.enforceCap(scrubbed, budget);
9691
+ const withArtifact = await maybePersistLargeToolOutput(tool.name, scrubbed, budget);
9692
+ const { text: capped, newBudget } = this.serializer.enforceCap(withArtifact, budget);
8807
9693
  this.opts.renderer?.writeToolResult(tool.name, capped, false);
8808
9694
  return {
8809
9695
  block: {
@@ -8828,38 +9714,27 @@ ${post.additionalContext}`;
8828
9714
  tool.timeoutMs ?? this.iterationTimeoutMs,
8829
9715
  this.maxToolTimeoutMs
8830
9716
  );
8831
- const ctrl = new AbortController();
8832
- const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
8833
- const combined = AbortSignal.any([parentSignal, ctrl.signal]);
8834
- let cleanupCalled = false;
8835
- let caught = false;
9717
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
9718
+ const combined = AbortSignal.any([parentSignal, timeoutSignal]);
9719
+ let output;
8836
9720
  try {
8837
- if (typeof tool.executeStream === "function") {
8838
- return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
8839
- }
8840
- return await tool.execute(input, ctx, { signal: combined });
9721
+ output = typeof tool.executeStream === "function" ? await this.runStreamedTool(tool, input, ctx, combined, toolUseId) : await tool.execute(input, ctx, { signal: combined });
8841
9722
  } catch (err) {
8842
- caught = true;
8843
- if (combined.aborted && typeof tool.cleanup === "function") {
8844
- cleanupCalled = true;
8845
- try {
8846
- await tool.cleanup(input, ctx);
8847
- } catch {
8848
- }
8849
- }
9723
+ if (combined.aborted) await this.runToolCleanup(tool, input, ctx);
8850
9724
  throw err;
8851
- } finally {
8852
- clearTimeout(timer);
8853
- if (combined.aborted && !caught) {
8854
- if (!cleanupCalled && typeof tool.cleanup === "function") {
8855
- try {
8856
- await tool.cleanup(input, ctx);
8857
- } catch {
8858
- }
8859
- }
8860
- const reason = combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "aborted");
8861
- throw reason;
8862
- }
9725
+ }
9726
+ if (combined.aborted) {
9727
+ await this.runToolCleanup(tool, input, ctx);
9728
+ throw combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "tool timeout");
9729
+ }
9730
+ return output;
9731
+ }
9732
+ /** Best-effort tool cleanup; never let it mask the original error. */
9733
+ async runToolCleanup(tool, input, ctx) {
9734
+ if (typeof tool.cleanup !== "function") return;
9735
+ try {
9736
+ await tool.cleanup(input, ctx);
9737
+ } catch {
8863
9738
  }
8864
9739
  }
8865
9740
  async runStreamedTool(tool, input, ctx, signal, toolUseId) {
@@ -9028,6 +9903,25 @@ function extractMalformedRaw(input) {
9028
9903
  return String(value);
9029
9904
  }
9030
9905
  }
9906
+ var TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES = 64 * 1024;
9907
+ async function maybePersistLargeToolOutput(toolName, content, budget) {
9908
+ const bytes = Buffer.byteLength(content, "utf8");
9909
+ if (bytes <= Math.min(TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES, Math.max(0, budget))) {
9910
+ return content;
9911
+ }
9912
+ try {
9913
+ const dir = path4.join(wstackGlobalRoot(), "tool-output");
9914
+ await fsp2.mkdir(dir, { recursive: true });
9915
+ const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
9916
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9917
+ const filePath = path4.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
9918
+ await fsp2.writeFile(filePath, content, "utf8");
9919
+ return content + `
9920
+ [full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
9921
+ } catch {
9922
+ return content;
9923
+ }
9924
+ }
9031
9925
 
9032
9926
  // src/execution/autonomous-runner.ts
9033
9927
  var DoneConditionChecker = class {
@@ -10339,13 +11233,13 @@ var SubagentBudget = class _SubagentBudget {
10339
11233
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
10340
11234
  return Promise.resolve("stop");
10341
11235
  }
10342
- return new Promise((resolve5) => {
11236
+ return new Promise((resolve6) => {
10343
11237
  let resolved = false;
10344
11238
  const respond = (d) => {
10345
11239
  if (resolved) return;
10346
11240
  resolved = true;
10347
11241
  clearTimeout(fallback);
10348
- resolve5(d);
11242
+ resolve6(d);
10349
11243
  };
10350
11244
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
10351
11245
  bus.emit("budget.threshold_reached", {
@@ -14156,7 +15050,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
14156
15050
  taskIds.map((id) => {
14157
15051
  const cached = this.completedResults.find((r) => r.taskId === id);
14158
15052
  if (cached) return cached;
14159
- return new Promise((resolve5, reject) => {
15053
+ return new Promise((resolve6, reject) => {
14160
15054
  const timeout = setTimeout(() => {
14161
15055
  this.off("task.completed", handler);
14162
15056
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -14165,7 +15059,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
14165
15059
  if (result.taskId === id) {
14166
15060
  clearTimeout(timeout);
14167
15061
  this.off("task.completed", handler);
14168
- resolve5(result);
15062
+ resolve6(result);
14169
15063
  }
14170
15064
  };
14171
15065
  this.on("task.completed", handler);
@@ -14433,12 +15327,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
14433
15327
  }
14434
15328
  return new Promise((resolveDecision) => {
14435
15329
  let settled = false;
14436
- const resolve5 = (d) => {
15330
+ const resolve6 = (d) => {
14437
15331
  if (settled) return;
14438
15332
  settled = true;
14439
15333
  resolveDecision(d);
14440
15334
  };
14441
- const fallback = setTimeout(() => resolve5("stop"), DECISION_TIMEOUT_MS);
15335
+ const fallback = setTimeout(() => resolve6("stop"), DECISION_TIMEOUT_MS);
14442
15336
  budget._events?.emit("budget.threshold_reached", {
14443
15337
  kind: "timeout",
14444
15338
  used,
@@ -14454,11 +15348,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
14454
15348
  // disagreeing, resolves as a stop). Async grants still resolve.
14455
15349
  extend: (extra) => {
14456
15350
  clearTimeout(fallback);
14457
- queueMicrotask(() => resolve5({ extend: extra }));
15351
+ queueMicrotask(() => resolve6({ extend: extra }));
14458
15352
  },
14459
15353
  deny: () => {
14460
15354
  clearTimeout(fallback);
14461
- resolve5("stop");
15355
+ resolve6("stop");
14462
15356
  }
14463
15357
  });
14464
15358
  });
@@ -15346,7 +16240,7 @@ var InMemoryAgentBridge = class {
15346
16240
  });
15347
16241
  }
15348
16242
  this.inflightGuards.add(correlationId);
15349
- return new Promise((resolve5, reject) => {
16243
+ return new Promise((resolve6, reject) => {
15350
16244
  const timer = setTimeout(() => {
15351
16245
  this.inflightGuards.delete(correlationId);
15352
16246
  this.pendingRequests.delete(correlationId);
@@ -15365,7 +16259,7 @@ var InMemoryAgentBridge = class {
15365
16259
  return;
15366
16260
  }
15367
16261
  this.pendingRequests.set(correlationId, {
15368
- resolve: resolve5,
16262
+ resolve: resolve6,
15369
16263
  reject,
15370
16264
  timer
15371
16265
  });
@@ -16037,6 +16931,24 @@ Working rules:
16037
16931
  var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
16038
16932
  a specific slice of a larger plan \u2014 do that slice well and report back.
16039
16933
 
16934
+ Capabilities & operating rules:
16935
+ - You have full developer tools for your task: read, write/edit, search,
16936
+ shell + build (lint, format, typecheck, test), and dependency install.
16937
+ Use them directly to finish the task end-to-end. You run non-interactively
16938
+ \u2014 there is no human to approve individual tool calls, so routine work is
16939
+ pre-authorized; do not stop to ask for permission to read, edit, or build.
16940
+ - Stay inside the project root. Do not write files outside the repository,
16941
+ and do not touch machine config, credentials, or global state \u2014 those
16942
+ require an explicit grant you do not have.
16943
+ - Prefer the least-destructive path. Do not run irreversible or destructive
16944
+ commands (e.g. \`rm -rf\`, \`git push --force\`, history rewrites, dropping
16945
+ databases, mass deletes) unless the task explicitly requires it and names
16946
+ the target.
16947
+ - When you change code, verify it: run the relevant build / typecheck / tests
16948
+ and fix what you broke before reporting done.
16949
+ - Make only the changes the task calls for \u2014 don't refactor or reformat
16950
+ unrelated code.
16951
+
16040
16952
  Bridge contract:
16041
16953
  - You have a parent (the Director). You may call \`request\` on the
16042
16954
  parent bridge to ask a clarifying question. Use this sparingly; the
@@ -16994,6 +17906,8 @@ var Director = class _Director {
16994
17906
  sessionsRoot;
16995
17907
  /** Director run id for JSONL path resolution. */
16996
17908
  directorRunId;
17909
+ /** Optional logger for structured logging. Falls back to noop when omitted. */
17910
+ logger;
16997
17911
  /** Resolves task descriptions back from `assign()` so completion events
16998
17912
  * can also carry a human-readable title. */
16999
17913
  taskDescriptions = /* @__PURE__ */ new Map();
@@ -17082,6 +17996,7 @@ var Director = class _Director {
17082
17996
  opts.checkpointDebounceMs ?? 250
17083
17997
  ) : null;
17084
17998
  this.fleetManager = opts.fleetManager;
17999
+ this.logger = opts.logger;
17085
18000
  if (this.sharedScratchpadPath) {
17086
18001
  void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
17087
18002
  }
@@ -17381,7 +18296,10 @@ var Director = class _Director {
17381
18296
  entry.session.cancel(reason);
17382
18297
  for (const [_role, subagentId] of entry.session.getSubagentIds()) {
17383
18298
  this.coordinator.stop(subagentId).catch((err) => {
17384
- console.debug(`[director] stop subagent ${subagentId} failed (may have already completed): ${err}`);
18299
+ this.logger?.debug(`stop subagent ${subagentId} failed (may have already completed)`, {
18300
+ subagentId,
18301
+ err: err instanceof Error ? err.message : String(err)
18302
+ });
17385
18303
  });
17386
18304
  }
17387
18305
  }
@@ -17441,7 +18359,7 @@ var Director = class _Director {
17441
18359
  * Caller-supplied `priceLookup` is optional but recommended — without
17442
18360
  * it the `cost` column in `usage.snapshot()` stays at 0.
17443
18361
  */
17444
- async spawn(config, priceLookup) {
18362
+ async spawn(callerConfig, priceLookup) {
17445
18363
  if (this.workCompleteFlag) {
17446
18364
  throw new FleetSpawnBudgetError(
17447
18365
  "max_spawns",
@@ -17450,6 +18368,7 @@ var Director = class _Director {
17450
18368
  "workComplete() has been called \u2014 director closed further spawning"
17451
18369
  );
17452
18370
  }
18371
+ const config = { ...callerConfig };
17453
18372
  if (!config.model && this.modelMatrix) {
17454
18373
  const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
17455
18374
  const entry = resolveModelMatrix(matrix, config.role);
@@ -17675,7 +18594,7 @@ var Director = class _Director {
17675
18594
  })),
17676
18595
  usage: this.usage.snapshot()
17677
18596
  };
17678
- await fsp2.mkdir(path3.dirname(this.manifestPath), { recursive: true });
18597
+ await fsp2.mkdir(path4.dirname(this.manifestPath), { recursive: true });
17679
18598
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
17680
18599
  return this.manifestPath;
17681
18600
  }
@@ -17802,11 +18721,11 @@ var Director = class _Director {
17802
18721
  if (cached) return cached;
17803
18722
  const existing = this.taskWaiters.get(id);
17804
18723
  if (existing) return existing.promise;
17805
- let resolve5;
18724
+ let resolve6;
17806
18725
  const promise = new Promise((res) => {
17807
- resolve5 = res;
18726
+ resolve6 = res;
17808
18727
  });
17809
- this.taskWaiters.set(id, { promise, resolve: resolve5 });
18728
+ this.taskWaiters.set(id, { promise, resolve: resolve6 });
17810
18729
  return promise;
17811
18730
  })
17812
18731
  );
@@ -17889,7 +18808,7 @@ var Director = class _Director {
17889
18808
  */
17890
18809
  async readSession(subagentId, tail) {
17891
18810
  if (!this.sessionsRoot) return null;
17892
- const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
18811
+ const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
17893
18812
  let raw;
17894
18813
  try {
17895
18814
  raw = await fsp2.readFile(filePath, "utf8");
@@ -18202,7 +19121,7 @@ function createDelegateTool(opts) {
18202
19121
  subagentId
18203
19122
  });
18204
19123
  const dir = director;
18205
- const result = await new Promise((resolve5) => {
19124
+ const result = await new Promise((resolve6) => {
18206
19125
  let settled = false;
18207
19126
  let timer;
18208
19127
  const finish = (value) => {
@@ -18212,7 +19131,7 @@ function createDelegateTool(opts) {
18212
19131
  offTool();
18213
19132
  offIter();
18214
19133
  offProgress();
18215
- resolve5(value);
19134
+ resolve6(value);
18216
19135
  };
18217
19136
  const arm = () => {
18218
19137
  if (timer) clearTimeout(timer);
@@ -18419,13 +19338,13 @@ async function readSubagentPartial(opts, subagentId) {
18419
19338
  if (!opts.sessionsRoot) return void 0;
18420
19339
  const candidates = [];
18421
19340
  if (opts.directorRunId) {
18422
- candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
19341
+ candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
18423
19342
  } else {
18424
19343
  try {
18425
19344
  const entries = await fsp2.readdir(opts.sessionsRoot, { withFileTypes: true });
18426
19345
  for (const entry of entries) {
18427
19346
  if (entry.isDirectory()) {
18428
- candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
19347
+ candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
18429
19348
  }
18430
19349
  }
18431
19350
  } catch {
@@ -18475,9 +19394,9 @@ function makeDirectorSessionFactory(opts) {
18475
19394
  let dir;
18476
19395
  if (opts.store) {
18477
19396
  store = opts.store;
18478
- dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
19397
+ dir = opts.sessionsRoot ? path4.join(opts.sessionsRoot, runId) : "(caller-managed)";
18479
19398
  } else if (opts.sessionsRoot) {
18480
- dir = path3.join(opts.sessionsRoot, runId);
19399
+ dir = path4.join(opts.sessionsRoot, runId);
18481
19400
  store = new DefaultSessionStore({ dir });
18482
19401
  } else {
18483
19402
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -18627,7 +19546,7 @@ var DefaultModelsRegistry = class {
18627
19546
  this.overlay = opts.overlay;
18628
19547
  this.overlayUrl = opts.overlayUrl;
18629
19548
  this.overlayFile = opts.overlayFile;
18630
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
19549
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path4.join(path4.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
18631
19550
  }
18632
19551
  async load(opts = {}) {
18633
19552
  if (this.payload && !opts.force) return this.payload;
@@ -18840,7 +19759,7 @@ var DefaultModelsRegistry = class {
18840
19759
  }
18841
19760
  /** Used by `wstack models refresh` to expose where the cache lives. */
18842
19761
  cacheLocation() {
18843
- return path3.resolve(this.cacheFile);
19762
+ return path4.resolve(this.cacheFile);
18844
19763
  }
18845
19764
  };
18846
19765
  function hasEntries(payload) {
@@ -19207,7 +20126,7 @@ var DefaultModeStore = class {
19207
20126
  }
19208
20127
  async loadActiveMode() {
19209
20128
  try {
19210
- const configPath = path3.join(this.configDir, "mode.json");
20129
+ const configPath = path4.join(this.configDir, "mode.json");
19211
20130
  const content = await fsp2.readFile(configPath, "utf8");
19212
20131
  const data = JSON.parse(content);
19213
20132
  this.activeModeId = data.activeMode ?? null;
@@ -19218,7 +20137,7 @@ var DefaultModeStore = class {
19218
20137
  async saveActiveMode() {
19219
20138
  try {
19220
20139
  await fsp2.mkdir(this.configDir, { recursive: true });
19221
- const configPath = path3.join(this.configDir, "mode.json");
20140
+ const configPath = path4.join(this.configDir, "mode.json");
19222
20141
  await atomicWrite(
19223
20142
  configPath,
19224
20143
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -19233,11 +20152,11 @@ async function loadProjectModes(modesDir) {
19233
20152
  const entries = await fsp2.readdir(modesDir);
19234
20153
  for (const entry of entries) {
19235
20154
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
19236
- const filePath = path3.join(modesDir, entry);
20155
+ const filePath = path4.join(modesDir, entry);
19237
20156
  const stat6 = await fsp2.stat(filePath);
19238
20157
  if (!stat6.isFile()) continue;
19239
20158
  const content = await fsp2.readFile(filePath, "utf8");
19240
- const id = path3.basename(entry, path3.extname(entry));
20159
+ const id = path4.basename(entry, path4.extname(entry));
19241
20160
  modes.push({
19242
20161
  id,
19243
20162
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -19253,7 +20172,7 @@ async function loadProjectModes(modesDir) {
19253
20172
  async function loadUserModes(modesDir) {
19254
20173
  const modes = [];
19255
20174
  try {
19256
- const manifestPath = path3.join(modesDir, "modes.json");
20175
+ const manifestPath = path4.join(modesDir, "modes.json");
19257
20176
  const content = await fsp2.readFile(manifestPath, "utf8");
19258
20177
  const manifest = JSON.parse(content);
19259
20178
  for (const mode of manifest.modes) {
@@ -19264,6 +20183,35 @@ async function loadUserModes(modesDir) {
19264
20183
  return modes;
19265
20184
  }
19266
20185
 
20186
+ // src/models/provider-model-resolve.ts
20187
+ function describeCatalogModel(m) {
20188
+ return {
20189
+ id: m.id,
20190
+ name: m.name,
20191
+ releaseDate: m.release_date,
20192
+ contextWindow: m.limit?.context,
20193
+ inputCost: m.cost?.input,
20194
+ outputCost: m.cost?.output,
20195
+ capabilities: [
20196
+ ...m.tool_call ? ["tools"] : [],
20197
+ ...m.reasoning ? ["reasoning"] : [],
20198
+ ...m.modalities?.input?.includes("image") ? ["vision"] : [],
20199
+ ...m.open_weights ? ["open_weights"] : []
20200
+ ]
20201
+ };
20202
+ }
20203
+ function resolveProviderModelList(savedModels, catalog) {
20204
+ if (savedModels && savedModels.length > 0) {
20205
+ const byId = new Map((catalog?.models ?? []).map((m) => [m.id, m]));
20206
+ return savedModels.map((id) => {
20207
+ const hit = byId.get(id);
20208
+ return hit ? describeCatalogModel(hit) : { id, name: id, capabilities: [] };
20209
+ });
20210
+ }
20211
+ if (catalog) return catalog.models.map(describeCatalogModel);
20212
+ return [];
20213
+ }
20214
+
19267
20215
  // src/sdd/spec-parser.ts
19268
20216
  var SpecParser = class {
19269
20217
  parse(content) {
@@ -20205,7 +21153,7 @@ var SpecStore = class {
20205
21153
  indexPath;
20206
21154
  constructor(opts) {
20207
21155
  this.baseDir = opts.baseDir;
20208
- this.indexPath = path3.join(this.baseDir, "_index.json");
21156
+ this.indexPath = path4.join(this.baseDir, "_index.json");
20209
21157
  }
20210
21158
  async save(spec) {
20211
21159
  await ensureDir(this.baseDir);
@@ -20274,7 +21222,7 @@ var SpecStore = class {
20274
21222
  return updated;
20275
21223
  }
20276
21224
  filePath(id) {
20277
- return path3.join(this.baseDir, `${id}.json`);
21225
+ return path4.join(this.baseDir, `${id}.json`);
20278
21226
  }
20279
21227
  async readIndex() {
20280
21228
  try {
@@ -20331,7 +21279,7 @@ var TaskGraphStore = class {
20331
21279
  indexPath;
20332
21280
  constructor(opts) {
20333
21281
  this.baseDir = opts.baseDir;
20334
- this.indexPath = path3.join(this.baseDir, "_index.json");
21282
+ this.indexPath = path4.join(this.baseDir, "_index.json");
20335
21283
  }
20336
21284
  async save(graph) {
20337
21285
  await ensureDir(this.baseDir);
@@ -20369,7 +21317,7 @@ var TaskGraphStore = class {
20369
21317
  }
20370
21318
  }
20371
21319
  filePath(id) {
20372
- return path3.join(this.baseDir, `${id}.json`);
21320
+ return path4.join(this.baseDir, `${id}.json`);
20373
21321
  }
20374
21322
  async readIndex() {
20375
21323
  try {
@@ -20614,9 +21562,9 @@ var AISpecBuilder = class {
20614
21562
  if (!this.sessionPath) return;
20615
21563
  try {
20616
21564
  const fsp16 = await import('fs/promises');
20617
- const path19 = await import('path');
21565
+ const path21 = await import('path');
20618
21566
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
20619
- await fsp16.mkdir(path19.dirname(this.sessionPath), { recursive: true });
21567
+ await fsp16.mkdir(path21.dirname(this.sessionPath), { recursive: true });
20620
21568
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
20621
21569
  } catch {
20622
21570
  }
@@ -21343,15 +22291,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
21343
22291
  maxId = id;
21344
22292
  }
21345
22293
  }
21346
- const path19 = [];
22294
+ const path21 = [];
21347
22295
  let current = maxId;
21348
22296
  const visited = /* @__PURE__ */ new Set();
21349
22297
  while (current && !visited.has(current)) {
21350
22298
  visited.add(current);
21351
- path19.unshift(current);
22299
+ path21.unshift(current);
21352
22300
  current = prev.get(current) ?? null;
21353
22301
  }
21354
- return path19;
22302
+ return path21;
21355
22303
  }
21356
22304
  function computeParallelGroups(graph, blockedByMap) {
21357
22305
  const groups = [];
@@ -22174,9 +23122,9 @@ var DefaultHealthRegistry = class {
22174
23122
  }
22175
23123
  async runOne(check) {
22176
23124
  let timer = null;
22177
- const timeout = new Promise((resolve5) => {
23125
+ const timeout = new Promise((resolve6) => {
22178
23126
  timer = setTimeout(
22179
- () => resolve5({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
23127
+ () => resolve6({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
22180
23128
  this.timeoutMs
22181
23129
  );
22182
23130
  });
@@ -22359,7 +23307,7 @@ async function startMetricsServer(opts) {
22359
23307
  const tls = opts.tls;
22360
23308
  const useHttps = !!(tls?.cert && tls?.key);
22361
23309
  const host = opts.host ?? "127.0.0.1";
22362
- const path19 = opts.path ?? "/metrics";
23310
+ const path21 = opts.path ?? "/metrics";
22363
23311
  const healthPath = opts.healthPath ?? "/healthz";
22364
23312
  const healthRegistry = opts.healthRegistry;
22365
23313
  const listener = (req, res) => {
@@ -22369,7 +23317,7 @@ async function startMetricsServer(opts) {
22369
23317
  return;
22370
23318
  }
22371
23319
  const url = req.url.split("?")[0];
22372
- if (url === path19) {
23320
+ if (url === path21) {
22373
23321
  let body;
22374
23322
  try {
22375
23323
  body = renderPrometheus(opts.sink.snapshot());
@@ -22415,14 +23363,14 @@ async function startMetricsServer(opts) {
22415
23363
  const { createServer } = await import('http');
22416
23364
  server = createServer(listener);
22417
23365
  }
22418
- await new Promise((resolve5, reject) => {
23366
+ await new Promise((resolve6, reject) => {
22419
23367
  const onError = (err) => {
22420
23368
  server.off("listening", onListening);
22421
23369
  reject(err);
22422
23370
  };
22423
23371
  const onListening = () => {
22424
23372
  server.off("error", onError);
22425
- resolve5();
23373
+ resolve6();
22426
23374
  };
22427
23375
  server.once("error", onError);
22428
23376
  server.once("listening", onListening);
@@ -22433,9 +23381,9 @@ async function startMetricsServer(opts) {
22433
23381
  const protocol = useHttps ? "https" : "http";
22434
23382
  return {
22435
23383
  port: boundPort,
22436
- url: `${protocol}://${host}:${boundPort}${path19}`,
22437
- close: () => new Promise((resolve5, reject) => {
22438
- server.close((err) => err ? reject(err) : resolve5());
23384
+ url: `${protocol}://${host}:${boundPort}${path21}`,
23385
+ close: () => new Promise((resolve6, reject) => {
23386
+ server.close((err) => err ? reject(err) : resolve6());
22439
23387
  })
22440
23388
  };
22441
23389
  }
@@ -23093,6 +24041,6 @@ var allServers = () => ({
23093
24041
  playwright: { ...playwrightServer(), enabled: false }
23094
24042
  });
23095
24043
 
23096
- export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, 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, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, 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, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveSessionLoggingConfig, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
24044
+ export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, 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, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, 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, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, describeCatalogModel, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveProviderModelList, resolveSessionLoggingConfig, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
23097
24045
  //# sourceMappingURL=index.js.map
23098
24046
  //# sourceMappingURL=index.js.map