@wrongstack/core 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{compactor-BUU6Zm_3.d.ts → compactor-DpJBI1YH.d.ts} +7 -1
  2. package/dist/{config-CKLYPkCi.d.ts → config-D2qvAxVd.d.ts} +38 -1
  3. package/dist/coordination/index.d.ts +3 -3
  4. package/dist/coordination/index.js +283 -244
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +7 -7
  7. package/dist/defaults/index.js +803 -524
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/{events-CNB9PALO.d.ts → events-BHIQs4o1.d.ts} +7 -0
  10. package/dist/execution/index.d.ts +10 -7
  11. package/dist/execution/index.js +166 -18
  12. package/dist/execution/index.js.map +1 -1
  13. package/dist/extension/index.d.ts +3 -3
  14. package/dist/extension/index.js +14 -7
  15. package/dist/extension/index.js.map +1 -1
  16. package/dist/{index-BDb0cAMP.d.ts → index-hWNybrNZ.d.ts} +3 -5
  17. package/dist/index.d.ts +12 -12
  18. package/dist/index.js +629 -299
  19. package/dist/index.js.map +1 -1
  20. package/dist/infrastructure/index.d.ts +5 -5
  21. package/dist/infrastructure/index.js +191 -20
  22. package/dist/infrastructure/index.js.map +1 -1
  23. package/dist/kernel/index.d.ts +4 -4
  24. package/dist/kernel/index.js.map +1 -1
  25. package/dist/{mcp-servers-DR35ojJZ.d.ts → mcp-servers-C2OopXOn.d.ts} +20 -4
  26. package/dist/observability/index.d.ts +1 -1
  27. package/dist/{path-resolver-Cl_q0u-R.d.ts → path-resolver--59rCou3.d.ts} +1 -1
  28. package/dist/{provider-runner-BXuADQqQ.d.ts → provider-runner-B39miKRw.d.ts} +1 -1
  29. package/dist/sdd/index.d.ts +1 -1
  30. package/dist/storage/index.d.ts +4 -3
  31. package/dist/storage/index.js +180 -13
  32. package/dist/storage/index.js.map +1 -1
  33. package/dist/{tool-executor-DKu4A6nB.d.ts → tool-executor-HsBLGRaA.d.ts} +2 -2
  34. package/dist/types/index.d.ts +7 -7
  35. package/dist/types/index.js +206 -9
  36. package/dist/types/index.js.map +1 -1
  37. package/dist/utils/index.d.ts +23 -2
  38. package/dist/utils/index.js +93 -1
  39. package/dist/utils/index.js.map +1 -1
  40. package/package.json +1 -1
@@ -1,7 +1,6 @@
1
1
  import { randomBytes, randomUUID } from 'crypto';
2
2
  import * as fsp from 'fs/promises';
3
3
  import * as path2 from 'path';
4
- import 'fs';
5
4
  import * as os from 'os';
6
5
 
7
6
  // src/storage/session-store.ts
@@ -70,6 +69,98 @@ async function renameWithRetry(from, to) {
70
69
  throw lastErr;
71
70
  }
72
71
 
72
+ // src/utils/message-invariants.ts
73
+ function repairToolUseAdjacency(messages) {
74
+ const removedToolUses = [];
75
+ const removedToolResults = [];
76
+ let removedMessages = 0;
77
+ let changed = false;
78
+ const out = [];
79
+ for (let i = 0; i < messages.length; i++) {
80
+ const original = messages[i];
81
+ let msg = original;
82
+ if (hasToolUse(msg)) {
83
+ const nextIds = toolResultIds(messages[i + 1]);
84
+ const filtered = mapContent(msg, (blocks) => {
85
+ const next = [];
86
+ for (const block of blocks) {
87
+ if (block.type === "tool_use" && !nextIds.has(block.id)) {
88
+ removedToolUses.push(block.id);
89
+ changed = true;
90
+ continue;
91
+ }
92
+ next.push(block);
93
+ }
94
+ return next;
95
+ });
96
+ msg = filtered ?? msg;
97
+ }
98
+ if (hasToolResult(msg)) {
99
+ const allowed = toolUseIds(out[out.length - 1]);
100
+ const filtered = mapContent(msg, (blocks) => {
101
+ const next = [];
102
+ for (const block of blocks) {
103
+ if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
104
+ removedToolResults.push(block.tool_use_id);
105
+ changed = true;
106
+ continue;
107
+ }
108
+ next.push(block);
109
+ }
110
+ return next;
111
+ });
112
+ msg = filtered ?? msg;
113
+ }
114
+ if (isEmptyMessage(msg)) {
115
+ removedMessages++;
116
+ changed = true;
117
+ continue;
118
+ }
119
+ out.push(msg);
120
+ }
121
+ return {
122
+ messages: changed ? out : messages,
123
+ report: { changed, removedToolUses, removedToolResults, removedMessages }
124
+ };
125
+ }
126
+ function hasToolUse(msg) {
127
+ return contentBlocks(msg).some((b) => b.type === "tool_use");
128
+ }
129
+ function hasToolResult(msg) {
130
+ return contentBlocks(msg).some((b) => b.type === "tool_result");
131
+ }
132
+ function toolUseIds(msg) {
133
+ const ids = /* @__PURE__ */ new Set();
134
+ if (!msg || msg.role !== "assistant") return ids;
135
+ for (const block of contentBlocks(msg)) {
136
+ if (block.type === "tool_use") ids.add(block.id);
137
+ }
138
+ return ids;
139
+ }
140
+ function toolResultIds(msg) {
141
+ const ids = /* @__PURE__ */ new Set();
142
+ if (!msg || msg.role !== "user") return ids;
143
+ for (const block of contentBlocks(msg)) {
144
+ if (block.type === "tool_result") ids.add(block.tool_use_id);
145
+ }
146
+ return ids;
147
+ }
148
+ function contentBlocks(msg) {
149
+ return msg && Array.isArray(msg.content) ? msg.content : [];
150
+ }
151
+ function mapContent(msg, fn) {
152
+ if (!Array.isArray(msg.content)) return msg;
153
+ const next = fn(msg.content);
154
+ if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
155
+ return msg;
156
+ }
157
+ return { ...msg, content: next };
158
+ }
159
+ function isEmptyMessage(msg) {
160
+ if (typeof msg.content === "string") return msg.content.trim().length === 0;
161
+ return msg.content.length === 0;
162
+ }
163
+
73
164
  // src/storage/session-store.ts
74
165
  var DefaultSessionStore = class {
75
166
  dir;
@@ -272,11 +363,17 @@ var DefaultSessionStore = class {
272
363
  if (openToolUses.size > 0) {
273
364
  this.events?.emit("session.damaged", {
274
365
  sessionId,
275
- detail: `${openToolUses.size} tool_use blocks without matching results \u2014 replay truncated`
366
+ detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
367
+ });
368
+ }
369
+ const repaired = repairToolUseAdjacency(messages);
370
+ if (repaired.report.changed) {
371
+ this.events?.emit("session.damaged", {
372
+ sessionId,
373
+ detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
276
374
  });
277
- return { messages, usage };
278
375
  }
279
- return { messages, usage };
376
+ return { messages: repaired.messages, usage };
280
377
  }
281
378
  };
282
379
  var FileSessionWriter = class {
@@ -302,6 +399,7 @@ var FileSessionWriter = class {
302
399
  startedAt;
303
400
  meta;
304
401
  closed = false;
402
+ closing = false;
305
403
  manifestFile;
306
404
  summary;
307
405
  tokenIn = 0;
@@ -317,9 +415,7 @@ var FileSessionWriter = class {
317
415
  resumed;
318
416
  appendFailCount = 0;
319
417
  lastAppendWarnAt = 0;
320
- async writeSessionStart() {
321
- if (this.initDone || this.closed) return;
322
- this.initDone = true;
418
+ async writeSessionStartLazy() {
323
419
  const record = `${JSON.stringify({
324
420
  type: this.resumed ? "session_resumed" : "session_start",
325
421
  ts: this.startedAt,
@@ -338,7 +434,8 @@ var FileSessionWriter = class {
338
434
  async append(event) {
339
435
  if (this.closed) return;
340
436
  if (!this.initDone) {
341
- await this.writeSessionStart();
437
+ this.initDone = true;
438
+ await this.writeSessionStartLazy();
342
439
  }
343
440
  this.observeForSummary(event);
344
441
  try {
@@ -379,7 +476,8 @@ var FileSessionWriter = class {
379
476
  }
380
477
  }
381
478
  async close() {
382
- if (this.closed) return;
479
+ if (this.closing) return;
480
+ this.closing = true;
383
481
  this.closed = true;
384
482
  if (this.manifestFile) {
385
483
  try {
@@ -781,6 +879,8 @@ function deepFreeze(obj) {
781
879
  }
782
880
  return Object.freeze(obj);
783
881
  }
882
+
883
+ // src/security/config-secrets.ts
784
884
  function decryptConfigSecrets(cfg, vault) {
785
885
  return walk(cfg, vault, (v, key) => {
786
886
  try {
@@ -820,6 +920,57 @@ function isSecretField(name) {
820
920
  return SECRET_KEY_PATTERN.test(lc);
821
921
  }
822
922
 
923
+ // src/types/context-window.ts
924
+ var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
925
+ var CONTEXT_WINDOW_MODES = Object.freeze([
926
+ {
927
+ id: "balanced",
928
+ name: "Balanced",
929
+ description: "Default rolling compaction: recent work stays verbatim, old tool output is trimmed.",
930
+ thresholds: { warn: 0.6, soft: 0.75, hard: 0.9 },
931
+ aggressiveOn: "soft",
932
+ preserveK: 10,
933
+ eliseThreshold: 2e3,
934
+ targetLoad: 0.65
935
+ },
936
+ {
937
+ id: "frugal",
938
+ name: "Frugal",
939
+ description: "Token-saver mode: compacts early and keeps a tighter verbatim tail.",
940
+ thresholds: { warn: 0.45, soft: 0.6, hard: 0.75 },
941
+ aggressiveOn: "warn",
942
+ preserveK: 6,
943
+ eliseThreshold: 700,
944
+ targetLoad: 0.5
945
+ },
946
+ {
947
+ id: "deep",
948
+ name: "Deep",
949
+ description: "Long-reasoning mode: delays compaction and keeps more recent turns intact.",
950
+ thresholds: { warn: 0.72, soft: 0.86, hard: 0.96 },
951
+ aggressiveOn: "hard",
952
+ preserveK: 18,
953
+ eliseThreshold: 5e3,
954
+ targetLoad: 0.78
955
+ },
956
+ {
957
+ id: "archival",
958
+ name: "Archival",
959
+ description: "Decision-preserving mode: compacts steadily while keeping summaries prominent.",
960
+ thresholds: { warn: 0.55, soft: 0.7, hard: 0.84 },
961
+ aggressiveOn: "soft",
962
+ preserveK: 8,
963
+ eliseThreshold: 1200,
964
+ targetLoad: 0.58
965
+ }
966
+ ]);
967
+ function listContextWindowModes() {
968
+ return CONTEXT_WINDOW_MODES.map((m) => ({ ...m, thresholds: { ...m.thresholds } }));
969
+ }
970
+ function isContextWindowModeId(id) {
971
+ return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
972
+ }
973
+
823
974
  // src/utils/safe-json.ts
824
975
  function safeParse(input, maxBytes = 5e6) {
825
976
  if (input.length > maxBytes) {
@@ -839,6 +990,7 @@ function safeParse(input, maxBytes = 5e6) {
839
990
  var BEHAVIOR_DEFAULTS = {
840
991
  version: 1,
841
992
  context: {
993
+ mode: DEFAULT_CONTEXT_WINDOW_MODE_ID,
842
994
  warnThreshold: 0.6,
843
995
  softThreshold: 0.75,
844
996
  hardThreshold: 0.9,
@@ -1013,6 +1165,10 @@ var DefaultConfigLoader = class {
1013
1165
  if (c.warnThreshold >= c.softThreshold || c.softThreshold >= c.hardThreshold) {
1014
1166
  throw new Error("Config: context thresholds must satisfy warn < soft < hard");
1015
1167
  }
1168
+ if (c.mode !== void 0 && !isContextWindowModeId(c.mode)) {
1169
+ const known = listContextWindowModes().map((m) => m.id).join(", ");
1170
+ throw new Error(`Config: context.mode must be one of: ${known}`);
1171
+ }
1016
1172
  }
1017
1173
  validateIdentity(cfg) {
1018
1174
  if (!cfg.provider) {
@@ -1589,24 +1745,35 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
1589
1745
  function attachTodosCheckpoint(state, filePath, sessionId) {
1590
1746
  let timer = null;
1591
1747
  let pending = null;
1748
+ let writeChain = Promise.resolve();
1749
+ const enqueueWrite = (todos) => {
1750
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos));
1751
+ return writeChain;
1752
+ };
1592
1753
  const flush = () => {
1593
1754
  timer = null;
1594
1755
  if (pending) {
1595
- void saveTodosCheckpoint(filePath, sessionId, pending);
1756
+ const todos = pending;
1596
1757
  pending = null;
1758
+ return enqueueWrite(todos);
1597
1759
  }
1760
+ return writeChain;
1598
1761
  };
1599
1762
  const unsubscribe = state.onChange((change) => {
1600
1763
  if (change.kind !== "todos_replaced") return;
1601
1764
  pending = change.todos;
1602
1765
  if (timer) clearTimeout(timer);
1603
- timer = setTimeout(flush, 150);
1766
+ timer = setTimeout(() => {
1767
+ void flush();
1768
+ }, 150);
1604
1769
  });
1605
- return () => {
1770
+ return async () => {
1606
1771
  unsubscribe();
1607
1772
  if (timer) {
1608
1773
  clearTimeout(timer);
1609
- flush();
1774
+ await flush();
1775
+ } else {
1776
+ await writeChain;
1610
1777
  }
1611
1778
  };
1612
1779
  }