@wrongstack/core 0.2.0 → 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 (57) hide show
  1. package/dist/{agent-bridge-DmBiCipY.d.ts → agent-bridge-C3DUGjSb.d.ts} +1 -1
  2. package/dist/{compactor-DSl2FK7a.d.ts → compactor-DpJBI1YH.d.ts} +8 -2
  3. package/dist/{config-DXrqb41m.d.ts → config-D2qvAxVd.d.ts} +39 -2
  4. package/dist/{context-u0bryklF.d.ts → context-IovtuTf8.d.ts} +2 -0
  5. package/dist/coordination/index.d.ts +11 -11
  6. package/dist/coordination/index.js +307 -245
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +30 -15
  9. package/dist/defaults/index.js +1077 -479
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{events-B6Q03pTu.d.ts → events-BHIQs4o1.d.ts} +34 -1
  12. package/dist/execution/index.d.ts +17 -14
  13. package/dist/execution/index.js +166 -18
  14. package/dist/execution/index.js.map +1 -1
  15. package/dist/extension/index.d.ts +9 -0
  16. package/dist/extension/index.js +241 -0
  17. package/dist/extension/index.js.map +1 -0
  18. package/dist/{plugin-CoYYZKdn.d.ts → index-hWNybrNZ.d.ts} +368 -11
  19. package/dist/index.d.ts +76 -26
  20. package/dist/index.js +1595 -748
  21. package/dist/index.js.map +1 -1
  22. package/dist/infrastructure/index.d.ts +6 -6
  23. package/dist/infrastructure/index.js +191 -20
  24. package/dist/infrastructure/index.js.map +1 -1
  25. package/dist/kernel/index.d.ts +12 -9
  26. package/dist/kernel/index.js +73 -7
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/{mcp-servers-BA1Ofmfj.d.ts → mcp-servers-C2OopXOn.d.ts} +21 -5
  29. package/dist/models/index.d.ts +2 -2
  30. package/dist/models/index.js +24 -1
  31. package/dist/models/index.js.map +1 -1
  32. package/dist/{multi-agent-BDfkxL5C.d.ts → multi-agent-B9a6sflH.d.ts} +2 -2
  33. package/dist/observability/index.d.ts +2 -2
  34. package/dist/{path-resolver-Crkt8wTQ.d.ts → path-resolver--59rCou3.d.ts} +2 -2
  35. package/dist/provider-runner-B39miKRw.d.ts +36 -0
  36. package/dist/sdd/index.d.ts +3 -3
  37. package/dist/{secret-scrubber-3TLUkiCV.d.ts → secret-scrubber-CgG2tV2B.d.ts} +1 -1
  38. package/dist/{secret-scrubber-CwYliRWd.d.ts → secret-scrubber-Cuy5afaQ.d.ts} +1 -1
  39. package/dist/security/index.d.ts +3 -3
  40. package/dist/security/index.js +24 -1
  41. package/dist/security/index.js.map +1 -1
  42. package/dist/{selector-BRqzvugb.d.ts → selector-wT2fv9Fg.d.ts} +1 -1
  43. package/dist/{session-reader-C3x96CDR.d.ts → session-reader-CcPi4BQ8.d.ts} +1 -1
  44. package/dist/{skill-Bx8jxznf.d.ts → skill-C_7znCIC.d.ts} +2 -2
  45. package/dist/storage/index.d.ts +7 -6
  46. package/dist/storage/index.js +204 -14
  47. package/dist/storage/index.js.map +1 -1
  48. package/dist/{renderer-0A2ZEtca.d.ts → system-prompt-Dk1qm8ey.d.ts} +30 -2
  49. package/dist/{tool-executor-CYdZdtno.d.ts → tool-executor-HsBLGRaA.d.ts} +5 -5
  50. package/dist/types/index.d.ts +16 -16
  51. package/dist/types/index.js +230 -10
  52. package/dist/types/index.js.map +1 -1
  53. package/dist/utils/index.d.ts +23 -2
  54. package/dist/utils/index.js +117 -2
  55. package/dist/utils/index.js.map +1 -1
  56. package/package.json +5 -1
  57. package/dist/system-prompt-CG9jU5-5.d.ts +0 -31
@@ -33,7 +33,7 @@ async function atomicWrite(targetPath, content, opts = {}) {
33
33
  if (mode !== void 0) {
34
34
  await fsp4.chmod(tmp, mode);
35
35
  }
36
- await fsp4.rename(tmp, targetPath);
36
+ await renameWithRetry(tmp, targetPath);
37
37
  } catch (err) {
38
38
  try {
39
39
  await fsp4.unlink(tmp);
@@ -45,6 +45,29 @@ async function atomicWrite(targetPath, content, opts = {}) {
45
45
  async function ensureDir(dir) {
46
46
  await fsp4.mkdir(dir, { recursive: true });
47
47
  }
48
+ var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
49
+ async function renameWithRetry(from, to) {
50
+ if (process.platform !== "win32") {
51
+ await fsp4.rename(from, to);
52
+ return;
53
+ }
54
+ const delays = [10, 25, 60, 120, 250];
55
+ let lastErr;
56
+ for (let i = 0; i <= delays.length; i++) {
57
+ try {
58
+ await fsp4.rename(from, to);
59
+ return;
60
+ } catch (err) {
61
+ lastErr = err;
62
+ const code = err?.code;
63
+ if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
64
+ throw err;
65
+ }
66
+ await new Promise((resolve) => setTimeout(resolve, delays[i]));
67
+ }
68
+ }
69
+ throw lastErr;
70
+ }
48
71
 
49
72
  // src/storage/director-state.ts
50
73
  var DirectorStateCheckpoint = class {
@@ -267,6 +290,7 @@ var InMemoryAgentBridge = class {
267
290
  this.stopped = true;
268
291
  for (const [, p] of this.pendingRequests) {
269
292
  clearTimeout(p.timer);
293
+ p.reject(new Error("Bridge stopped"));
270
294
  }
271
295
  this.pendingRequests.clear();
272
296
  this.inflightGuards.clear();
@@ -1178,6 +1202,179 @@ function providerErrorToSubagentError(err, message, cause) {
1178
1202
  }
1179
1203
  return { kind: "unknown", message, retryable: err.retryable, cause };
1180
1204
  }
1205
+ function makeSpawnTool(director, roster) {
1206
+ const inputSchema = {
1207
+ type: "object",
1208
+ properties: {
1209
+ role: { type: "string", description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields." },
1210
+ name: { type: "string", description: "Display name for the subagent. Required when not using roster." },
1211
+ provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
1212
+ model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
1213
+ systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
1214
+ maxIterations: { type: "number" },
1215
+ maxToolCalls: { type: "number" },
1216
+ maxCostUsd: { type: "number" }
1217
+ },
1218
+ required: []
1219
+ };
1220
+ return {
1221
+ name: "spawn_subagent",
1222
+ description: "Create a new subagent under this director. Returns the subagent id.",
1223
+ usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
1224
+ permission: "auto",
1225
+ mutating: false,
1226
+ inputSchema,
1227
+ async execute(input) {
1228
+ const i = input ?? {};
1229
+ const role = typeof i.role === "string" ? i.role : void 0;
1230
+ const base = role && roster ? roster[role] : void 0;
1231
+ if (role && !base) {
1232
+ return { error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}` };
1233
+ }
1234
+ const cfg = { ...base ?? { name: i.name ?? "subagent" } };
1235
+ if (typeof i.name === "string") cfg.name = i.name;
1236
+ if (typeof i.provider === "string") cfg.provider = i.provider;
1237
+ if (typeof i.model === "string") cfg.model = i.model;
1238
+ if (typeof i.systemPromptOverride === "string") cfg.systemPromptOverride = i.systemPromptOverride;
1239
+ if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
1240
+ if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
1241
+ if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
1242
+ try {
1243
+ const subagentId = await director.spawn(cfg);
1244
+ return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
1245
+ } catch (err) {
1246
+ if (err instanceof DirectorBudgetError) {
1247
+ return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
1248
+ }
1249
+ return { error: err instanceof Error ? err.message : String(err) };
1250
+ }
1251
+ }
1252
+ };
1253
+ }
1254
+ function makeAssignTool(director) {
1255
+ const inputSchema = {
1256
+ type: "object",
1257
+ properties: {
1258
+ subagentId: { type: "string", description: "Target subagent id. Required." },
1259
+ description: { type: "string", description: "The task in natural language \u2014 what you want this subagent to do." },
1260
+ maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
1261
+ timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
1262
+ },
1263
+ required: ["subagentId", "description"]
1264
+ };
1265
+ return {
1266
+ name: "assign_task",
1267
+ description: "Hand a task to a previously spawned subagent. Returns the task id.",
1268
+ permission: "auto",
1269
+ mutating: false,
1270
+ inputSchema,
1271
+ async execute(input) {
1272
+ const i = input;
1273
+ const task = { id: randomUUID(), description: i.description, subagentId: i.subagentId, maxToolCalls: i.maxToolCalls, timeoutMs: i.timeoutMs };
1274
+ const taskId = await director.assign(task);
1275
+ return { taskId, subagentId: i.subagentId };
1276
+ }
1277
+ };
1278
+ }
1279
+ function makeAwaitTasksTool(director) {
1280
+ return {
1281
+ name: "await_tasks",
1282
+ description: "Block until every named task completes. Returns the array of TaskResult.",
1283
+ permission: "auto",
1284
+ mutating: false,
1285
+ inputSchema: { type: "object", properties: { taskIds: { type: "array", items: { type: "string" }, description: "One or more task ids returned by `assign_task`." } }, required: ["taskIds"] },
1286
+ async execute(input) {
1287
+ const i = input;
1288
+ const results = await director.awaitTasks(i.taskIds);
1289
+ return { results };
1290
+ }
1291
+ };
1292
+ }
1293
+ function makeAskTool(director) {
1294
+ return {
1295
+ name: "ask_subagent",
1296
+ description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge.",
1297
+ permission: "auto",
1298
+ mutating: false,
1299
+ inputSchema: {
1300
+ type: "object",
1301
+ properties: {
1302
+ subagentId: { type: "string", description: "Subagent to ask. Must be a previously spawned id." },
1303
+ question: { type: "string", description: "The question or instruction." },
1304
+ timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
1305
+ },
1306
+ required: ["subagentId", "question"]
1307
+ },
1308
+ async execute(input) {
1309
+ const i = input;
1310
+ try {
1311
+ const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
1312
+ return { ok: true, answer };
1313
+ } catch (err) {
1314
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
1315
+ }
1316
+ }
1317
+ };
1318
+ }
1319
+ function makeRollUpTool(director) {
1320
+ return {
1321
+ name: "roll_up",
1322
+ description: "Aggregate completed task results into a single formatted summary.",
1323
+ permission: "auto",
1324
+ mutating: false,
1325
+ inputSchema: {
1326
+ type: "object",
1327
+ properties: {
1328
+ taskIds: { type: "array", items: { type: "string" }, description: "Completed task ids to aggregate." },
1329
+ style: { type: "string", enum: ["markdown", "json"], description: "Output flavor \u2014 markdown (default) or json." }
1330
+ },
1331
+ required: ["taskIds"]
1332
+ },
1333
+ async execute(input) {
1334
+ const i = input;
1335
+ const summary = director.rollUp(i.taskIds, i.style ?? "markdown");
1336
+ return { summary, count: i.taskIds.length };
1337
+ }
1338
+ };
1339
+ }
1340
+ function makeTerminateTool(director) {
1341
+ return {
1342
+ name: "terminate_subagent",
1343
+ description: "Forcibly abort a subagent.",
1344
+ permission: "auto",
1345
+ mutating: true,
1346
+ inputSchema: { type: "object", properties: { subagentId: { type: "string", description: "Subagent to abort." } }, required: ["subagentId"] },
1347
+ async execute(input) {
1348
+ const i = input;
1349
+ await director.terminate(i.subagentId);
1350
+ return { ok: true };
1351
+ }
1352
+ };
1353
+ }
1354
+ function makeFleetStatusTool(director) {
1355
+ return {
1356
+ name: "fleet_status",
1357
+ description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts.",
1358
+ permission: "auto",
1359
+ mutating: false,
1360
+ inputSchema: { type: "object", properties: {}, required: [] },
1361
+ async execute() {
1362
+ return director.status();
1363
+ }
1364
+ };
1365
+ }
1366
+ function makeFleetUsageTool(director) {
1367
+ return {
1368
+ name: "fleet_usage",
1369
+ description: "Token + cost breakdown across the fleet, per-subagent and totals.",
1370
+ permission: "auto",
1371
+ mutating: false,
1372
+ inputSchema: { type: "object", properties: {}, required: [] },
1373
+ async execute() {
1374
+ return director.snapshot();
1375
+ }
1376
+ };
1377
+ }
1181
1378
 
1182
1379
  // src/coordination/director.ts
1183
1380
  var DirectorBudgetError = class extends Error {
@@ -1736,242 +1933,6 @@ var Director = class {
1736
1933
  return t;
1737
1934
  }
1738
1935
  };
1739
- function makeSpawnTool(director, roster) {
1740
- const inputSchema = {
1741
- type: "object",
1742
- properties: {
1743
- role: {
1744
- type: "string",
1745
- description: "Roster role id (preferred). When set, the spawn uses the matching config from the roster and ignores other fields."
1746
- },
1747
- name: {
1748
- type: "string",
1749
- description: "Display name for the subagent. Required when not using roster."
1750
- },
1751
- provider: {
1752
- type: "string",
1753
- description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.'
1754
- },
1755
- model: {
1756
- type: "string",
1757
- description: "Model id within the provider. Defaults to the leader model when omitted."
1758
- },
1759
- systemPromptOverride: {
1760
- type: "string",
1761
- description: "Extra prompt text appended after the role-base prompt."
1762
- },
1763
- maxIterations: { type: "number" },
1764
- maxToolCalls: { type: "number" },
1765
- maxCostUsd: { type: "number" }
1766
- },
1767
- required: []
1768
- };
1769
- return {
1770
- name: "spawn_subagent",
1771
- description: "Create a new subagent under this director. Returns the subagent id. Use this when you need a worker with a specific provider, model, or role to handle a piece of the plan.",
1772
- usageHint: "Either pass `role` (matches the roster) OR pass `name` + optional `provider`/`model`. Returns `{ subagentId }`.",
1773
- permission: "auto",
1774
- mutating: false,
1775
- inputSchema,
1776
- async execute(input) {
1777
- const i = input ?? {};
1778
- const role = typeof i.role === "string" ? i.role : void 0;
1779
- const base = role && roster ? roster[role] : void 0;
1780
- if (role && !base) {
1781
- return {
1782
- error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}`
1783
- };
1784
- }
1785
- const cfg = {
1786
- ...base ?? { name: i.name ?? "subagent" }
1787
- };
1788
- if (typeof i.name === "string") cfg.name = i.name;
1789
- if (typeof i.provider === "string") cfg.provider = i.provider;
1790
- if (typeof i.model === "string") cfg.model = i.model;
1791
- if (typeof i.systemPromptOverride === "string")
1792
- cfg.systemPromptOverride = i.systemPromptOverride;
1793
- if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
1794
- if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
1795
- if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
1796
- try {
1797
- const subagentId = await director.spawn(cfg);
1798
- return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
1799
- } catch (err) {
1800
- if (err instanceof DirectorBudgetError) {
1801
- return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
1802
- }
1803
- return { error: err instanceof Error ? err.message : String(err) };
1804
- }
1805
- }
1806
- };
1807
- }
1808
- function makeAssignTool(director) {
1809
- const inputSchema = {
1810
- type: "object",
1811
- properties: {
1812
- subagentId: { type: "string", description: "Target subagent id. Required." },
1813
- description: {
1814
- type: "string",
1815
- description: "The task in natural language \u2014 what you want this subagent to do."
1816
- },
1817
- maxToolCalls: { type: "number", description: "Optional per-task tool-call budget override." },
1818
- timeoutMs: { type: "number", description: "Optional per-task timeout in ms." }
1819
- },
1820
- required: ["subagentId", "description"]
1821
- };
1822
- return {
1823
- name: "assign_task",
1824
- description: "Hand a task to a previously spawned subagent. Returns the task id \u2014 pass it to `await_tasks` to block on completion.",
1825
- permission: "auto",
1826
- mutating: false,
1827
- inputSchema,
1828
- async execute(input) {
1829
- const i = input;
1830
- const task = {
1831
- id: randomUUID(),
1832
- description: i.description,
1833
- subagentId: i.subagentId,
1834
- maxToolCalls: i.maxToolCalls,
1835
- timeoutMs: i.timeoutMs
1836
- };
1837
- const taskId = await director.assign(task);
1838
- return { taskId, subagentId: i.subagentId };
1839
- }
1840
- };
1841
- }
1842
- function makeAwaitTasksTool(director) {
1843
- const inputSchema = {
1844
- type: "object",
1845
- properties: {
1846
- taskIds: {
1847
- type: "array",
1848
- items: { type: "string" },
1849
- description: "One or more task ids returned by `assign_task`. The call blocks until every id resolves."
1850
- }
1851
- },
1852
- required: ["taskIds"]
1853
- };
1854
- return {
1855
- name: "await_tasks",
1856
- description: "Block until every named task completes. Returns the array of TaskResult \u2014 use this to gather subagent output before deciding the next step.",
1857
- permission: "auto",
1858
- mutating: false,
1859
- inputSchema,
1860
- async execute(input) {
1861
- const i = input;
1862
- const results = await director.awaitTasks(i.taskIds);
1863
- return { results };
1864
- }
1865
- };
1866
- }
1867
- function makeAskTool(director) {
1868
- const inputSchema = {
1869
- type: "object",
1870
- properties: {
1871
- subagentId: {
1872
- type: "string",
1873
- description: "Subagent to ask. Must be a previously spawned id."
1874
- },
1875
- question: {
1876
- type: "string",
1877
- description: "The question or instruction. Sent as the bridge message payload."
1878
- },
1879
- timeoutMs: { type: "number", description: "Optional timeout in ms (default 30s)." }
1880
- },
1881
- required: ["subagentId", "question"]
1882
- };
1883
- return {
1884
- name: "ask_subagent",
1885
- description: "Synchronously ask a subagent a question. Blocks until the subagent replies via the bridge (or the timeout fires). Use this when you need a one-shot answer without spawning a fresh task.",
1886
- permission: "auto",
1887
- mutating: false,
1888
- inputSchema,
1889
- async execute(input) {
1890
- const i = input;
1891
- try {
1892
- const answer = await director.ask(i.subagentId, { question: i.question }, i.timeoutMs);
1893
- return { ok: true, answer };
1894
- } catch (err) {
1895
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
1896
- }
1897
- }
1898
- };
1899
- }
1900
- function makeRollUpTool(director) {
1901
- const inputSchema = {
1902
- type: "object",
1903
- properties: {
1904
- taskIds: {
1905
- type: "array",
1906
- items: { type: "string" },
1907
- description: "Completed task ids to aggregate. Pass the ids returned by previous `assign_task` calls."
1908
- },
1909
- style: {
1910
- type: "string",
1911
- enum: ["markdown", "json"],
1912
- description: "Output flavor \u2014 markdown (default) for in-prompt summarization, json for structured downstream processing."
1913
- }
1914
- },
1915
- required: ["taskIds"]
1916
- };
1917
- return {
1918
- name: "roll_up",
1919
- description: "Aggregate completed task results into a single formatted summary. Use this after `await_tasks` to fold subagent outputs back into the director's context before deciding the next step.",
1920
- permission: "auto",
1921
- mutating: false,
1922
- inputSchema,
1923
- async execute(input) {
1924
- const i = input;
1925
- const summary = director.rollUp(i.taskIds, i.style ?? "markdown");
1926
- return { summary, count: i.taskIds.length };
1927
- }
1928
- };
1929
- }
1930
- function makeTerminateTool(director) {
1931
- const inputSchema = {
1932
- type: "object",
1933
- properties: {
1934
- subagentId: { type: "string", description: "Subagent to abort." }
1935
- },
1936
- required: ["subagentId"]
1937
- };
1938
- return {
1939
- name: "terminate_subagent",
1940
- description: 'Forcibly abort a subagent. Use sparingly \u2014 prefer waiting on the natural budget to expire. The current task (if any) ends with status "stopped".',
1941
- permission: "auto",
1942
- mutating: true,
1943
- inputSchema,
1944
- async execute(input) {
1945
- const i = input;
1946
- await director.terminate(i.subagentId);
1947
- return { ok: true };
1948
- }
1949
- };
1950
- }
1951
- function makeFleetStatusTool(director) {
1952
- return {
1953
- name: "fleet_status",
1954
- description: "Snapshot of the fleet \u2014 every subagent's current status, pending vs. completed task counts, and the running total iteration count. Cheap; call freely.",
1955
- permission: "auto",
1956
- mutating: false,
1957
- inputSchema: { type: "object", properties: {}, required: [] },
1958
- async execute() {
1959
- return director.status();
1960
- }
1961
- };
1962
- }
1963
- function makeFleetUsageTool(director) {
1964
- return {
1965
- name: "fleet_usage",
1966
- description: "Token + cost breakdown across the fleet, per-subagent and totals. Use this to reason about which workers to assign costly tasks to or when to wrap up to stay within budget.",
1967
- permission: "auto",
1968
- mutating: false,
1969
- inputSchema: { type: "object", properties: {}, required: [] },
1970
- async execute() {
1971
- return director.snapshot();
1972
- }
1973
- };
1974
- }
1975
1936
  function createDelegateTool(opts) {
1976
1937
  const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
1977
1938
  const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
@@ -2334,6 +2295,100 @@ function makeAgentSubagentRunner(opts) {
2334
2295
  function defaultFormatTaskInput(task) {
2335
2296
  return task.description ?? "";
2336
2297
  }
2298
+
2299
+ // src/utils/message-invariants.ts
2300
+ function repairToolUseAdjacency(messages) {
2301
+ const removedToolUses = [];
2302
+ const removedToolResults = [];
2303
+ let removedMessages = 0;
2304
+ let changed = false;
2305
+ const out = [];
2306
+ for (let i = 0; i < messages.length; i++) {
2307
+ const original = messages[i];
2308
+ let msg = original;
2309
+ if (hasToolUse(msg)) {
2310
+ const nextIds = toolResultIds(messages[i + 1]);
2311
+ const filtered = mapContent(msg, (blocks) => {
2312
+ const next = [];
2313
+ for (const block of blocks) {
2314
+ if (block.type === "tool_use" && !nextIds.has(block.id)) {
2315
+ removedToolUses.push(block.id);
2316
+ changed = true;
2317
+ continue;
2318
+ }
2319
+ next.push(block);
2320
+ }
2321
+ return next;
2322
+ });
2323
+ msg = filtered ?? msg;
2324
+ }
2325
+ if (hasToolResult(msg)) {
2326
+ const allowed = toolUseIds(out[out.length - 1]);
2327
+ const filtered = mapContent(msg, (blocks) => {
2328
+ const next = [];
2329
+ for (const block of blocks) {
2330
+ if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
2331
+ removedToolResults.push(block.tool_use_id);
2332
+ changed = true;
2333
+ continue;
2334
+ }
2335
+ next.push(block);
2336
+ }
2337
+ return next;
2338
+ });
2339
+ msg = filtered ?? msg;
2340
+ }
2341
+ if (isEmptyMessage(msg)) {
2342
+ removedMessages++;
2343
+ changed = true;
2344
+ continue;
2345
+ }
2346
+ out.push(msg);
2347
+ }
2348
+ return {
2349
+ messages: changed ? out : messages,
2350
+ report: { changed, removedToolUses, removedToolResults, removedMessages }
2351
+ };
2352
+ }
2353
+ function hasToolUse(msg) {
2354
+ return contentBlocks(msg).some((b) => b.type === "tool_use");
2355
+ }
2356
+ function hasToolResult(msg) {
2357
+ return contentBlocks(msg).some((b) => b.type === "tool_result");
2358
+ }
2359
+ function toolUseIds(msg) {
2360
+ const ids = /* @__PURE__ */ new Set();
2361
+ if (!msg || msg.role !== "assistant") return ids;
2362
+ for (const block of contentBlocks(msg)) {
2363
+ if (block.type === "tool_use") ids.add(block.id);
2364
+ }
2365
+ return ids;
2366
+ }
2367
+ function toolResultIds(msg) {
2368
+ const ids = /* @__PURE__ */ new Set();
2369
+ if (!msg || msg.role !== "user") return ids;
2370
+ for (const block of contentBlocks(msg)) {
2371
+ if (block.type === "tool_result") ids.add(block.tool_use_id);
2372
+ }
2373
+ return ids;
2374
+ }
2375
+ function contentBlocks(msg) {
2376
+ return msg && Array.isArray(msg.content) ? msg.content : [];
2377
+ }
2378
+ function mapContent(msg, fn) {
2379
+ if (!Array.isArray(msg.content)) return msg;
2380
+ const next = fn(msg.content);
2381
+ if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
2382
+ return msg;
2383
+ }
2384
+ return { ...msg, content: next };
2385
+ }
2386
+ function isEmptyMessage(msg) {
2387
+ if (typeof msg.content === "string") return msg.content.trim().length === 0;
2388
+ return msg.content.length === 0;
2389
+ }
2390
+
2391
+ // src/storage/session-store.ts
2337
2392
  var DefaultSessionStore = class {
2338
2393
  dir;
2339
2394
  events;
@@ -2535,11 +2590,17 @@ var DefaultSessionStore = class {
2535
2590
  if (openToolUses.size > 0) {
2536
2591
  this.events?.emit("session.damaged", {
2537
2592
  sessionId,
2538
- detail: `${openToolUses.size} tool_use blocks without matching results \u2014 replay truncated`
2593
+ detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
2539
2594
  });
2540
- return { messages, usage };
2541
2595
  }
2542
- return { messages, usage };
2596
+ const repaired = repairToolUseAdjacency(messages);
2597
+ if (repaired.report.changed) {
2598
+ this.events?.emit("session.damaged", {
2599
+ sessionId,
2600
+ detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
2601
+ });
2602
+ }
2603
+ return { messages: repaired.messages, usage };
2543
2604
  }
2544
2605
  };
2545
2606
  var FileSessionWriter = class {
@@ -2565,6 +2626,7 @@ var FileSessionWriter = class {
2565
2626
  startedAt;
2566
2627
  meta;
2567
2628
  closed = false;
2629
+ closing = false;
2568
2630
  manifestFile;
2569
2631
  summary;
2570
2632
  tokenIn = 0;
@@ -2580,9 +2642,7 @@ var FileSessionWriter = class {
2580
2642
  resumed;
2581
2643
  appendFailCount = 0;
2582
2644
  lastAppendWarnAt = 0;
2583
- async writeSessionStart() {
2584
- if (this.initDone || this.closed) return;
2585
- this.initDone = true;
2645
+ async writeSessionStartLazy() {
2586
2646
  const record = `${JSON.stringify({
2587
2647
  type: this.resumed ? "session_resumed" : "session_start",
2588
2648
  ts: this.startedAt,
@@ -2601,7 +2661,8 @@ var FileSessionWriter = class {
2601
2661
  async append(event) {
2602
2662
  if (this.closed) return;
2603
2663
  if (!this.initDone) {
2604
- await this.writeSessionStart();
2664
+ this.initDone = true;
2665
+ await this.writeSessionStartLazy();
2605
2666
  }
2606
2667
  this.observeForSummary(event);
2607
2668
  try {
@@ -2642,7 +2703,8 @@ var FileSessionWriter = class {
2642
2703
  }
2643
2704
  }
2644
2705
  async close() {
2645
- if (this.closed) return;
2706
+ if (this.closing) return;
2707
+ this.closing = true;
2646
2708
  this.closed = true;
2647
2709
  if (this.manifestFile) {
2648
2710
  try {