@wrongstack/core 0.155.0 → 0.250.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 (81) hide show
  1. package/dist/{agent-bridge-BbZU5TPN.d.ts → agent-bridge-4gc0vfW2.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-Bsueu0J2.d.ts → agent-subagent-runner-Dz-9kiE6.d.ts} +9 -8
  3. package/dist/{brain-CS_B0vIE.d.ts → brain-sCZ3lCjq.d.ts} +26 -2
  4. package/dist/{compactor-BueGt7LG.d.ts → compactor-BRfg3QPd.d.ts} +1 -1
  5. package/dist/{config-BaVThgnT.d.ts → config-eSsrto5d.d.ts} +8 -2
  6. package/dist/{context-C7G_MtLV.d.ts → context-CLz3z_E8.d.ts} +126 -2
  7. package/dist/coordination/index.d.ts +70 -13
  8. package/dist/coordination/index.js +1986 -146
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -26
  11. package/dist/defaults/index.js +1110 -296
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +45 -16
  14. package/dist/execution/index.js +229 -56
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +86 -0
  17. package/dist/execution/prompt-enhancer.js +125 -0
  18. package/dist/execution/prompt-enhancer.js.map +1 -0
  19. package/dist/extension/index.d.ts +6 -6
  20. package/dist/extension/index.js +3 -1
  21. package/dist/extension/index.js.map +1 -1
  22. package/dist/{goal-preamble-CbV8pXLD.d.ts → goal-preamble-BjJpnLW4.d.ts} +19 -10
  23. package/dist/{index-CI1hRfPt.d.ts → index-Dy8OwfBD.d.ts} +8 -8
  24. package/dist/{index-B5wz-GXm.d.ts → index-IehiNryU.d.ts} +7 -5
  25. package/dist/index.d.ts +438 -128
  26. package/dist/index.js +4989 -849
  27. package/dist/index.js.map +1 -1
  28. package/dist/infrastructure/index.d.ts +7 -7
  29. package/dist/infrastructure/index.js +61 -13
  30. package/dist/infrastructure/index.js.map +1 -1
  31. package/dist/kernel/index.d.ts +9 -9
  32. package/dist/kernel/index.js +7 -1
  33. package/dist/kernel/index.js.map +1 -1
  34. package/dist/{llm-selector-CP72f1lC.d.ts → llm-selector-D22R4AFz.d.ts} +2 -2
  35. package/dist/logger-DmmQhf4P.d.ts +65 -0
  36. package/dist/{mcp-servers-CPERR2De.d.ts → mcp-servers-DfXxCASH.d.ts} +3 -3
  37. package/dist/models/index.d.ts +5 -5
  38. package/dist/models/index.js +89 -9
  39. package/dist/models/index.js.map +1 -1
  40. package/dist/{models-registry-D90K9UnM.d.ts → models-registry-DpanBg8D.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-BSKSFNhv.d.ts → multi-agent-coordinator-CnbEqpv0.d.ts} +8 -8
  42. package/dist/{null-fleet-bus-CGOez8Le.d.ts → null-fleet-bus-Do1OLYpj.d.ts} +7 -7
  43. package/dist/observability/index.d.ts +2 -2
  44. package/dist/package-outdated-watcher-CA5GGB4C.d.ts +560 -0
  45. package/dist/{parallel-eternal-engine-CYoTKjsz.d.ts → parallel-eternal-engine-UZg1xOzE.d.ts} +13 -9
  46. package/dist/{path-resolver-DuhlmPil.d.ts → path-resolver-BaP06Owy.d.ts} +3 -3
  47. package/dist/{permission-B7nKnEvQ.d.ts → permission-DbWPbuoA.d.ts} +1 -1
  48. package/dist/{permission-policy-8-6zBmfA.d.ts → permission-policy-AOk0LVsV.d.ts} +2 -2
  49. package/dist/pipeline-D1n-gQI-.d.ts +493 -0
  50. package/dist/{plan-templates-DbH7lg-t.d.ts → plan-templates-BUVRY0pU.d.ts} +18 -7
  51. package/dist/{provider-runner-Cocq0O9E.d.ts → provider-runner-D0HgUqwV.d.ts} +3 -3
  52. package/dist/{retry-policy-rutAfVeR.d.ts → retry-policy-BVnkbMET.d.ts} +1 -1
  53. package/dist/sdd/index.d.ts +8 -8
  54. package/dist/sdd/index.js +221 -87
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-vault-w8MbUe2Q.d.ts → secret-vault-CeVNiy_f.d.ts} +3 -2
  57. package/dist/security/index.d.ts +5 -4
  58. package/dist/security/index.js +155 -13
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-4vDFZKt3.d.ts → selector-Cb4_9-hf.d.ts} +1 -1
  61. package/dist/{session-event-bridge-DWlvglC2.d.ts → session-event-bridge-BhtkkFFy.d.ts} +4 -2
  62. package/dist/{session-reader-BAtCxdaw.d.ts → session-reader-CCOssnBS.d.ts} +1 -1
  63. package/dist/skills/index.js +171 -21
  64. package/dist/skills/index.js.map +1 -1
  65. package/dist/storage/index.d.ts +150 -12
  66. package/dist/storage/index.js +1041 -214
  67. package/dist/storage/index.js.map +1 -1
  68. package/dist/types/index.d.ts +67 -20
  69. package/dist/types/index.js +562 -55
  70. package/dist/types/index.js.map +1 -1
  71. package/dist/utils/expect-defined.js +3 -1
  72. package/dist/utils/expect-defined.js.map +1 -1
  73. package/dist/utils/index.d.ts +25 -4
  74. package/dist/utils/index.js +45 -14
  75. package/dist/utils/index.js.map +1 -1
  76. package/dist/{wstack-paths-DD50Omgn.d.ts → wstack-paths-CJjEwPXn.d.ts} +14 -1
  77. package/package.json +7 -3
  78. package/skills/chimera/SKILL.md +105 -0
  79. package/skills/research-web/SKILL.md +342 -0
  80. package/dist/logger-B9J5puGM.d.ts +0 -32
  81. package/dist/pipeline-BG7UgbDc.d.ts +0 -239
@@ -77,7 +77,13 @@ var ERROR_CODES = {
77
77
  FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
78
78
  FS_DELETE_FAILED: "FS_DELETE_FAILED",
79
79
  FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
80
+ // SDD (Spec-Driven Development)
81
+ SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
82
+ SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
83
+ SDD_INVALID_STATE: "SDD_INVALID_STATE",
84
+ SDD_NOT_READY: "SDD_NOT_READY",
80
85
  // General
86
+ VALIDATION_ERROR: "VALIDATION_ERROR",
81
87
  UNKNOWN: "UNKNOWN"
82
88
  };
83
89
  var WrongStackError = class extends Error {
@@ -192,6 +198,20 @@ var SessionError = class extends WrongStackError {
192
198
  this.sessionId = opts.sessionId;
193
199
  }
194
200
  };
201
+ var SddError = class extends WrongStackError {
202
+ constructor(opts) {
203
+ super({
204
+ message: opts.message,
205
+ code: opts.code,
206
+ subsystem: "sdd",
207
+ severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? "warning" : "error",
208
+ recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,
209
+ context: opts.context,
210
+ cause: opts.cause
211
+ });
212
+ this.name = "SddError";
213
+ }
214
+ };
195
215
  var FsError = class extends WrongStackError {
196
216
  path;
197
217
  constructor(opts) {
@@ -229,6 +249,14 @@ function isAgentError(err) {
229
249
  function isFsError(err) {
230
250
  return err instanceof FsError;
231
251
  }
252
+ function isSddError(err) {
253
+ return err instanceof SddError;
254
+ }
255
+
256
+ // src/utils/string.ts
257
+ function truncate(s, max) {
258
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
259
+ }
232
260
 
233
261
  // src/types/provider.ts
234
262
  var ProviderError = class extends WrongStackError {
@@ -288,9 +316,6 @@ function describeStatus(status, type) {
288
316
  if (type) return `${type} (${status})`;
289
317
  return `HTTP ${status}`;
290
318
  }
291
- function truncate(s, n) {
292
- return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
293
- }
294
319
  var StreamHangError = class extends ProviderError {
295
320
  /** Name of the provider that hung, e.g. "zai", "anthropic". */
296
321
  hungProviderId;
@@ -419,7 +444,7 @@ async function renameWithRetry(from, to) {
419
444
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
420
445
  throw err;
421
446
  }
422
- await new Promise((resolve3) => setTimeout(resolve3, delays[i]));
447
+ await new Promise((resolve4) => setTimeout(resolve4, delays[i]));
423
448
  }
424
449
  }
425
450
  throw lastErr;
@@ -489,6 +514,26 @@ var KEY_BYTES = 32;
489
514
  var IV_BYTES = 12;
490
515
  var TAG_BYTES = 16;
491
516
  var ALGO = "aes-256-gcm";
517
+ var KEY_FILE_MODE = 384;
518
+ function checkKeyFilePermissions(keyFile) {
519
+ if (process.platform === "win32") return;
520
+ try {
521
+ const stat2 = fs2.statSync(keyFile);
522
+ const actualMode = stat2.mode & 511;
523
+ if (actualMode !== KEY_FILE_MODE) {
524
+ console.warn(JSON.stringify({
525
+ level: "warn",
526
+ event: "vault.key_file_wrong_permissions",
527
+ message: `Key file ${keyFile} has mode ${actualMode.toString(8)} \u2014 expected ${KEY_FILE_MODE.toString(8)}. Run: chmod ${KEY_FILE_MODE.toString(8)} ${keyFile}`,
528
+ keyFile,
529
+ expectedMode: KEY_FILE_MODE,
530
+ actualMode,
531
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
532
+ }));
533
+ }
534
+ } catch {
535
+ }
536
+ }
492
537
  var DefaultSecretVault = class {
493
538
  keyFile;
494
539
  key;
@@ -512,14 +557,26 @@ var DefaultSecretVault = class {
512
557
  const rest = value.slice(ENCRYPTED_PREFIX.length);
513
558
  const parts = rest.split(":");
514
559
  if (parts.length !== 3) {
515
- throw new Error("SecretVault: malformed encrypted value");
560
+ throw new ConfigError({
561
+ message: "SecretVault: malformed encrypted value",
562
+ code: ERROR_CODES.CONFIG_PARSE_FAILED,
563
+ context: { field: "encrypted_value" }
564
+ });
516
565
  }
517
566
  const [ivB64, tagB64, ctB64] = parts;
518
567
  const iv = Buffer.from(ivB64, "base64");
519
568
  const tag = Buffer.from(tagB64, "base64");
520
569
  const ct = Buffer.from(ctB64, "base64");
521
- if (iv.length !== IV_BYTES) throw new Error("SecretVault: bad IV length");
522
- if (tag.length !== TAG_BYTES) throw new Error("SecretVault: bad tag length");
570
+ if (iv.length !== IV_BYTES) throw new ConfigError({
571
+ message: "SecretVault: bad IV length",
572
+ code: ERROR_CODES.CONFIG_PARSE_FAILED,
573
+ context: { expected: IV_BYTES, actual: iv.length }
574
+ });
575
+ if (tag.length !== TAG_BYTES) throw new ConfigError({
576
+ message: "SecretVault: bad tag length",
577
+ code: ERROR_CODES.CONFIG_PARSE_FAILED,
578
+ context: { expected: TAG_BYTES, actual: tag.length }
579
+ });
523
580
  const key = this.loadOrCreateKey();
524
581
  const decipher = createDecipheriv(ALGO, key, iv);
525
582
  decipher.setAuthTag(tag);
@@ -531,11 +588,14 @@ var DefaultSecretVault = class {
531
588
  try {
532
589
  const buf = fs2.readFileSync(this.keyFile);
533
590
  if (buf.length !== KEY_BYTES) {
534
- throw new Error(
535
- `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
536
- );
591
+ throw new ConfigError({
592
+ message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`,
593
+ code: ERROR_CODES.CONFIG_INVALID,
594
+ context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
595
+ });
537
596
  }
538
597
  this.key = buf;
598
+ checkKeyFilePermissions(this.keyFile);
539
599
  return this.key;
540
600
  } catch (err) {
541
601
  if (err.code !== "ENOENT") throw err;
@@ -548,11 +608,14 @@ var DefaultSecretVault = class {
548
608
  if (err.code !== "EEXIST") throw err;
549
609
  const buf = fs2.readFileSync(this.keyFile);
550
610
  if (buf.length !== KEY_BYTES) {
551
- throw new Error(
552
- `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
553
- );
611
+ throw new ConfigError({
612
+ message: `SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`,
613
+ code: ERROR_CODES.CONFIG_INVALID,
614
+ context: { keyFile: this.keyFile, expectedBytes: KEY_BYTES, actualBytes: buf.length }
615
+ });
554
616
  }
555
617
  this.key = buf;
618
+ checkKeyFilePermissions(this.keyFile);
556
619
  return this.key;
557
620
  }
558
621
  this.key = key;
@@ -613,7 +676,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
613
676
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
614
677
  await restrictFilePermissions(configPath);
615
678
  }
616
- async function migratePlaintextSecrets(configPath, vault) {
679
+ async function migratePlaintextSecrets(configPath, vault, logger) {
617
680
  let raw;
618
681
  try {
619
682
  raw = await fs.readFile(configPath, "utf8");
@@ -630,11 +693,14 @@ async function migratePlaintextSecrets(configPath, vault) {
630
693
  const migrated = walkCount(parsed, vault, counter);
631
694
  if (counter.n === 0) return { migrated: 0, file: configPath };
632
695
  await atomicWrite(configPath, JSON.stringify(migrated, null, 2), { mode: 384 });
633
- await restrictFilePermissions(configPath);
696
+ await restrictFilePermissions(
697
+ configPath,
698
+ logger ? { warn: (msg) => logger.warn(msg) } : void 0
699
+ );
634
700
  return { migrated: counter.n, file: configPath };
635
701
  }
636
702
  async function restrictFilePermissions(filePath, opts) {
637
- const warn = ((msg) => console.warn(msg));
703
+ const warn = opts?.warn ?? ((msg) => console.warn(msg));
638
704
  if (process.platform === "win32") {
639
705
  try {
640
706
  const { execFile } = await import('child_process');
@@ -751,18 +817,24 @@ var COLORS = {
751
817
  trace: color.dim
752
818
  };
753
819
  var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
820
+ var LOG_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
754
821
  var DefaultLogger = class _DefaultLogger {
822
+ /** How many file writes between rotation size checks (statSync is not free). */
823
+ static ROTATE_CHECK_EVERY = 100;
755
824
  level;
756
825
  file;
757
826
  bindings;
758
- pretty;
827
+ format;
759
828
  stderr;
829
+ maxFileBytes;
830
+ writesSinceRotateCheck = 0;
760
831
  constructor(opts = {}) {
761
832
  this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
762
833
  this.file = opts.file;
763
834
  this.bindings = opts.bindings ?? {};
764
- this.pretty = opts.pretty ?? true;
835
+ this.format = opts.format ?? parseLogFormat(process.env.WRONGSTACK_LOG_FORMAT);
765
836
  this.stderr = opts.stderr !== false;
837
+ this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
766
838
  if (this.file) {
767
839
  try {
768
840
  fs2.mkdirSync(path4.dirname(this.file), { recursive: true });
@@ -789,11 +861,30 @@ var DefaultLogger = class _DefaultLogger {
789
861
  return new _DefaultLogger({
790
862
  level: this.level,
791
863
  file: this.file,
792
- pretty: this.pretty,
864
+ format: this.format,
793
865
  stderr: this.stderr,
866
+ maxFileBytes: this.maxFileBytes,
794
867
  bindings: { ...this.bindings, ...bindings }
795
868
  });
796
869
  }
870
+ /**
871
+ * Size-based rotation: when the file outgrows `maxFileBytes`, rename it to
872
+ * `<file>.1` (dropping the previous `.1`) so the live file restarts empty.
873
+ * Checked on the first write and every ROTATE_CHECK_EVERY writes after.
874
+ * Best-effort: a rename can fail on Windows while another process holds
875
+ * the file — the next check retries. Multiple processes appending to the
876
+ * same log all run this check; whoever crosses the threshold first wins.
877
+ */
878
+ maybeRotate(file) {
879
+ if (this.writesSinceRotateCheck++ % _DefaultLogger.ROTATE_CHECK_EVERY !== 0) return;
880
+ try {
881
+ const st = fs2.statSync(file);
882
+ if (st.size < this.maxFileBytes) return;
883
+ fs2.rmSync(`${file}.1`, { force: true });
884
+ fs2.renameSync(file, `${file}.1`);
885
+ } catch {
886
+ }
887
+ }
797
888
  log(level, msg, ctx) {
798
889
  const r = LEVEL_RANK[level];
799
890
  const allowed = LEVEL_RANK[this.level];
@@ -805,13 +896,17 @@ var DefaultLogger = class _DefaultLogger {
805
896
  }
806
897
  if (this.file) {
807
898
  try {
899
+ this.maybeRotate(this.file);
808
900
  fs2.appendFileSync(this.file, `${JSON.stringify(entry)}
809
901
  `);
810
902
  } catch {
811
903
  }
812
904
  }
813
905
  if (!this.stderr) return;
814
- if (r <= LEVEL_RANK.warn || this.level === "debug" || this.level === "trace") {
906
+ if (this.format === "json") {
907
+ writeErr(`${JSON.stringify(entry)}
908
+ `);
909
+ } else {
815
910
  const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
816
911
  if (ctx !== void 0) {
817
912
  writeErr(`${head} ${formatCtx(ctx)}
@@ -826,6 +921,9 @@ var DefaultLogger = class _DefaultLogger {
826
921
  function parseLogLevel(raw) {
827
922
  return raw && LOG_LEVELS.has(raw) ? raw : "info";
828
923
  }
924
+ function parseLogFormat(raw) {
925
+ return raw && LOG_FORMATS.has(raw) ? raw : "pretty";
926
+ }
829
927
  function formatCtx(ctx) {
830
928
  if (ctx instanceof Error) return color.dim(ctx.message);
831
929
  if (typeof ctx === "string") return color.dim(ctx);
@@ -835,6 +933,22 @@ function formatCtx(ctx) {
835
933
  return color.dim(String(ctx));
836
934
  }
837
935
  }
936
+ var noOpLogger = {
937
+ // 'error' is the quietest level the Logger contract offers; the methods
938
+ // discard everything regardless, this only matters to level checks.
939
+ level: "error",
940
+ error: () => {
941
+ },
942
+ warn: () => {
943
+ },
944
+ info: () => {
945
+ },
946
+ debug: () => {
947
+ },
948
+ trace: () => {
949
+ },
950
+ child: () => noOpLogger
951
+ };
838
952
 
839
953
  // src/infrastructure/token-counter.ts
840
954
  var PRICE_CACHE_MAX_SIZE = 100;
@@ -975,7 +1089,9 @@ var MEMORY_TYPE_LABELS = {
975
1089
  // src/utils/expect-defined.ts
976
1090
  function expectDefined(value, label) {
977
1091
  if (value === null || value === void 0) {
978
- throw new Error("Expected value to be defined");
1092
+ const err = new Error("Expected value to be defined");
1093
+ err.name = "ExpectDefinedError";
1094
+ throw err;
979
1095
  }
980
1096
  return value;
981
1097
  }
@@ -1023,22 +1139,31 @@ function estimateToolResultTokens(content) {
1023
1139
  function estimateTextTokens(text) {
1024
1140
  return RoughTokenEstimate(text);
1025
1141
  }
1142
+ function computeMessageTokens(msg) {
1143
+ if (typeof msg.content === "string") return estimateTextTokens(msg.content);
1144
+ let total = 0;
1145
+ for (const b of msg.content) {
1146
+ if (b.type === "text") total += estimateTextTokens(b.text);
1147
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
1148
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
1149
+ else total += RoughTokenEstimate(JSON.stringify(b));
1150
+ }
1151
+ return total;
1152
+ }
1026
1153
  function estimateMessageTokens(messages) {
1027
1154
  let total = 0;
1028
1155
  for (const m of messages) {
1029
- if (typeof m.content === "string") {
1030
- total += estimateTextTokens(m.content);
1031
- } else {
1032
- for (const b of m.content) {
1033
- if (b.type === "text") total += estimateTextTokens(b.text);
1034
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
1035
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
1036
- }
1156
+ if (typeof m._estTokens === "number" && m._estTokens > 0) {
1157
+ total += m._estTokens;
1158
+ continue;
1037
1159
  }
1160
+ total += computeMessageTokens(m);
1038
1161
  }
1039
1162
  return total;
1040
1163
  }
1041
1164
  function estimateToolDefTokens(tool) {
1165
+ const cached = tool._estDefTokens;
1166
+ if (typeof cached === "number" && cached > 0) return cached;
1042
1167
  return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
1043
1168
  }
1044
1169
  function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
@@ -1048,6 +1173,11 @@ function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = C
1048
1173
  } else if (Array.isArray(messages)) {
1049
1174
  for (const m of messages) {
1050
1175
  if (typeof m === "object" && m !== null && "content" in m) {
1176
+ const cached = m._estTokens;
1177
+ if (typeof cached === "number" && cached > 0) {
1178
+ messagesTokens += cached;
1179
+ continue;
1180
+ }
1051
1181
  const content = m.content;
1052
1182
  if (typeof content === "string") {
1053
1183
  messagesTokens += RoughTokenEstimate(content);
@@ -1213,6 +1343,18 @@ function findPreserveStart(messages, preserveK) {
1213
1343
  }
1214
1344
  function eliseOldToolResults(messages, opts) {
1215
1345
  const preserveStart = findPreserveStart(messages, opts.preserveK);
1346
+ let hasOversized = false;
1347
+ for (let i = 0; i < preserveStart && !hasOversized; i++) {
1348
+ const msg = messages[i];
1349
+ if (!msg || !Array.isArray(msg.content)) continue;
1350
+ for (const b of msg.content) {
1351
+ if (b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold) {
1352
+ hasOversized = true;
1353
+ break;
1354
+ }
1355
+ }
1356
+ }
1357
+ if (!hasOversized) return { messages, saved: 0, changed: false };
1216
1358
  let saved = 0;
1217
1359
  let changed = false;
1218
1360
  const next = new Array(messages.length);
@@ -2393,6 +2535,77 @@ Remember: your job is to make the user a better developer, not just to complete
2393
2535
  tags: ["teaching", "mentor", "learning"],
2394
2536
  toolPreferences: ["read", "edit", "explain"],
2395
2537
  suggestedSkills: ["prompt-engineering", "skill-creator", "node-modern", "typescript-strict"]
2538
+ },
2539
+ {
2540
+ id: "research-web",
2541
+ name: "Research Web",
2542
+ description: "Current-data research \u2014 search web, verify, inject findings into context",
2543
+ prompt: `## Research Web Mode
2544
+
2545
+ You are in research mode. Your role: find, verify, and incorporate
2546
+ current web data. Your training data is stale \u2014 every factual claim
2547
+ about version numbers, API surfaces, package status, or ecosystem
2548
+ changes must be verified against live sources.
2549
+
2550
+ ### When to research
2551
+ - The user asks "is this still the case?", "what's current?", "latest version?"
2552
+ - You're about to claim a version number, deprecation, or API change
2553
+ - You're comparing tools, packages, or approaches released in the last 12 months
2554
+ - You realize your knowledge may be >6 months old on a fast-moving topic
2555
+
2556
+ ### Research methodology
2557
+ 1. **Search first, fetch selectively.** Use web_search with 5-8 results for
2558
+ broad queries. Then web_fetch the 1-2 most authoritative results for detail.
2559
+ Don't fetch every result \u2014 you'll burn tokens on noise.
2560
+ 2. **Cross-reference.** One source is a data point. Two sources that agree
2561
+ is a signal. Three is confirmation. Flag single-source claims as tentative.
2562
+ 3. **Cite sources.** Every factual claim from web data must include where it
2563
+ came from: domain name, and date if visible on the page.
2564
+ 4. **Know when to stop.** 2-3 searches + 1-2 fetches is usually sufficient.
2565
+ If you're on your 5th search without a clear answer, pause and tell the user
2566
+ what you've found and what's still unclear \u2014 let them decide to dig deeper.
2567
+ 5. **Inject findings for reuse.** After gathering current data, use
2568
+ context_manager with add_note to inject a structured "Research Findings"
2569
+ block into the conversation. Future turns see this and don't re-search.
2570
+
2571
+ ### Self-injection pattern
2572
+ When you discover current data mid-research, inject it so subsequent turns
2573
+ benefit without re-searching:
2574
+
2575
+ web_search("Next.js middleware breaking changes 2025")
2576
+ \u2192 Surfaced: Next.js 15.2 changed middleware runtime from edge to node
2577
+ web_fetch("https://nextjs.org/docs/messages/middleware-upgrade-guide")
2578
+ \u2192 Confirmed: middleware now runs on Node.js runtime by default
2579
+ context_manager: add_note(
2580
+ "## Research: Next.js middleware
2581
+ - Next.js 15.2: middleware defaults to Node.js runtime (was edge)
2582
+ - Breaking: edge-only APIs (crypto.subtle, WebSocket) no longer available
2583
+ - Migration: use node:* equivalents or set runtime: 'edge' explicitly
2584
+ - Source: nextjs.org/docs/messages/middleware-upgrade-guide"
2585
+ )
2586
+
2587
+ The add_note persists in conversation \u2014 you won't re-search on the next turn.
2588
+
2589
+ ### Anti-patterns
2590
+ - Don't research things already in the conversation context (including
2591
+ earlier add_note blocks you injected)
2592
+ - Don't treat a single web search result as ground truth \u2014 cross-reference
2593
+ - Don't inject raw JSON or search result dumps via add_note \u2014 summarize
2594
+ - Don't research while the user is waiting for a quick code edit \u2014 toggle
2595
+ research-web mode only during analysis/discussion phases
2596
+ - Don't research-loop: 5+ searches on one topic \u2192 stop and ask the user
2597
+
2598
+ ### Exiting research mode
2599
+ When the user no longer needs current-data research, suggest switching back
2600
+ to the previous mode. You stay in research mode until explicitly told to
2601
+ switch \u2014 but don't force web searches on every turn. The methodology rules
2602
+ above already gate when to actually search.
2603
+
2604
+ When you're done with research: suggest the user run \`/mode default\` or
2605
+ their previous mode.`,
2606
+ tags: ["research", "web", "current-data", "up-to-date"],
2607
+ toolPreferences: ["web_search", "web_fetch", "search", "fetch", "context_manager"],
2608
+ suggestedSkills: ["research-web", "tech-stack", "node-modern", "security-scanner", "react-modern"]
2396
2609
  }
2397
2610
  ];
2398
2611
 
@@ -2563,28 +2776,40 @@ var InMemoryAgentBridge = class {
2563
2776
  return () => this.subscriptions.delete(handler);
2564
2777
  }
2565
2778
  async request(msg, timeoutMs) {
2566
- if (this.stopped) throw new Error("Bridge is stopped");
2779
+ if (this.stopped) throw new AgentError({
2780
+ message: "Bridge is stopped",
2781
+ code: ERROR_CODES.AGENT_ABORTED
2782
+ });
2567
2783
  const timeout = timeoutMs ?? this.timeoutMs;
2568
2784
  const correlationId = msg.id;
2569
2785
  if (this.inflightGuards.has(correlationId)) {
2570
- throw new Error(
2571
- `Bridge request id "${correlationId}" collides with an in-flight request \u2014 caller is reusing message ids`
2572
- );
2786
+ throw new AgentError({
2787
+ message: `Bridge request id "${correlationId}" collides with an in-flight request \u2014 caller is reusing message ids`,
2788
+ code: ERROR_CODES.AGENT_RUN_FAILED,
2789
+ context: { correlationId }
2790
+ });
2573
2791
  }
2574
2792
  this.inflightGuards.add(correlationId);
2575
- return new Promise((resolve3, reject) => {
2793
+ return new Promise((resolve4, reject) => {
2576
2794
  const timer = setTimeout(() => {
2577
2795
  this.inflightGuards.delete(correlationId);
2578
2796
  this.pendingRequests.delete(correlationId);
2579
- reject(new Error(`Request ${correlationId} timed out after ${timeout}ms`));
2797
+ reject(new AgentError({
2798
+ message: `Request ${correlationId} timed out after ${timeout}ms`,
2799
+ code: ERROR_CODES.AGENT_RUN_FAILED,
2800
+ context: { correlationId, timeoutMs: timeout }
2801
+ }));
2580
2802
  }, timeout);
2581
2803
  if (!this.inflightGuards.has(correlationId)) {
2582
2804
  clearTimeout(timer);
2583
- reject(new Error("Bridge stopped"));
2805
+ reject(new AgentError({
2806
+ message: "Bridge stopped",
2807
+ code: ERROR_CODES.AGENT_ABORTED
2808
+ }));
2584
2809
  return;
2585
2810
  }
2586
2811
  this.pendingRequests.set(correlationId, {
2587
- resolve: resolve3,
2812
+ resolve: resolve4,
2588
2813
  reject,
2589
2814
  timer
2590
2815
  });
@@ -2601,7 +2826,10 @@ var InMemoryAgentBridge = class {
2601
2826
  this.stopped = true;
2602
2827
  for (const [, p] of this.pendingRequests) {
2603
2828
  clearTimeout(p.timer);
2604
- p.reject(new Error("Bridge stopped"));
2829
+ p.reject(new AgentError({
2830
+ message: "Bridge stopped",
2831
+ code: ERROR_CODES.AGENT_ABORTED
2832
+ }));
2605
2833
  }
2606
2834
  this.pendingRequests.clear();
2607
2835
  this.inflightGuards.clear();
@@ -2780,11 +3008,11 @@ function validateAgainstSchema(value, schema) {
2780
3008
  walk2(value, schema, "", errors);
2781
3009
  return { ok: errors.length === 0, errors };
2782
3010
  }
2783
- function walk2(value, schema, path6, errors) {
3011
+ function walk2(value, schema, path7, errors) {
2784
3012
  if (schema.enum !== void 0) {
2785
3013
  if (!schema.enum.some((e) => deepEqual(e, value))) {
2786
3014
  errors.push({
2787
- path: path6 || "<root>",
3015
+ path: path7 || "<root>",
2788
3016
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
2789
3017
  });
2790
3018
  return;
@@ -2793,7 +3021,7 @@ function walk2(value, schema, path6, errors) {
2793
3021
  if (typeof schema.type === "string") {
2794
3022
  if (!checkType(value, schema.type)) {
2795
3023
  errors.push({
2796
- path: path6 || "<root>",
3024
+ path: path7 || "<root>",
2797
3025
  message: `expected ${schema.type}, got ${describeType(value)}`
2798
3026
  });
2799
3027
  return;
@@ -2803,19 +3031,19 @@ function walk2(value, schema, path6, errors) {
2803
3031
  const obj = value;
2804
3032
  for (const req of schema.required ?? []) {
2805
3033
  if (!(req in obj)) {
2806
- errors.push({ path: joinPath(path6, req), message: "required property missing" });
3034
+ errors.push({ path: joinPath(path7, req), message: "required property missing" });
2807
3035
  }
2808
3036
  }
2809
3037
  if (schema.properties) {
2810
3038
  for (const [key, subSchema] of Object.entries(schema.properties)) {
2811
3039
  if (key in obj) {
2812
- walk2(obj[key], subSchema, joinPath(path6, key), errors);
3040
+ walk2(obj[key], subSchema, joinPath(path7, key), errors);
2813
3041
  }
2814
3042
  }
2815
3043
  }
2816
3044
  }
2817
3045
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
2818
- value.forEach((item, i) => walk2(item, schema.items, `${path6}[${i}]`, errors));
3046
+ value.forEach((item, i) => walk2(item, schema.items, `${path7}[${i}]`, errors));
2819
3047
  }
2820
3048
  }
2821
3049
  function checkType(value, type) {
@@ -2913,7 +3141,7 @@ function createToolOutputSerializer(opts = {}) {
2913
3141
  }
2914
3142
 
2915
3143
  // src/execution/tool-executor.ts
2916
- var ToolExecutor = class {
3144
+ var ToolExecutor = class _ToolExecutor {
2917
3145
  constructor(registry, opts) {
2918
3146
  this.registry = registry;
2919
3147
  this.opts = opts;
@@ -2925,6 +3153,10 @@ var ToolExecutor = class {
2925
3153
  }
2926
3154
  registry;
2927
3155
  opts;
3156
+ /** Minimum gap between coalesced `partial_output` tool.progress emits. */
3157
+ static PROGRESS_EMIT_INTERVAL_MS = 100;
3158
+ /** Max chars of accumulated stream text carried per coalesced emit. */
3159
+ static PROGRESS_TAIL_CHARS = 16384;
2928
3160
  serializer;
2929
3161
  iterationTimeoutMs;
2930
3162
  maxToolTimeoutMs;
@@ -2970,9 +3202,6 @@ Please call the tool again with arguments that match its inputSchema. You can us
2970
3202
  return { result, tool, durationMs: Date.now() - start };
2971
3203
  }
2972
3204
  const toolDangerousCaps = getDangerousCapabilities(tool);
2973
- if (toolDangerousCaps.length > 0) {
2974
- if (this.opts.events) ;
2975
- }
2976
3205
  if (hasMalformedArguments(use.input)) {
2977
3206
  const result = this.malformedInputResult(use, extractMalformedRaw(use.input));
2978
3207
  budget = this.decrementBudget(result, budget);
@@ -3210,17 +3439,48 @@ ${post.additionalContext}` };
3210
3439
  throw new Error(`Tool "${tool.name}" does not support streaming execution`);
3211
3440
  }
3212
3441
  const stream = tool.executeStream(input, ctx, { signal });
3213
- for await (const ev of stream) {
3214
- if (ev.type === "final") {
3215
- finalOutput = ev.output;
3216
- sawFinal = true;
3217
- break;
3218
- }
3442
+ const iter = stream[Symbol.asyncIterator]();
3443
+ let progressTail = "";
3444
+ let lastProgressEmitAt = 0;
3445
+ const emitProgress = (ev) => {
3219
3446
  this.opts.events?.emit("tool.progress", {
3220
3447
  name: tool.name,
3221
3448
  id: toolUseId ?? "<unknown>",
3222
3449
  event: ev
3223
3450
  });
3451
+ };
3452
+ const flushProgressTail = (force) => {
3453
+ if (progressTail.length === 0) return;
3454
+ const now = Date.now();
3455
+ if (!force && now - lastProgressEmitAt < _ToolExecutor.PROGRESS_EMIT_INTERVAL_MS) return;
3456
+ const text = progressTail;
3457
+ progressTail = "";
3458
+ lastProgressEmitAt = now;
3459
+ emitProgress({ type: "partial_output", text });
3460
+ };
3461
+ try {
3462
+ while (true) {
3463
+ const { done, value: ev } = await iter.next();
3464
+ if (done) break;
3465
+ if (ev.type === "final") {
3466
+ finalOutput = ev.output;
3467
+ sawFinal = true;
3468
+ break;
3469
+ }
3470
+ if (ev.type === "partial_output" && typeof ev.text === "string") {
3471
+ progressTail += ev.text;
3472
+ if (progressTail.length > _ToolExecutor.PROGRESS_TAIL_CHARS) {
3473
+ progressTail = progressTail.slice(-_ToolExecutor.PROGRESS_TAIL_CHARS);
3474
+ }
3475
+ flushProgressTail(false);
3476
+ continue;
3477
+ }
3478
+ flushProgressTail(true);
3479
+ emitProgress(ev);
3480
+ }
3481
+ flushProgressTail(true);
3482
+ } finally {
3483
+ await iter.return?.(void 0);
3224
3484
  }
3225
3485
  if (!sawFinal) {
3226
3486
  throw new Error(`tool "${tool.name}" executeStream completed without a 'final' event`);
@@ -3329,6 +3589,253 @@ function extractMalformedRaw(input) {
3329
3589
  }
3330
3590
  }
3331
3591
 
3592
+ // src/core/conversation-state.ts
3593
+ var ConversationState = class {
3594
+ ctx;
3595
+ listeners = /* @__PURE__ */ new Set();
3596
+ constructor(ctx) {
3597
+ this.ctx = ctx;
3598
+ }
3599
+ get messages() {
3600
+ return this.ctx.messages;
3601
+ }
3602
+ get todos() {
3603
+ return this.ctx.todos;
3604
+ }
3605
+ get meta() {
3606
+ return this.ctx.meta;
3607
+ }
3608
+ /**
3609
+ * Cheap immutable snapshot. Useful for tests and for compaction passes
3610
+ * that need a stable view across an async boundary.
3611
+ *
3612
+ * Uses shallow-freeze instead of deep-freeze: only the wrapper object
3613
+ * and the three content arrays are frozen. Individual message/todo
3614
+ * objects are NOT recursively frozen — they are reconstructed via
3615
+ * spread copies and are immutable by convention. This cuts the freeze
3616
+ * count from O(n·m·d) (n=messages, m=content blocks, d=depth) to O(1).
3617
+ */
3618
+ snapshot() {
3619
+ const snap = {
3620
+ messages: [...this.ctx.messages],
3621
+ todos: [...this.ctx.todos],
3622
+ meta: { ...this.ctx.meta }
3623
+ };
3624
+ Object.freeze(snap.messages);
3625
+ Object.freeze(snap.todos);
3626
+ Object.freeze(snap.meta);
3627
+ return Object.freeze(snap);
3628
+ }
3629
+ appendMessage(message) {
3630
+ if (message._estTokens === void 0) {
3631
+ message._estTokens = computeMessageTokens(message);
3632
+ }
3633
+ this.ctx.messages.splice(this.ctx.messages.length, 0, message);
3634
+ this.emit({ kind: "message_appended", message });
3635
+ }
3636
+ replaceMessages(messages) {
3637
+ for (const m of messages) {
3638
+ if (m._estTokens === void 0) {
3639
+ m._estTokens = computeMessageTokens(m);
3640
+ }
3641
+ }
3642
+ const arr = this.ctx.messages;
3643
+ if (messages.length < arr.length) {
3644
+ arr.length = messages.length;
3645
+ }
3646
+ for (let i = 0; i < messages.length; i++) {
3647
+ arr[i] = messages[i];
3648
+ }
3649
+ if (messages.some(
3650
+ (m) => Array.isArray(m.content) && m.content.some((b) => b.type === "tool_use" || b.type === "tool_result")
3651
+ )) {
3652
+ this.ctx.toolAdjacencyDirty = true;
3653
+ }
3654
+ this.emit({ kind: "messages_replaced", messages: [...messages] });
3655
+ }
3656
+ replaceTodos(todos) {
3657
+ const allDone = todos.length > 0 && todos.every((t) => t.status === "completed");
3658
+ const effective = allDone ? [] : todos;
3659
+ this.ctx.todos.length = 0;
3660
+ this.ctx.todos.splice(0, 0, ...effective);
3661
+ this.emit({ kind: "todos_replaced", todos: [...effective] });
3662
+ }
3663
+ setMeta(key, value) {
3664
+ this.ctx.meta[key] = value;
3665
+ this.emit({ kind: "meta_set", key, value });
3666
+ }
3667
+ deleteMeta(key) {
3668
+ if (!(key in this.ctx.meta)) return;
3669
+ delete this.ctx.meta[key];
3670
+ this.emit({ kind: "meta_deleted", key });
3671
+ }
3672
+ clearMeta() {
3673
+ const keys = Object.keys(this.ctx.meta);
3674
+ if (keys.length === 0) return;
3675
+ for (const key of keys) delete this.ctx.meta[key];
3676
+ this.emit({ kind: "meta_cleared" });
3677
+ }
3678
+ /**
3679
+ * Subscribe to mutations that go through this wrapper. Direct mutations of
3680
+ * the compatibility arrays are intentionally not observed.
3681
+ */
3682
+ onChange(listener) {
3683
+ this.listeners.add(listener);
3684
+ return () => this.listeners.delete(listener);
3685
+ }
3686
+ emit(change) {
3687
+ for (const h of this.listeners) {
3688
+ try {
3689
+ h(change, this);
3690
+ } catch {
3691
+ }
3692
+ }
3693
+ }
3694
+ };
3695
+
3696
+ // src/core/context.ts
3697
+ var Context = class {
3698
+ messages = [];
3699
+ todos = [];
3700
+ readFiles = /* @__PURE__ */ new Set();
3701
+ fileMtimes = /* @__PURE__ */ new Map();
3702
+ systemPrompt;
3703
+ provider;
3704
+ session;
3705
+ signal;
3706
+ tokenCounter;
3707
+ cwd;
3708
+ projectRoot;
3709
+ /** Mutable working directory — starts as `cwd`. Change via `setWorkingDir()`. */
3710
+ workingDir;
3711
+ model;
3712
+ tools = [];
3713
+ meta = {};
3714
+ /** Agent id performing this run (e.g. 'leader', 'executor'). */
3715
+ agentId;
3716
+ /** Human-readable agent name. */
3717
+ agentName;
3718
+ /** Callbacks fired when `setWorkingDir()` changes the working directory. */
3719
+ _onWorkingDirChanged = [];
3720
+ /**
3721
+ * Set to true when the conversation gains new tool_use or tool_result
3722
+ * blocks — the only time repairToolUseAdjacency() can find new issues.
3723
+ * buildAndRunRequestPipeline() checks this flag to skip an O(n) scan
3724
+ * on iterations where no tool content was added (pure text responses).
3725
+ */
3726
+ toolAdjacencyDirty = false;
3727
+ constructor(init) {
3728
+ this.systemPrompt = init.systemPrompt;
3729
+ this.provider = init.provider;
3730
+ this.session = init.session;
3731
+ this.signal = init.signal;
3732
+ this.tokenCounter = init.tokenCounter;
3733
+ this.cwd = init.cwd;
3734
+ this.projectRoot = init.projectRoot;
3735
+ this.workingDir = init.workingDir ?? init.cwd;
3736
+ this.model = init.model;
3737
+ this.tools = init.tools ?? [];
3738
+ this.agentId = init.agentId ?? "unknown";
3739
+ this.agentName = init.agentName ?? "Unknown Agent";
3740
+ }
3741
+ /**
3742
+ * Observable wrapper over the mutable conversation state. Lazy so
3743
+ * subsystems that don't subscribe pay nothing. Mutations made directly
3744
+ * on `ctx.messages` / `ctx.todos` are still visible through this
3745
+ * wrapper's read API (it holds a reference, not a copy) but only
3746
+ * mutations that go through `state.appendMessage()` etc. fire
3747
+ * `onChange`. New code should prefer the wrapper API.
3748
+ */
3749
+ _state = null;
3750
+ get state() {
3751
+ if (!this._state) this._state = new ConversationState(this);
3752
+ return this._state;
3753
+ }
3754
+ /**
3755
+ * Register a teardown hook tied to the current run's abort signal.
3756
+ * Hooks registered before a run starts are stored and fired when the
3757
+ * next run ends; there is no immediate fire when no run is active.
3758
+ *
3759
+ * **Scope:** these hooks fire on the **whole agent run's** abort, not on
3760
+ * an individual tool call. For per-tool teardown of resources owned by
3761
+ * the tool author (child processes, handles), prefer `Tool.cleanup` —
3762
+ * see its JSDoc for the full rule.
3763
+ */
3764
+ abortHooks = /* @__PURE__ */ new Set();
3765
+ registerAbortHook(fn) {
3766
+ this.abortHooks.add(fn);
3767
+ return () => this.abortHooks.delete(fn);
3768
+ }
3769
+ async drainAbortHooks() {
3770
+ const snapshot = [...this.abortHooks].reverse();
3771
+ this.abortHooks.clear();
3772
+ for (const fn of snapshot) {
3773
+ try {
3774
+ await fn();
3775
+ } catch {
3776
+ }
3777
+ }
3778
+ }
3779
+ recordRead(absPath, mtimeMs) {
3780
+ this.readFiles.add(absPath);
3781
+ this.fileMtimes.set(absPath, mtimeMs);
3782
+ }
3783
+ /** Clear accumulated file-read metadata after compaction or at boundaries
3784
+ * where stale read history could cause tools to skip legitimate re-reads.
3785
+ * The agent re-populates this naturally on the next file access. */
3786
+ clearFileTracking() {
3787
+ this.readFiles.clear();
3788
+ this.fileMtimes.clear();
3789
+ }
3790
+ hasRead(absPath) {
3791
+ return this.readFiles.has(absPath);
3792
+ }
3793
+ lastReadMtime(absPath) {
3794
+ return this.fileMtimes.get(absPath);
3795
+ }
3796
+ /**
3797
+ * Change the working directory for path resolution. Resolves relative paths
3798
+ * against `projectRoot` and validates the result is within the project root.
3799
+ * Fires all registered `onWorkingDirChanged` callbacks.
3800
+ * Returns the resolved absolute path.
3801
+ */
3802
+ setWorkingDir(dir) {
3803
+ const resolved = path4.isAbsolute(dir) ? path4.resolve(dir) : path4.resolve(this.projectRoot, dir);
3804
+ const root = path4.resolve(this.projectRoot);
3805
+ const rel = path4.relative(root, resolved);
3806
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) {
3807
+ throw new Error(
3808
+ `Working directory "${resolved}" is outside project root "${root}"`
3809
+ );
3810
+ }
3811
+ const old = this.workingDir;
3812
+ this.workingDir = resolved;
3813
+ for (const cb of this._onWorkingDirChanged) {
3814
+ try {
3815
+ cb(resolved, old);
3816
+ } catch {
3817
+ }
3818
+ }
3819
+ return resolved;
3820
+ }
3821
+ /**
3822
+ * Register a callback that fires when the working directory changes.
3823
+ * Returns an unsubscribe function. Callbacks are fired synchronously
3824
+ * inside `setWorkingDir()` — errors in callbacks are swallowed so one
3825
+ * bad listener doesn't prevent others from executing.
3826
+ */
3827
+ onWorkingDirChanged(cb) {
3828
+ this._onWorkingDirChanged.push(cb);
3829
+ return () => {
3830
+ const idx = this._onWorkingDirChanged.indexOf(cb);
3831
+ if (idx >= 0) this._onWorkingDirChanged.splice(idx, 1);
3832
+ };
3833
+ }
3834
+ usage() {
3835
+ return this.tokenCounter.total();
3836
+ }
3837
+ };
3838
+
3332
3839
  // src/utils/regex-guard.ts
3333
3840
  var MAX_PATTERN_LEN = 512;
3334
3841
  var DANGEROUS_PATTERNS = [
@@ -3641,6 +4148,6 @@ function renderPlainText(meta, events) {
3641
4148
  return lines.join("\n");
3642
4149
  }
3643
4150
 
3644
- export { AgentError, CONTEXT_WINDOW_MODES, ConfigError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_TOOLS_CONFIG, DefaultErrorHandler, DefaultLogger, DefaultModelsRegistry, DefaultPathResolver, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultTokenCounter, ERROR_CODES, FsError, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, MEMORY_TYPE_LABELS, PluginError, ProviderError, SessionError, StreamHangError, ToolError, ToolExecutor, WrongStackError, asBlocks, asText, buildRecoveryStrategies, classifyFamily, computeTaskProgress, createMessage, decryptConfigSecrets, encryptConfigSecrets, findCriticalPath, formatContextWindowModeList, getContextWindowMode, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSecretField, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, migratePlaintextSecrets, resolveContextWindowPolicy, rewriteConfigEncrypted, toWrongStackError, topologicalSort };
4151
+ export { AgentError, CONTEXT_WINDOW_MODES, ConfigError, Context, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_TOOLS_CONFIG, DefaultErrorHandler, DefaultLogger, DefaultModelsRegistry, DefaultPathResolver, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultTokenCounter, ERROR_CODES, FsError, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, MEMORY_TYPE_LABELS, PluginError, ProviderError, SddError, SessionError, StreamHangError, ToolError, ToolExecutor, WrongStackError, asBlocks, asText, buildRecoveryStrategies, classifyFamily, computeTaskProgress, createMessage, decryptConfigSecrets, encryptConfigSecrets, findCriticalPath, formatContextWindowModeList, getContextWindowMode, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSddError, isSecretField, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, migratePlaintextSecrets, noOpLogger, resolveContextWindowPolicy, rewriteConfigEncrypted, toWrongStackError, topologicalSort };
3645
4152
  //# sourceMappingURL=index.js.map
3646
4153
  //# sourceMappingURL=index.js.map