@wrongstack/core 0.289.0 → 0.291.1

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 (177) hide show
  1. package/dist/boot.d.ts.map +1 -1
  2. package/dist/chronicle/domain-adapter.d.ts.map +1 -1
  3. package/dist/chronicle/index.d.ts +1 -1
  4. package/dist/chronicle/index.d.ts.map +1 -1
  5. package/dist/chronicle/index.js +964 -245
  6. package/dist/chronicle/index.js.map +3 -3
  7. package/dist/chronicle/journal.d.ts +35 -3
  8. package/dist/chronicle/journal.d.ts.map +1 -1
  9. package/dist/chronicle/query.d.ts +8 -5
  10. package/dist/chronicle/query.d.ts.map +1 -1
  11. package/dist/chronicle/tool-adapter.d.ts.map +1 -1
  12. package/dist/coordination/agent-subagent-runner.d.ts +6 -0
  13. package/dist/coordination/agent-subagent-runner.d.ts.map +1 -1
  14. package/dist/coordination/agents/phase6-domain.d.ts.map +1 -1
  15. package/dist/coordination/brain.d.ts +2 -1
  16. package/dist/coordination/brain.d.ts.map +1 -1
  17. package/dist/coordination/collab-debug.d.ts +0 -9
  18. package/dist/coordination/collab-debug.d.ts.map +1 -1
  19. package/dist/coordination/coordinator/error-classifier.d.ts.map +1 -1
  20. package/dist/coordination/dep-watcher.d.ts +2 -1
  21. package/dist/coordination/dep-watcher.d.ts.map +1 -1
  22. package/dist/coordination/director/director-collab.d.ts +1 -1
  23. package/dist/coordination/director/director-collab.d.ts.map +1 -1
  24. package/dist/coordination/director-tools.d.ts.map +1 -1
  25. package/dist/coordination/director.d.ts +19 -1
  26. package/dist/coordination/director.d.ts.map +1 -1
  27. package/dist/coordination/fleet-event-validation.d.ts +0 -7
  28. package/dist/coordination/fleet-event-validation.d.ts.map +1 -1
  29. package/dist/coordination/fleet-spawn.d.ts.map +1 -1
  30. package/dist/coordination/fleet-supervisor.d.ts +1 -1
  31. package/dist/coordination/fleet-supervisor.d.ts.map +1 -1
  32. package/dist/coordination/fleet.d.ts +0 -44
  33. package/dist/coordination/fleet.d.ts.map +1 -1
  34. package/dist/coordination/index.d.ts +2 -2
  35. package/dist/coordination/index.d.ts.map +1 -1
  36. package/dist/coordination/index.js +733 -111
  37. package/dist/coordination/index.js.map +4 -4
  38. package/dist/coordination/knowledge-graph.d.ts +2 -1
  39. package/dist/coordination/knowledge-graph.d.ts.map +1 -1
  40. package/dist/coordination/large-answer-store.d.ts +0 -6
  41. package/dist/coordination/large-answer-store.d.ts.map +1 -1
  42. package/dist/coordination/mail-tools.d.ts.map +1 -1
  43. package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
  44. package/dist/coordination/mailbox-message-codec.d.ts +35 -0
  45. package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
  46. package/dist/coordination/mailbox-tool.d.ts +1 -1
  47. package/dist/coordination/mailbox-tool.d.ts.map +1 -1
  48. package/dist/coordination/mailbox-types.d.ts +151 -0
  49. package/dist/coordination/mailbox-types.d.ts.map +1 -1
  50. package/dist/coordination/provider-status-tracker.d.ts +27 -2
  51. package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
  52. package/dist/coordination/subagent-budget.d.ts +2 -1
  53. package/dist/coordination/subagent-budget.d.ts.map +1 -1
  54. package/dist/core/agent-response.d.ts +2 -1
  55. package/dist/core/agent-response.d.ts.map +1 -1
  56. package/dist/core/agent-tools.d.ts.map +1 -1
  57. package/dist/core/context.d.ts +51 -0
  58. package/dist/core/context.d.ts.map +1 -1
  59. package/dist/core/fallback-model.d.ts +1 -1
  60. package/dist/core/fallback-model.d.ts.map +1 -1
  61. package/dist/core/fallback-profile-manager.d.ts +1 -1
  62. package/dist/core/fallback-profile-manager.d.ts.map +1 -1
  63. package/dist/core/fleet-pulse.d.ts +2 -1
  64. package/dist/core/fleet-pulse.d.ts.map +1 -1
  65. package/dist/core/iteration-limit.d.ts +2 -1
  66. package/dist/core/iteration-limit.d.ts.map +1 -1
  67. package/dist/core/mailbox-loop.d.ts +11 -1
  68. package/dist/core/mailbox-loop.d.ts.map +1 -1
  69. package/dist/core/model-availability-calendar.d.ts +27 -0
  70. package/dist/core/model-availability-calendar.d.ts.map +1 -0
  71. package/dist/core/system-prompt-builder.d.ts +2 -1
  72. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  73. package/dist/defaults/index.js +1459 -560
  74. package/dist/defaults/index.js.map +4 -4
  75. package/dist/execution/auto-compaction-middleware.d.ts +3 -2
  76. package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
  77. package/dist/execution/enhance-recovery.d.ts +8 -0
  78. package/dist/execution/enhance-recovery.d.ts.map +1 -1
  79. package/dist/execution/error-handler.d.ts +2 -2
  80. package/dist/execution/error-handler.d.ts.map +1 -1
  81. package/dist/execution/index.js +643 -134
  82. package/dist/execution/index.js.map +4 -4
  83. package/dist/execution/intelligent-compactor.d.ts +0 -16
  84. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  85. package/dist/execution/one-shot-llm.d.ts.map +1 -1
  86. package/dist/execution/retry-policy.d.ts +0 -13
  87. package/dist/execution/retry-policy.d.ts.map +1 -1
  88. package/dist/execution/skill-loader.d.ts.map +1 -1
  89. package/dist/execution/tool-executor.d.ts.map +1 -1
  90. package/dist/hq/auth-store.d.ts.map +1 -1
  91. package/dist/hq/index.js +39 -1
  92. package/dist/hq/index.js.map +3 -3
  93. package/dist/index.d.ts +32 -30
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +22263 -19999
  96. package/dist/index.js.map +4 -4
  97. package/dist/infrastructure/index.js +59 -5
  98. package/dist/infrastructure/index.js.map +2 -2
  99. package/dist/kernel/events/agent-events.d.ts +2 -0
  100. package/dist/kernel/events/agent-events.d.ts.map +1 -1
  101. package/dist/kernel/events/file-events.d.ts +46 -0
  102. package/dist/kernel/events/file-events.d.ts.map +1 -1
  103. package/dist/kernel/events/memory-events.d.ts +9 -0
  104. package/dist/kernel/events/memory-events.d.ts.map +1 -1
  105. package/dist/kernel/events/provider-events.d.ts +2 -1
  106. package/dist/kernel/events/provider-events.d.ts.map +1 -1
  107. package/dist/kernel/events/tool-events.d.ts +25 -0
  108. package/dist/kernel/events/tool-events.d.ts.map +1 -1
  109. package/dist/mailbox-attach.d.ts.map +1 -1
  110. package/dist/plugins/auto-review-plugin.d.ts +10 -0
  111. package/dist/plugins/auto-review-plugin.d.ts.map +1 -1
  112. package/dist/plugins/chimera-plugin.d.ts +5 -0
  113. package/dist/plugins/chimera-plugin.d.ts.map +1 -1
  114. package/dist/security/index.d.ts +1 -0
  115. package/dist/security/index.d.ts.map +1 -1
  116. package/dist/security/index.js +317 -9
  117. package/dist/security/index.js.map +4 -4
  118. package/dist/security/kanban-boundary.d.ts +10 -0
  119. package/dist/security/kanban-boundary.d.ts.map +1 -0
  120. package/dist/security/permission-policy.d.ts +8 -1
  121. package/dist/security/permission-policy.d.ts.map +1 -1
  122. package/dist/storage/config-loader.d.ts +15 -0
  123. package/dist/storage/config-loader.d.ts.map +1 -1
  124. package/dist/storage/index.d.ts +2 -2
  125. package/dist/storage/index.d.ts.map +1 -1
  126. package/dist/storage/index.js +627 -428
  127. package/dist/storage/index.js.map +4 -4
  128. package/dist/storage/memory-consolidator.d.ts +49 -4
  129. package/dist/storage/memory-consolidator.d.ts.map +1 -1
  130. package/dist/storage/memory-graph-backend.d.ts +11 -0
  131. package/dist/storage/memory-graph-backend.d.ts.map +1 -1
  132. package/dist/storage/provider-config-watcher.d.ts +17 -0
  133. package/dist/storage/provider-config-watcher.d.ts.map +1 -1
  134. package/dist/storage/session-event-bridge.d.ts +2 -2
  135. package/dist/storage/session-event-bridge.d.ts.map +1 -1
  136. package/dist/tools/fallback-manage-tools.d.ts.map +1 -1
  137. package/dist/tools/index.js +166 -26
  138. package/dist/tools/index.js.map +3 -3
  139. package/dist/types/config.d.ts +12 -4
  140. package/dist/types/config.d.ts.map +1 -1
  141. package/dist/types/file-event-record.d.ts +68 -0
  142. package/dist/types/file-event-record.d.ts.map +1 -0
  143. package/dist/types/index.d.ts +1 -0
  144. package/dist/types/index.d.ts.map +1 -1
  145. package/dist/types/index.js +13 -8
  146. package/dist/types/index.js.map +3 -3
  147. package/dist/types/permission.d.ts +38 -0
  148. package/dist/types/permission.d.ts.map +1 -1
  149. package/dist/types/provider.d.ts +1 -1
  150. package/dist/types/provider.d.ts.map +1 -1
  151. package/dist/types/session.d.ts +27 -0
  152. package/dist/types/session.d.ts.map +1 -1
  153. package/dist/types/tool-executor.d.ts +2 -0
  154. package/dist/types/tool-executor.d.ts.map +1 -1
  155. package/dist/utils/config-backup.d.ts +20 -0
  156. package/dist/utils/config-backup.d.ts.map +1 -0
  157. package/dist/utils/index.d.ts +12 -11
  158. package/dist/utils/index.d.ts.map +1 -1
  159. package/dist/utils/index.js +436 -290
  160. package/dist/utils/index.js.map +4 -4
  161. package/dist/utils/message-invariants.d.ts +12 -0
  162. package/dist/utils/message-invariants.d.ts.map +1 -1
  163. package/dist/utils/safe-json.d.ts +1 -1
  164. package/dist/utils/safe-json.d.ts.map +1 -1
  165. package/dist/utils/term.d.ts +6 -0
  166. package/dist/utils/term.d.ts.map +1 -1
  167. package/dist/utils/tool-wire-compact.d.ts +9 -0
  168. package/dist/utils/tool-wire-compact.d.ts.map +1 -1
  169. package/dist/utils/wstack-paths.d.ts +21 -1
  170. package/dist/utils/wstack-paths.d.ts.map +1 -1
  171. package/instructions/agents/ios.md +113 -0
  172. package/instructions/llm/memory-consolidator.md +4 -12
  173. package/instructions/system.md +4 -1
  174. package/package.json +3 -3
  175. package/skills/auto-review/SKILL.md +176 -0
  176. package/skills/mnemosyne/SKILL.md +156 -0
  177. package/skills/mnemosyne/instructions/agent-prompt.md +200 -0
@@ -492,6 +492,12 @@ var TerminalLifecycle = class {
492
492
  * raw mode. Null when `acquire()` has never been called.
493
493
  */
494
494
  _wasRaw = null;
495
+ /**
496
+ * Whether stdin was paused before the TUI took ownership. Readline closes
497
+ * its interface by pausing the shared process stream, so raw mode alone is
498
+ * not enough to make Ink receive bytes during the boot-prompt -> TUI handoff.
499
+ */
500
+ _wasPaused = null;
495
501
  /**
496
502
  * Request the process to exit. Set the flag, trigger any registered
497
503
  * `onRequestExit` callback (typically Ink's `unmount()`), and arm a
@@ -543,8 +549,10 @@ var TerminalLifecycle = class {
543
549
  if (stdin?.isTTY !== true) return false;
544
550
  if (typeof stdin.setRawMode !== "function") return false;
545
551
  this._wasRaw = stdin.isRaw ?? false;
552
+ this._wasPaused = stdin.isPaused();
546
553
  this._stdin = stdin;
547
554
  stdin.setRawMode(true);
555
+ stdin.resume();
548
556
  this._active = true;
549
557
  if (process.platform === "win32") {
550
558
  setImmediate(() => {
@@ -573,9 +581,11 @@ var TerminalLifecycle = class {
573
581
  const stdin = this._stdin;
574
582
  if (stdin?.isTTY === true) {
575
583
  stdin.setRawMode?.(this._wasRaw ?? false);
584
+ if (this._wasPaused) stdin.pause();
576
585
  }
577
586
  this._stdin = void 0;
578
587
  this._wasRaw = null;
588
+ this._wasPaused = null;
579
589
  }
580
590
  /**
581
591
  * Synchronously reset all terminal state written by the TUI layer:
@@ -686,11 +696,42 @@ function stripAnsi(s) {
686
696
  return s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
687
697
  }
688
698
 
689
- // src/utils/config-json.ts
699
+ // src/utils/config-backup.ts
690
700
  import * as fs2 from "node:fs/promises";
701
+ import * as path2 from "node:path";
702
+ function configHistoryDir(globalRoot) {
703
+ return path2.join(globalRoot, "config-history");
704
+ }
705
+ function configSlug(absolutePath, globalRoot) {
706
+ const rel = path2.relative(globalRoot, absolutePath);
707
+ const normalized = rel.replace(/\\/g, "/").replace(/\.json$/i, "");
708
+ return normalized.replace(/\//g, "-");
709
+ }
710
+ async function backupConfigFile(filePath, paths) {
711
+ let currentContent;
712
+ try {
713
+ currentContent = await fs2.readFile(filePath, "utf8");
714
+ if (!currentContent.trim()) return;
715
+ } catch {
716
+ return;
717
+ }
718
+ const now = /* @__PURE__ */ new Date();
719
+ const ts = now.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
720
+ const slug = configSlug(filePath, paths.globalRoot);
721
+ const backupDir = configHistoryDir(paths.globalRoot);
722
+ const backupFile = path2.join(backupDir, `${slug}-${ts}.json`);
723
+ try {
724
+ await fs2.mkdir(backupDir, { recursive: true });
725
+ await fs2.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
726
+ } catch {
727
+ }
728
+ }
729
+
730
+ // src/utils/config-json.ts
731
+ import * as fs3 from "node:fs/promises";
691
732
  async function readJsonObjectFile(filePath) {
692
733
  try {
693
- const parsed = JSON.parse(await fs2.readFile(filePath, "utf8"));
734
+ const parsed = JSON.parse(await fs3.readFile(filePath, "utf8"));
694
735
  return isJsonObject(parsed) ? parsed : {};
695
736
  } catch {
696
737
  return {};
@@ -698,7 +739,7 @@ async function readJsonObjectFile(filePath) {
698
739
  }
699
740
  async function jsonObjectFileExists(filePath) {
700
741
  try {
701
- await fs2.access(filePath);
742
+ await fs3.access(filePath);
702
743
  return true;
703
744
  } catch {
704
745
  return false;
@@ -714,9 +755,9 @@ async function updateJsonObjectFile(filePath, mutator) {
714
755
  await writeJsonObjectFile(filePath, next);
715
756
  return next;
716
757
  }
717
- function getJsonPath(root, path5) {
758
+ function getJsonPath(root, path6) {
718
759
  let current = root;
719
- for (const segment of path5) {
760
+ for (const segment of path6) {
720
761
  if (typeof segment === "number") {
721
762
  if (!Array.isArray(current)) return void 0;
722
763
  current = current[segment];
@@ -727,13 +768,13 @@ function getJsonPath(root, path5) {
727
768
  }
728
769
  return current;
729
770
  }
730
- function setJsonPath(root, path5, value) {
731
- if (path5.length === 0) {
771
+ function setJsonPath(root, path6, value) {
772
+ if (path6.length === 0) {
732
773
  if (!isJsonObject(value)) throw new Error("Root config value must be an object");
733
774
  return value;
734
775
  }
735
- const parent = ensureJsonParent(root, path5);
736
- const leaf = lastPathSegment(path5);
776
+ const parent = ensureJsonParent(root, path6);
777
+ const leaf = lastPathSegment(path6);
737
778
  if (typeof leaf === "number") {
738
779
  if (!Array.isArray(parent)) throw new Error(`Cannot set numeric segment ${leaf} on non-array parent`);
739
780
  parent[leaf] = value;
@@ -743,10 +784,10 @@ function setJsonPath(root, path5, value) {
743
784
  }
744
785
  return root;
745
786
  }
746
- function removeJsonPath(root, path5) {
747
- if (path5.length === 0) return false;
748
- const parent = getJsonPath(root, path5.slice(0, -1));
749
- const leaf = lastPathSegment(path5);
787
+ function removeJsonPath(root, path6) {
788
+ if (path6.length === 0) return false;
789
+ const parent = getJsonPath(root, path6.slice(0, -1));
790
+ const leaf = lastPathSegment(path6);
750
791
  if (typeof leaf === "number") {
751
792
  if (!Array.isArray(parent) || leaf < 0 || leaf >= parent.length) return false;
752
793
  parent.splice(leaf, 1);
@@ -756,27 +797,27 @@ function removeJsonPath(root, path5) {
756
797
  delete parent[leaf];
757
798
  return true;
758
799
  }
759
- async function setJsonPathInFile(filePath, path5, value) {
760
- return updateJsonObjectFile(filePath, (config) => setJsonPath(config, path5, value));
800
+ async function setJsonPathInFile(filePath, path6, value) {
801
+ return updateJsonObjectFile(filePath, (config) => setJsonPath(config, path6, value));
761
802
  }
762
- async function removeJsonPathInFile(filePath, path5) {
803
+ async function removeJsonPathInFile(filePath, path6) {
763
804
  return updateJsonObjectFile(filePath, (config) => {
764
- removeJsonPath(config, path5);
805
+ removeJsonPath(config, path6);
765
806
  });
766
807
  }
767
808
  function isJsonObject(value) {
768
809
  return typeof value === "object" && value !== null && !Array.isArray(value);
769
810
  }
770
- function lastPathSegment(path5) {
771
- const segment = path5[path5.length - 1];
811
+ function lastPathSegment(path6) {
812
+ const segment = path6[path6.length - 1];
772
813
  if (segment === void 0) throw new Error("Invalid empty JSON path");
773
814
  return segment;
774
815
  }
775
- function ensureJsonParent(root, path5) {
816
+ function ensureJsonParent(root, path6) {
776
817
  let current = root;
777
- for (let i = 0; i < path5.length - 1; i += 1) {
778
- const segment = path5[i];
779
- const nextSegment = path5[i + 1];
818
+ for (let i = 0; i < path6.length - 1; i += 1) {
819
+ const segment = path6[i];
820
+ const nextSegment = path6[i + 1];
780
821
  if (segment === void 0) throw new Error("Invalid empty JSON path segment");
781
822
  const nextContainer = typeof nextSegment === "number" ? [] : {};
782
823
  if (typeof segment === "number") {
@@ -793,7 +834,7 @@ function ensureJsonParent(root, path5) {
793
834
  }
794
835
 
795
836
  // src/utils/context-evidence.ts
796
- import * as path2 from "node:path";
837
+ import * as path3 from "node:path";
797
838
  var MAX_TOOL_CALLS = 80;
798
839
  var MAX_FACTS = 40;
799
840
  var MAX_ERRORS = 20;
@@ -1033,10 +1074,10 @@ function addPath(ctx, out, raw) {
1033
1074
  if (!clean || clean.length > 260) return;
1034
1075
  let normalized = clean.replace(/\\/g, "/");
1035
1076
  try {
1036
- const abs = path2.isAbsolute(clean) ? path2.resolve(clean) : null;
1077
+ const abs = path3.isAbsolute(clean) ? path3.resolve(clean) : null;
1037
1078
  if (abs) {
1038
- const rel = path2.relative(ctx.projectRoot, abs);
1039
- if (!rel.startsWith("..") && !path2.isAbsolute(rel)) {
1079
+ const rel = path3.relative(ctx.projectRoot, abs);
1080
+ if (!rel.startsWith("..") && !path3.isAbsolute(rel)) {
1040
1081
  normalized = rel.replace(/\\/g, "/");
1041
1082
  }
1042
1083
  }
@@ -1154,7 +1195,7 @@ function implicitFactFor(metadata) {
1154
1195
  function metadataReferencedByText(metadata, haystack) {
1155
1196
  for (const file of metadata.files) {
1156
1197
  const f = file.toLowerCase();
1157
- const base = path2.basename(file).toLowerCase();
1198
+ const base = path3.basename(file).toLowerCase();
1158
1199
  if (f && haystack.includes(f)) return true;
1159
1200
  if (base && haystack.includes(base)) return true;
1160
1201
  }
@@ -1860,10 +1901,10 @@ function validateAgainstSchema(value, schema) {
1860
1901
  return { ok: errors.length === 0, errors };
1861
1902
  }
1862
1903
  var MAX_SCHEMA_DEPTH = 64;
1863
- function walk(value, schema, path5, errors, depth) {
1904
+ function walk(value, schema, path6, errors, depth) {
1864
1905
  if (depth > MAX_SCHEMA_DEPTH) {
1865
1906
  errors.push({
1866
- path: path5 || "<root>",
1907
+ path: path6 || "<root>",
1867
1908
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
1868
1909
  });
1869
1910
  return;
@@ -1871,7 +1912,7 @@ function walk(value, schema, path5, errors, depth) {
1871
1912
  if (schema.enum !== void 0) {
1872
1913
  if (!enumIncludes(schema.enum, value)) {
1873
1914
  errors.push({
1874
- path: path5 || "<root>",
1915
+ path: path6 || "<root>",
1875
1916
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
1876
1917
  });
1877
1918
  return;
@@ -1880,7 +1921,7 @@ function walk(value, schema, path5, errors, depth) {
1880
1921
  if (typeof schema.type === "string") {
1881
1922
  if (!checkType(value, schema.type)) {
1882
1923
  errors.push({
1883
- path: path5 || "<root>",
1924
+ path: path6 || "<root>",
1884
1925
  message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
1885
1926
  });
1886
1927
  return;
@@ -1892,7 +1933,7 @@ function walk(value, schema, path5, errors, depth) {
1892
1933
  if (!(req in obj)) {
1893
1934
  const expected = schema.properties?.[req]?.type;
1894
1935
  errors.push({
1895
- path: joinPath(path5, req),
1936
+ path: joinPath(path6, req),
1896
1937
  message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
1897
1938
  });
1898
1939
  }
@@ -1900,14 +1941,14 @@ function walk(value, schema, path5, errors, depth) {
1900
1941
  if (schema.properties) {
1901
1942
  for (const [key, subSchema] of Object.entries(schema.properties)) {
1902
1943
  if (key in obj) {
1903
- walk(obj[key], subSchema, joinPath(path5, key), errors, depth + 1);
1944
+ walk(obj[key], subSchema, joinPath(path6, key), errors, depth + 1);
1904
1945
  }
1905
1946
  }
1906
1947
  }
1907
1948
  }
1908
1949
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
1909
1950
  for (let i = 0; i < value.length; i++) {
1910
- walk(value[i], schema.items, `${path5}[${i}]`, errors, depth + 1);
1951
+ walk(value[i], schema.items, `${path6}[${i}]`, errors, depth + 1);
1911
1952
  }
1912
1953
  }
1913
1954
  }
@@ -2204,9 +2245,23 @@ function mapContent(msg, fn) {
2204
2245
  }
2205
2246
  return { ...msg, content: next };
2206
2247
  }
2248
+ function hasMeaningfulContent(content) {
2249
+ if (typeof content === "string") return content.trim().length > 0;
2250
+ for (const block of content) {
2251
+ if (block.type === "text") {
2252
+ if (block.text.trim().length > 0) return true;
2253
+ continue;
2254
+ }
2255
+ if (block.type === "thinking") {
2256
+ if (block.thinking.trim().length > 0 || block.signature) return true;
2257
+ continue;
2258
+ }
2259
+ return true;
2260
+ }
2261
+ return false;
2262
+ }
2207
2263
  function isEmptyMessage(msg) {
2208
- if (typeof msg.content === "string") return msg.content.trim().length === 0;
2209
- return msg.content.length === 0;
2264
+ return !hasMeaningfulContent(msg.content);
2210
2265
  }
2211
2266
 
2212
2267
  // src/utils/newline-normalize.ts
@@ -2279,7 +2334,7 @@ function compileUserRegex(pattern, flags) {
2279
2334
 
2280
2335
  // src/utils/safe-json.ts
2281
2336
  function safeParse(input, maxBytes = 5e6) {
2282
- if (input.length > maxBytes) {
2337
+ if (Buffer.byteLength(input, "utf8") > maxBytes) {
2283
2338
  return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
2284
2339
  }
2285
2340
  try {
@@ -2314,7 +2369,7 @@ function safeStringify(value, pretty = false) {
2314
2369
  }
2315
2370
  function sanitizeJsonString(s) {
2316
2371
  let out = s.trim();
2317
- out = stripSingleLineComments(out);
2372
+ out = stripJsonComments(out);
2318
2373
  out = out.replace(/,(\s*[}\]])/g, "$1");
2319
2374
  out = escapeControlCharsInStrings(out);
2320
2375
  try {
@@ -2372,34 +2427,59 @@ function escapeControlCharsInStrings(s) {
2372
2427
  }
2373
2428
  return out;
2374
2429
  }
2375
- function stripSingleLineComments(s) {
2430
+ function stripJsonComments(s) {
2376
2431
  let inString = false;
2432
+ let escaped = false;
2377
2433
  const chars = [];
2378
2434
  let i = 0;
2379
2435
  while (i < s.length) {
2380
2436
  const c = s.charAt(i);
2381
- if (c === '"' && (i === 0 || s.charAt(i - 1) !== "\\")) {
2382
- inString = !inString;
2437
+ if (inString) {
2383
2438
  chars.push(c);
2384
- } else if (c === "/" && s.charAt(i + 1) === "/" && !inString) {
2385
- while (i < s.length && s.charAt(i) !== "\n") i++;
2386
- } else {
2439
+ if (escaped) {
2440
+ escaped = false;
2441
+ } else if (c === "\\") {
2442
+ escaped = true;
2443
+ } else if (c === '"') {
2444
+ inString = false;
2445
+ }
2446
+ i++;
2447
+ continue;
2448
+ }
2449
+ if (c === '"') {
2450
+ inString = true;
2387
2451
  chars.push(c);
2452
+ i++;
2453
+ continue;
2454
+ }
2455
+ if (c === "/" && s.charAt(i + 1) === "/") {
2456
+ while (i < s.length && s.charAt(i) !== "\n") i++;
2457
+ continue;
2388
2458
  }
2459
+ if (c === "/" && s.charAt(i + 1) === "*") {
2460
+ const end = s.indexOf("*/", i + 2);
2461
+ if (end === -1) {
2462
+ chars.push(s.slice(i));
2463
+ break;
2464
+ }
2465
+ i = end + 2;
2466
+ continue;
2467
+ }
2468
+ chars.push(c);
2389
2469
  i++;
2390
2470
  }
2391
2471
  return chars.join("");
2392
2472
  }
2393
2473
 
2394
2474
  // src/utils/session-scoped-path.ts
2395
- import * as path3 from "node:path";
2475
+ import * as path4 from "node:path";
2396
2476
  function sessionScopedPath(dir, sessionId, suffix) {
2397
2477
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
2398
2478
  throw invalid(sessionId);
2399
2479
  }
2400
- const resolved = path3.resolve(dir, `${sessionId}${suffix}`);
2401
- const rel = path3.relative(path3.resolve(dir), resolved);
2402
- if (rel.startsWith("..") || path3.isAbsolute(rel)) {
2480
+ const resolved = path4.resolve(dir, `${sessionId}${suffix}`);
2481
+ const rel = path4.relative(path4.resolve(dir), resolved);
2482
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) {
2403
2483
  throw invalid(sessionId);
2404
2484
  }
2405
2485
  return resolved;
@@ -2413,51 +2493,16 @@ function invalid(sessionId) {
2413
2493
  });
2414
2494
  }
2415
2495
 
2416
- // src/utils/slug.ts
2417
- function slugify(name, fallback = "prompt", maxLen = 64) {
2418
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLen).replace(/-+$/g, "") || fallback;
2419
- }
2420
-
2421
- // src/utils/ulid.ts
2422
- import { randomBytes as randomBytes2 } from "node:crypto";
2423
- var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2424
- var ENCODING_LEN = ENCODING.length;
2425
- var TIME_LEN = 10;
2426
- var RANDOM_LEN = 16;
2427
- function encodeTime(now, len) {
2428
- let mod;
2429
- let str = "";
2430
- for (let i = len - 1; i >= 0; i--) {
2431
- mod = now % ENCODING_LEN;
2432
- str = ENCODING[mod] + str;
2433
- now = (now - mod) / ENCODING_LEN;
2434
- }
2435
- return str;
2436
- }
2437
- function encodeRandom(len) {
2438
- const bytes = randomBytes2(len);
2439
- let str = "";
2440
- for (let i = 0; i < len; i++) {
2441
- str += ENCODING[bytes[i] % ENCODING_LEN];
2442
- }
2443
- return str;
2444
- }
2445
- function ulid(seedTime = Date.now()) {
2446
- return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);
2447
- }
2448
- function isUlid(value) {
2449
- if (value.length !== TIME_LEN + RANDOM_LEN) return false;
2450
- for (const ch of value) {
2451
- if (!ENCODING.includes(ch)) return false;
2452
- }
2453
- return true;
2454
- }
2455
-
2456
2496
  // src/utils/sleep.ts
2457
2497
  function sleep(ms) {
2458
2498
  return new Promise((resolve5) => setTimeout(resolve5, ms));
2459
2499
  }
2460
2500
 
2501
+ // src/utils/slug.ts
2502
+ function slugify(name, fallback = "prompt", maxLen = 64) {
2503
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLen).replace(/-+$/g, "") || fallback;
2504
+ }
2505
+
2461
2506
  // src/utils/string.ts
2462
2507
  function truncate(s, max) {
2463
2508
  return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
@@ -2591,41 +2636,6 @@ function hasOpenTodos(todos) {
2591
2636
  return todos.some((t) => t.status === "pending" || t.status === "in_progress");
2592
2637
  }
2593
2638
 
2594
- // src/utils/tool-subject.ts
2595
- var GLOB_METACHARACTERS = /[*?[\]]/g;
2596
- function escapeGlobSubject(value) {
2597
- return value.replace(GLOB_METACHARACTERS, (char) => `\\${char}`);
2598
- }
2599
- function normalizePathSubject(value) {
2600
- return escapeGlobSubject(value.replace(/\\/g, "/"));
2601
- }
2602
- function isPathSubjectKey(subjectKey) {
2603
- return subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
2604
- }
2605
- function subjectForToolInput(toolName, input, subjectKey) {
2606
- if (!input || typeof input !== "object") return void 0;
2607
- const obj = input;
2608
- if (subjectKey) {
2609
- const value = obj[subjectKey];
2610
- if (typeof value === "string") {
2611
- return isPathSubjectKey(subjectKey) ? normalizePathSubject(value) : escapeGlobSubject(value);
2612
- }
2613
- }
2614
- if (toolName === "bash" && typeof obj.command === "string") {
2615
- return escapeGlobSubject(obj.command);
2616
- }
2617
- if (typeof obj.path === "string") {
2618
- return normalizePathSubject(obj.path);
2619
- }
2620
- if (typeof obj.url === "string") {
2621
- return escapeGlobSubject(obj.url);
2622
- }
2623
- if (typeof obj.name === "string") {
2624
- return escapeGlobSubject(obj.name);
2625
- }
2626
- return void 0;
2627
- }
2628
-
2629
2639
  // src/utils/tool-wire-compact.ts
2630
2640
  var TOOL_DESCRIPTION_MAX_CHARS = 400;
2631
2641
  var SCHEMA_DESCRIPTION_MAX_CHARS = 120;
@@ -2642,9 +2652,11 @@ function compactToolDefinitionForWire(tool, opts = {}) {
2642
2652
  tool.description ?? "",
2643
2653
  opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS
2644
2654
  ),
2645
- inputSchema: compactSchemaDescriptions(
2646
- tool.inputSchema,
2647
- opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
2655
+ inputSchema: normalizeTopLevelToolSchema(
2656
+ compactSchemaDescriptions(
2657
+ tool.inputSchema,
2658
+ opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
2659
+ )
2648
2660
  )
2649
2661
  };
2650
2662
  if (useDefaultOptions && typeof tool === "object" && tool !== null) {
@@ -2652,6 +2664,44 @@ function compactToolDefinitionForWire(tool, opts = {}) {
2652
2664
  }
2653
2665
  return compact;
2654
2666
  }
2667
+ function normalizeTopLevelToolSchema(schema) {
2668
+ const combinators = ["oneOf", "anyOf", "allOf"].map((keyword) => ({ keyword, branches: schema[keyword] })).filter(
2669
+ (entry) => Array.isArray(entry.branches)
2670
+ );
2671
+ if (combinators.length === 0) return schema;
2672
+ const out = { ...schema, type: "object" };
2673
+ delete out["oneOf"];
2674
+ delete out["anyOf"];
2675
+ delete out["allOf"];
2676
+ const properties = isRecord(schema["properties"]) ? { ...schema["properties"] } : {};
2677
+ let required = stringSet(schema["required"]);
2678
+ for (const { keyword, branches } of combinators) {
2679
+ const objectBranches = branches.filter(isRecord);
2680
+ for (const branch of objectBranches) {
2681
+ if (isRecord(branch["properties"])) Object.assign(properties, branch["properties"]);
2682
+ }
2683
+ const branchRequired = objectBranches.map((branch) => stringSet(branch["required"]));
2684
+ if (keyword === "allOf") {
2685
+ for (const fields of branchRequired) for (const field of fields) required.add(field);
2686
+ } else if (branchRequired.length > 0) {
2687
+ const common = new Set(
2688
+ [...branchRequired[0]].filter(
2689
+ (field) => branchRequired.slice(1).every((fields) => fields.has(field))
2690
+ )
2691
+ );
2692
+ required = /* @__PURE__ */ new Set([...required, ...common]);
2693
+ }
2694
+ }
2695
+ out["properties"] = properties;
2696
+ if (required.size > 0) out["required"] = [...required];
2697
+ else delete out["required"];
2698
+ return out;
2699
+ }
2700
+ function stringSet(value) {
2701
+ return new Set(
2702
+ Array.isArray(value) ? value.filter((item) => typeof item === "string") : []
2703
+ );
2704
+ }
2655
2705
  function compactSchemaDescriptions(schema, maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS) {
2656
2706
  const compact = compactSchemaNode(schema, maxDescriptionChars);
2657
2707
  return isRecord(compact) ? compact : { type: "object", properties: {} };
@@ -2892,6 +2942,114 @@ function resetCalibration(calibrationKey) {
2892
2942
  _cals.delete(calibrationKey);
2893
2943
  }
2894
2944
 
2945
+ // src/utils/tool-description-mode.ts
2946
+ var DEFAULT_TOOL_DESCRIPTION_MODE = "extend";
2947
+ var ORIGINAL_TOOL_DESCRIPTION = /* @__PURE__ */ Symbol.for("wrongstack.tool.originalDescription");
2948
+ function normalizeToolDescriptionMode(value) {
2949
+ if (typeof value !== "string") return void 0;
2950
+ const raw = value.trim().toLowerCase();
2951
+ if (raw === "extend" || raw === "extended" || raw === "full") return "extend";
2952
+ if (raw === "simple" || raw === "short" || raw === "brief") return "simple";
2953
+ return void 0;
2954
+ }
2955
+ function resolveToolDescriptionMode(modes, toolName) {
2956
+ return normalizeToolDescriptionMode(modes?.[toolName]) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
2957
+ }
2958
+ function simplifyToolDescription(text, opts = {}) {
2959
+ const maxSentences = Math.max(1, opts.maxSentences ?? 2);
2960
+ const maxChars = Math.max(40, opts.maxChars ?? 180);
2961
+ const normalized = text.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
2962
+ if (normalized.length <= maxChars) return normalized;
2963
+ const sentences = normalized.match(/[^.!?]+[.!?]+(?=\s|$)|[^.!?]+$/g) ?? [normalized];
2964
+ const selected = [];
2965
+ for (const sentence of sentences) {
2966
+ selected.push(sentence.trim());
2967
+ const candidate = selected.join(" ");
2968
+ if (selected.length >= maxSentences || candidate.length >= maxChars) break;
2969
+ }
2970
+ const summary = selected.join(" ").trim() || normalized;
2971
+ if (summary.length <= maxChars) return summary;
2972
+ const hardLimit = maxChars - 4;
2973
+ const boundary = findWordBoundary(summary, hardLimit);
2974
+ return `${summary.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd()} ...`;
2975
+ }
2976
+ function applyToolDescriptionModeToTool(tool, mode) {
2977
+ const existingOriginal = getOriginalDescription(tool);
2978
+ if (mode === "extend" && !existingOriginal) return tool;
2979
+ const original = existingOriginal ?? {
2980
+ description: tool.description,
2981
+ usageHint: tool.usageHint
2982
+ };
2983
+ const next = mode === "simple" ? withDescription(tool, {
2984
+ description: simplifyToolDescription(original.description),
2985
+ usageHint: original.usageHint === void 0 ? void 0 : simplifyToolDescription(original.usageHint)
2986
+ }) : withDescription(tool, original);
2987
+ return attachOriginalDescription(next, original);
2988
+ }
2989
+ function setToolDescriptionMode(registry, name, mode) {
2990
+ if (typeof registry.setDescriptionMode === "function") {
2991
+ return registry.setDescriptionMode(name, mode);
2992
+ }
2993
+ if (!registry.get(name) || typeof registry.wrap !== "function") return false;
2994
+ registry.wrap(
2995
+ name,
2996
+ (tool) => applyToolDescriptionModeToTool(tool, mode),
2997
+ "tool-description-mode"
2998
+ );
2999
+ return true;
3000
+ }
3001
+ function getToolDescriptionMode(registry, name) {
3002
+ return registry.getDescriptionMode?.(name) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
3003
+ }
3004
+ function applyToolDescriptionModes(registry, modes) {
3005
+ if (typeof registry.applyDescriptionModes === "function") {
3006
+ return registry.applyDescriptionModes(modes);
3007
+ }
3008
+ const entries = Object.entries(modes ?? {});
3009
+ const missing = [];
3010
+ let applied = 0;
3011
+ for (const [name, rawMode] of entries) {
3012
+ const mode = normalizeToolDescriptionMode(rawMode);
3013
+ if (!mode) continue;
3014
+ if (setToolDescriptionMode(registry, name, mode)) applied++;
3015
+ else missing.push(name);
3016
+ }
3017
+ return { applied, missing };
3018
+ }
3019
+ function getOriginalDescription(tool) {
3020
+ return tool[ORIGINAL_TOOL_DESCRIPTION];
3021
+ }
3022
+ function attachOriginalDescription(tool, original) {
3023
+ Object.defineProperty(tool, ORIGINAL_TOOL_DESCRIPTION, {
3024
+ configurable: true,
3025
+ enumerable: false,
3026
+ value: original,
3027
+ writable: true
3028
+ });
3029
+ return tool;
3030
+ }
3031
+ function withDescription(tool, next) {
3032
+ const copy = {
3033
+ ...tool,
3034
+ description: next.description,
3035
+ usageHint: next.usageHint
3036
+ };
3037
+ if (next.usageHint === void 0) {
3038
+ delete copy.usageHint;
3039
+ }
3040
+ return copy;
3041
+ }
3042
+ function findWordBoundary(text, limit) {
3043
+ const semantic = Math.max(
3044
+ text.lastIndexOf(". ", limit),
3045
+ text.lastIndexOf("; ", limit),
3046
+ text.lastIndexOf(", ", limit)
3047
+ );
3048
+ if (semantic > 40) return semantic + 1;
3049
+ const space = text.lastIndexOf(" ", limit);
3050
+ return space > 40 ? space : limit;
3051
+ }
3052
+
2895
3053
  // src/utils/tool-output-serializer.ts
2896
3054
  var DEFAULT_LIST_LIMIT = 500;
2897
3055
  var LOG_ENTRY_LIMIT = 200;
@@ -3555,114 +3713,6 @@ function isScalar(value) {
3555
3713
  return value === null || ["string", "number", "boolean"].includes(typeof value);
3556
3714
  }
3557
3715
 
3558
- // src/utils/tool-description-mode.ts
3559
- var DEFAULT_TOOL_DESCRIPTION_MODE = "extend";
3560
- var ORIGINAL_TOOL_DESCRIPTION = /* @__PURE__ */ Symbol.for("wrongstack.tool.originalDescription");
3561
- function normalizeToolDescriptionMode(value) {
3562
- if (typeof value !== "string") return void 0;
3563
- const raw = value.trim().toLowerCase();
3564
- if (raw === "extend" || raw === "extended" || raw === "full") return "extend";
3565
- if (raw === "simple" || raw === "short" || raw === "brief") return "simple";
3566
- return void 0;
3567
- }
3568
- function resolveToolDescriptionMode(modes, toolName) {
3569
- return normalizeToolDescriptionMode(modes?.[toolName]) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
3570
- }
3571
- function simplifyToolDescription(text, opts = {}) {
3572
- const maxSentences = Math.max(1, opts.maxSentences ?? 2);
3573
- const maxChars = Math.max(40, opts.maxChars ?? 180);
3574
- const normalized = text.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
3575
- if (normalized.length <= maxChars) return normalized;
3576
- const sentences = normalized.match(/[^.!?]+[.!?]+(?=\s|$)|[^.!?]+$/g) ?? [normalized];
3577
- const selected = [];
3578
- for (const sentence of sentences) {
3579
- selected.push(sentence.trim());
3580
- const candidate = selected.join(" ");
3581
- if (selected.length >= maxSentences || candidate.length >= maxChars) break;
3582
- }
3583
- const summary = selected.join(" ").trim() || normalized;
3584
- if (summary.length <= maxChars) return summary;
3585
- const hardLimit = maxChars - 4;
3586
- const boundary = findWordBoundary(summary, hardLimit);
3587
- return `${summary.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd()} ...`;
3588
- }
3589
- function applyToolDescriptionModeToTool(tool, mode) {
3590
- const existingOriginal = getOriginalDescription(tool);
3591
- if (mode === "extend" && !existingOriginal) return tool;
3592
- const original = existingOriginal ?? {
3593
- description: tool.description,
3594
- usageHint: tool.usageHint
3595
- };
3596
- const next = mode === "simple" ? withDescription(tool, {
3597
- description: simplifyToolDescription(original.description),
3598
- usageHint: original.usageHint === void 0 ? void 0 : simplifyToolDescription(original.usageHint)
3599
- }) : withDescription(tool, original);
3600
- return attachOriginalDescription(next, original);
3601
- }
3602
- function setToolDescriptionMode(registry, name, mode) {
3603
- if (typeof registry.setDescriptionMode === "function") {
3604
- return registry.setDescriptionMode(name, mode);
3605
- }
3606
- if (!registry.get(name) || typeof registry.wrap !== "function") return false;
3607
- registry.wrap(
3608
- name,
3609
- (tool) => applyToolDescriptionModeToTool(tool, mode),
3610
- "tool-description-mode"
3611
- );
3612
- return true;
3613
- }
3614
- function getToolDescriptionMode(registry, name) {
3615
- return registry.getDescriptionMode?.(name) ?? DEFAULT_TOOL_DESCRIPTION_MODE;
3616
- }
3617
- function applyToolDescriptionModes(registry, modes) {
3618
- if (typeof registry.applyDescriptionModes === "function") {
3619
- return registry.applyDescriptionModes(modes);
3620
- }
3621
- const entries = Object.entries(modes ?? {});
3622
- const missing = [];
3623
- let applied = 0;
3624
- for (const [name, rawMode] of entries) {
3625
- const mode = normalizeToolDescriptionMode(rawMode);
3626
- if (!mode) continue;
3627
- if (setToolDescriptionMode(registry, name, mode)) applied++;
3628
- else missing.push(name);
3629
- }
3630
- return { applied, missing };
3631
- }
3632
- function getOriginalDescription(tool) {
3633
- return tool[ORIGINAL_TOOL_DESCRIPTION];
3634
- }
3635
- function attachOriginalDescription(tool, original) {
3636
- Object.defineProperty(tool, ORIGINAL_TOOL_DESCRIPTION, {
3637
- configurable: true,
3638
- enumerable: false,
3639
- value: original,
3640
- writable: true
3641
- });
3642
- return tool;
3643
- }
3644
- function withDescription(tool, next) {
3645
- const copy = {
3646
- ...tool,
3647
- description: next.description,
3648
- usageHint: next.usageHint
3649
- };
3650
- if (next.usageHint === void 0) {
3651
- delete copy.usageHint;
3652
- }
3653
- return copy;
3654
- }
3655
- function findWordBoundary(text, limit) {
3656
- const semantic = Math.max(
3657
- text.lastIndexOf(". ", limit),
3658
- text.lastIndexOf("; ", limit),
3659
- text.lastIndexOf(", ", limit)
3660
- );
3661
- if (semantic > 40) return semantic + 1;
3662
- const space = text.lastIndexOf(" ", limit);
3663
- return space > 40 ? space : limit;
3664
- }
3665
-
3666
3716
  // src/utils/tool-result-render-mode.ts
3667
3717
  var DEFAULT_TOOL_RESULT_RENDER_MODE = "extend";
3668
3718
  function normalizeToolResultRenderMode(value) {
@@ -3700,25 +3750,95 @@ function applyToolResultRenderModes(registry, modes) {
3700
3750
  return { applied, missing };
3701
3751
  }
3702
3752
 
3753
+ // src/utils/tool-subject.ts
3754
+ var GLOB_METACHARACTERS = /[*?[\]]/g;
3755
+ function escapeGlobSubject(value) {
3756
+ return value.replace(GLOB_METACHARACTERS, (char) => `\\${char}`);
3757
+ }
3758
+ function normalizePathSubject(value) {
3759
+ return escapeGlobSubject(value.replace(/\\/g, "/"));
3760
+ }
3761
+ function isPathSubjectKey(subjectKey) {
3762
+ return subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
3763
+ }
3764
+ function subjectForToolInput(toolName, input, subjectKey) {
3765
+ if (!input || typeof input !== "object") return void 0;
3766
+ const obj = input;
3767
+ if (subjectKey) {
3768
+ const value = obj[subjectKey];
3769
+ if (typeof value === "string") {
3770
+ return isPathSubjectKey(subjectKey) ? normalizePathSubject(value) : escapeGlobSubject(value);
3771
+ }
3772
+ }
3773
+ if (toolName === "bash" && typeof obj.command === "string") {
3774
+ return escapeGlobSubject(obj.command);
3775
+ }
3776
+ if (typeof obj.path === "string") {
3777
+ return normalizePathSubject(obj.path);
3778
+ }
3779
+ if (typeof obj.url === "string") {
3780
+ return escapeGlobSubject(obj.url);
3781
+ }
3782
+ if (typeof obj.name === "string") {
3783
+ return escapeGlobSubject(obj.name);
3784
+ }
3785
+ return void 0;
3786
+ }
3787
+
3788
+ // src/utils/ulid.ts
3789
+ import { randomBytes as randomBytes2 } from "node:crypto";
3790
+ var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
3791
+ var ENCODING_LEN = ENCODING.length;
3792
+ var TIME_LEN = 10;
3793
+ var RANDOM_LEN = 16;
3794
+ function encodeTime(now, len) {
3795
+ let mod;
3796
+ let str = "";
3797
+ for (let i = len - 1; i >= 0; i--) {
3798
+ mod = now % ENCODING_LEN;
3799
+ str = ENCODING[mod] + str;
3800
+ now = (now - mod) / ENCODING_LEN;
3801
+ }
3802
+ return str;
3803
+ }
3804
+ function encodeRandom(len) {
3805
+ const bytes = randomBytes2(len);
3806
+ let str = "";
3807
+ for (let i = 0; i < len; i++) {
3808
+ str += ENCODING[bytes[i] % ENCODING_LEN];
3809
+ }
3810
+ return str;
3811
+ }
3812
+ function ulid(seedTime = Date.now()) {
3813
+ return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);
3814
+ }
3815
+ function isUlid(value) {
3816
+ if (value.length !== TIME_LEN + RANDOM_LEN) return false;
3817
+ for (const ch of value) {
3818
+ if (!ENCODING.includes(ch)) return false;
3819
+ }
3820
+ return true;
3821
+ }
3822
+
3703
3823
  // src/utils/wstack-paths.ts
3704
3824
  import { createHash } from "node:crypto";
3705
- import * as fs3 from "node:fs";
3825
+ import * as fs4 from "node:fs";
3706
3826
  import * as os from "node:os";
3707
- import * as path4 from "node:path";
3827
+ import * as path5 from "node:path";
3708
3828
  function canonicalProjectRoot(absRoot) {
3709
- const checkoutRoot = path4.resolve(absRoot);
3710
- const dotGit = path4.join(checkoutRoot, ".git");
3829
+ const checkoutRoot = path5.resolve(absRoot);
3830
+ const dotGit = path5.join(checkoutRoot, ".git");
3711
3831
  try {
3712
- if (!fs3.statSync(dotGit).isFile()) return checkoutRoot;
3713
- const gitDirLine = fs3.readFileSync(dotGit, "utf8").trim();
3832
+ if (!fs4.statSync(dotGit).isFile()) return checkoutRoot;
3833
+ const gitDirLine = fs4.readFileSync(dotGit, "utf8").trim();
3714
3834
  const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
3715
3835
  if (!match?.[1]) return checkoutRoot;
3716
- const gitDir = path4.resolve(checkoutRoot, match[1].trim());
3717
- const commonDirFile = path4.join(gitDir, "commondir");
3718
- if (!fs3.statSync(commonDirFile).isFile()) return checkoutRoot;
3719
- const commonDir = path4.resolve(gitDir, fs3.readFileSync(commonDirFile, "utf8").trim());
3720
- if (path4.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
3721
- return path4.dirname(commonDir);
3836
+ const gitDir = path5.resolve(checkoutRoot, match[1].trim());
3837
+ const commonDirFile = path5.join(gitDir, "commondir");
3838
+ if (!fs4.statSync(commonDirFile).isFile()) return checkoutRoot;
3839
+ const commonDir = path5.resolve(gitDir, fs4.readFileSync(commonDirFile, "utf8").trim());
3840
+ if (path5.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
3841
+ return path5.dirname(commonDir);
3722
3842
  } catch {
3723
3843
  return checkoutRoot;
3724
3844
  }
@@ -3728,7 +3848,7 @@ function projectHash(absRoot) {
3728
3848
  }
3729
3849
  function projectSlug(absRoot) {
3730
3850
  const identityRoot = canonicalProjectRoot(absRoot);
3731
- const base = slugify2(path4.basename(identityRoot));
3851
+ const base = slugify2(path5.basename(identityRoot));
3732
3852
  const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
3733
3853
  return `${base}-${hash}`;
3734
3854
  }
@@ -3737,61 +3857,83 @@ function slugify2(name) {
3737
3857
  }
3738
3858
  function wstackGlobalRoot() {
3739
3859
  const fromEnv = process.env["WRONGSTACK_HOME"];
3740
- if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
3741
- return path4.join(os.homedir(), ".wrongstack");
3860
+ if (fromEnv && fromEnv.trim().length > 0) return path5.resolve(fromEnv);
3861
+ return path5.join(os.homedir(), ".wrongstack");
3742
3862
  }
3743
3863
  function resolveWstackPaths(opts) {
3744
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
3864
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path5.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
3745
3865
  const homeDir = opts.userHome ?? os.homedir();
3746
3866
  const hash = projectHash(opts.projectRoot);
3747
3867
  const slug = projectSlug(opts.projectRoot);
3748
- const projectDir = path4.join(globalRoot, "projects", slug);
3868
+ const projectDir = path5.join(globalRoot, "projects", slug);
3749
3869
  return {
3750
3870
  globalRoot,
3751
3871
  projectRoot: opts.projectRoot,
3752
3872
  homeDir,
3753
3873
  configDir: globalRoot,
3754
- globalConfig: path4.join(globalRoot, "config.json"),
3755
- secretsKey: path4.join(globalRoot, ".key"),
3756
- globalMemory: path4.join(globalRoot, "memory.md"),
3757
- globalSkills: path4.join(globalRoot, "skills"),
3758
- globalClaudeSkills: path4.join(homeDir, ".claude", "skills"),
3759
- globalDesignKits: path4.join(globalRoot, "design-kits"),
3760
- globalPrompts: path4.join(globalRoot, "prompts"),
3761
- globalInstructions: path4.join(globalRoot, "instructions"),
3762
- promptUsage: path4.join(globalRoot, "prompt-usage.json"),
3763
- cacheDir: path4.join(globalRoot, "cache"),
3764
- modelsCache: path4.join(globalRoot, "cache", "models.dev.json"),
3765
- modelsOverlayCache: path4.join(globalRoot, "cache", "models-overlay.json"),
3766
- historyFile: path4.join(globalRoot, "history"),
3767
- logFile: path4.join(globalRoot, "logs", "wrongstack.log"),
3874
+ globalConfig: path5.join(globalRoot, "config.json"),
3875
+ profilesDir: path5.join(globalRoot, "profiles"),
3876
+ profileConfig: (name) => {
3877
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
3878
+ return path5.join(globalRoot, "profiles", safe || "default", "config.json");
3879
+ },
3880
+ profileStatuslineConfig: (name) => {
3881
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
3882
+ return path5.join(globalRoot, "profiles", safe || "default", "statusline.json");
3883
+ },
3884
+ profileModeConfig: (name) => {
3885
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
3886
+ return path5.join(globalRoot, "profiles", safe || "default", "mode.json");
3887
+ },
3888
+ profileProviderStatus: (name) => {
3889
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
3890
+ return path5.join(globalRoot, "profiles", safe || "default", "provider-status.json");
3891
+ },
3892
+ profileUpdateCache: (name) => {
3893
+ const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
3894
+ return path5.join(globalRoot, "profiles", safe || "default", "update-cache.json");
3895
+ },
3896
+ secretsKey: path5.join(globalRoot, ".key"),
3897
+ globalMemory: path5.join(globalRoot, "memory.md"),
3898
+ globalSkills: path5.join(globalRoot, "skills"),
3899
+ globalClaudeSkills: path5.join(homeDir, ".claude", "skills"),
3900
+ globalDesignKits: path5.join(globalRoot, "design-kits"),
3901
+ globalPrompts: path5.join(globalRoot, "prompts"),
3902
+ globalInstructions: path5.join(globalRoot, "instructions"),
3903
+ promptUsage: path5.join(globalRoot, "prompt-usage.json"),
3904
+ cacheDir: path5.join(globalRoot, "cache"),
3905
+ modelsCache: path5.join(globalRoot, "cache", "models.dev.json"),
3906
+ modelsOverlayCache: path5.join(globalRoot, "cache", "models-overlay.json"),
3907
+ historyFile: path5.join(globalRoot, "history"),
3908
+ logFile: path5.join(globalRoot, "logs", "wrongstack.log"),
3768
3909
  projectDir,
3769
- projectCodebaseIndex: path4.join(projectDir, "codebase-index"),
3770
- projectMemory: path4.join(projectDir, "memory.md"),
3771
- projectSessions: path4.join(projectDir, "sessions"),
3772
- projectTrust: path4.join(projectDir, "trust.json"),
3773
- projectMeta: path4.join(projectDir, "meta.json"),
3774
- projectLocalConfig: path4.join(projectDir, "config.local.json"),
3775
- inProjectConfig: path4.join(opts.projectRoot, ".wrongstack", "config.json"),
3776
- inProjectAgentsFile: path4.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
3777
- inProjectSkills: path4.join(opts.projectRoot, ".wrongstack", "skills"),
3778
- inProjectClaudeSkills: path4.join(opts.projectRoot, ".claude", "skills"),
3779
- inProjectPrompts: path4.join(opts.projectRoot, ".wrongstack", "prompts"),
3780
- inProjectInstructions: path4.join(opts.projectRoot, ".wrongstack", "instructions"),
3781
- inProjectDesignKits: path4.join(opts.projectRoot, ".wrongstack", "design-kits"),
3782
- inProjectWorktrees: path4.join(opts.projectRoot, ".wrongstack", "worktrees"),
3910
+ projectCodebaseIndex: path5.join(projectDir, "codebase-index"),
3911
+ projectMemory: path5.join(projectDir, "memory.md"),
3912
+ projectSessions: path5.join(projectDir, "sessions"),
3913
+ projectTrust: path5.join(projectDir, "trust.json"),
3914
+ projectMeta: path5.join(projectDir, "meta.json"),
3915
+ projectLocalConfig: path5.join(projectDir, "config.local.json"),
3916
+ inProjectConfig: path5.join(opts.projectRoot, ".wrongstack", "config.json"),
3917
+ inProjectAgentsFile: path5.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
3918
+ inProjectSkills: path5.join(opts.projectRoot, ".wrongstack", "skills"),
3919
+ inProjectClaudeSkills: path5.join(opts.projectRoot, ".claude", "skills"),
3920
+ inProjectPrompts: path5.join(opts.projectRoot, ".wrongstack", "prompts"),
3921
+ inProjectInstructions: path5.join(opts.projectRoot, ".wrongstack", "instructions"),
3922
+ inProjectDesignKits: path5.join(opts.projectRoot, ".wrongstack", "design-kits"),
3923
+ inProjectWorktrees: path5.join(opts.projectRoot, ".wrongstack", "worktrees"),
3783
3924
  projectHash: hash,
3784
3925
  projectSlug: slug,
3785
- projectGoal: path4.join(projectDir, "goal.json"),
3786
- projectInputHistory: path4.join(projectDir, "input-history.json"),
3787
- projectSpecs: path4.join(projectDir, "specs"),
3788
- projectTaskGraphs: path4.join(projectDir, "task-graphs"),
3789
- projectSddSession: path4.join(projectDir, "sdd-session.json"),
3790
- projectPlan: path4.join(projectDir, "plan.json"),
3791
- projectAutophase: path4.join(projectDir, "autophase"),
3792
- projectSddBoards: path4.join(projectDir, "sdd-boards"),
3793
- syncConfig: path4.join(globalRoot, "sync.json"),
3794
- projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
3926
+ projectGoal: path5.join(projectDir, "goal.json"),
3927
+ projectInputHistory: path5.join(projectDir, "input-history.json"),
3928
+ projectSpecs: path5.join(projectDir, "specs"),
3929
+ projectTaskGraphs: path5.join(projectDir, "task-graphs"),
3930
+ projectSddSession: path5.join(projectDir, "sdd-session.json"),
3931
+ projectPlan: path5.join(projectDir, "plan.json"),
3932
+ projectAutophase: path5.join(projectDir, "autophase"),
3933
+ projectSddBoards: path5.join(projectDir, "sdd-boards"),
3934
+ syncConfig: path5.join(globalRoot, "sync.json"),
3935
+ configHistoryDir: path5.join(globalRoot, "config-history"),
3936
+ projectStatus: (projectHash2) => path5.join(globalRoot, "projects", projectHash2, "status.json")
3795
3937
  };
3796
3938
  }
3797
3939
  export {
@@ -3810,6 +3952,7 @@ export {
3810
3952
  assertNever,
3811
3953
  assertNotPrivateHost,
3812
3954
  atomicWrite,
3955
+ backupConfigFile,
3813
3956
  buildChildEnv,
3814
3957
  buildCompletedWorkLedgerBlock,
3815
3958
  buildContextEvidenceDigest,
@@ -3826,6 +3969,7 @@ export {
3826
3969
  completePartialObject,
3827
3970
  computeMessageTokens,
3828
3971
  computeTaskItemProgress,
3972
+ configHistoryDir,
3829
3973
  configureChildEnvGitIdentity,
3830
3974
  createContextEvidenceState,
3831
3975
  createToolOutputSerializer,
@@ -3853,6 +3997,7 @@ export {
3853
3997
  getJsonPath,
3854
3998
  getToolDescriptionMode,
3855
3999
  getToolResultRenderMode,
4000
+ hasMeaningfulContent,
3856
4001
  hasOpenTodos,
3857
4002
  isJsonObject,
3858
4003
  isPathSubjectKey,
@@ -3870,6 +4015,7 @@ export {
3870
4015
  normalizeToLf,
3871
4016
  normalizeToolDescriptionMode,
3872
4017
  normalizeToolResultRenderMode,
4018
+ normalizeTopLevelToolSchema,
3873
4019
  parseIncomingImages,
3874
4020
  projectHash,
3875
4021
  projectSlug,