zidane 6.2.24 → 6.2.27

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 (114) hide show
  1. package/dist/{acp-CTn1kFvP.js → acp-R4-zVIud.js} +4 -4
  2. package/dist/{acp-CTn1kFvP.js.map → acp-R4-zVIud.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-tLfGSSQw.js → agent-DIe8yzcb.js} +343 -30
  7. package/dist/agent-DIe8yzcb.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +2 -2
  10. package/dist/{anthropic-uKvj1yle.js → anthropic-BEYY0hVK.js} +66 -10
  11. package/dist/anthropic-BEYY0hVK.js.map +1 -0
  12. package/dist/{auth-CpdQebUy.js → auth-C4YPRDN4.js} +3 -3
  13. package/dist/{auth-CpdQebUy.js.map → auth-C4YPRDN4.js.map} +1 -1
  14. package/dist/chat/pure.d.ts +3 -3
  15. package/dist/chat/pure.js +1 -1
  16. package/dist/chat.d.ts +4 -4
  17. package/dist/chat.d.ts.map +1 -1
  18. package/dist/chat.js +5 -5
  19. package/dist/contexts/daytona.d.ts +1 -1
  20. package/dist/contexts/daytona.d.ts.map +1 -1
  21. package/dist/contexts/daytona.js +9 -2
  22. package/dist/contexts/daytona.js.map +1 -1
  23. package/dist/contexts/docker.js +1 -1
  24. package/dist/contexts/e2b.d.ts +1 -1
  25. package/dist/contexts/e2b.d.ts.map +1 -1
  26. package/dist/contexts/e2b.js +3 -4
  27. package/dist/contexts/e2b.js.map +1 -1
  28. package/dist/{contexts-A4ep-iJs.js → contexts-0rmrBhTK.js} +8 -5
  29. package/dist/contexts-0rmrBhTK.js.map +1 -0
  30. package/dist/contexts.js +1 -1
  31. package/dist/eval.d.ts +1 -1
  32. package/dist/eval.js +2 -2
  33. package/dist/extensions.d.ts +1 -1
  34. package/dist/extensions.js +1 -1
  35. package/dist/{output-cap-kBUHBlnq.js → glob-DluQFSw1.js} +2 -50
  36. package/dist/glob-DluQFSw1.js.map +1 -0
  37. package/dist/headless.d.ts +1 -1
  38. package/dist/headless.js +5 -5
  39. package/dist/history-search-worker.js +6 -1
  40. package/dist/history-search-worker.js.map +1 -1
  41. package/dist/{index-8zm_hwdY.d.ts → index-B6eBI_OD.d.ts} +312 -96
  42. package/dist/index-B6eBI_OD.d.ts.map +1 -0
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +12 -9
  45. package/dist/index.js.map +1 -1
  46. package/dist/{inline-autocomplete-BAwBcZtD.js → inline-autocomplete-CbcdZpfC.js} +2 -1
  47. package/dist/inline-autocomplete-CbcdZpfC.js.map +1 -0
  48. package/dist/{login-DfJGuHUj.js → login-afiadwf0.js} +34 -18
  49. package/dist/login-afiadwf0.js.map +1 -0
  50. package/dist/mcp.d.ts +1 -1
  51. package/dist/{messages-CvHfYB7f.js → messages-Dy9t7DbQ.js} +97 -6
  52. package/dist/messages-Dy9t7DbQ.js.map +1 -0
  53. package/dist/output/stream-json.d.ts +1 -1
  54. package/dist/output/terminal.d.ts +1 -1
  55. package/dist/output-cap-DM1HrgIp.js +100 -0
  56. package/dist/output-cap-DM1HrgIp.js.map +1 -0
  57. package/dist/presets.d.ts +1 -1
  58. package/dist/presets.js +1 -1
  59. package/dist/providers/anthropic.d.ts +1 -1
  60. package/dist/providers/anthropic.js +1 -1
  61. package/dist/providers/arcee.d.ts +1 -1
  62. package/dist/providers/baseten.d.ts +1 -1
  63. package/dist/providers/cerebras.d.ts +1 -1
  64. package/dist/providers/local.d.ts +1 -1
  65. package/dist/providers/openai-compat.d.ts +1 -1
  66. package/dist/providers/openai.d.ts +1 -1
  67. package/dist/providers/openai.js +4 -3
  68. package/dist/providers/openai.js.map +1 -1
  69. package/dist/providers/openrouter.d.ts +1 -1
  70. package/dist/providers/xai.d.ts +1 -1
  71. package/dist/providers.d.ts +1 -1
  72. package/dist/providers.js +1 -1
  73. package/dist/restate.d.ts +1 -1
  74. package/dist/{run-summary-nD3IQ4ZQ.js → run-summary-BU_NgwUT.js} +17 -1
  75. package/dist/run-summary-BU_NgwUT.js.map +1 -0
  76. package/dist/session/sqlite.d.ts +1 -1
  77. package/dist/{session-jLsc_kMH.js → session-DkVnYGm9.js} +2 -2
  78. package/dist/{session-jLsc_kMH.js.map → session-DkVnYGm9.js.map} +1 -1
  79. package/dist/session.d.ts +1 -1
  80. package/dist/session.js +2 -2
  81. package/dist/skills.d.ts +1 -1
  82. package/dist/{tool-formatters-CZJYzP4x.d.ts → tool-formatters-DdchId_3.d.ts} +2 -2
  83. package/dist/{tool-formatters-CZJYzP4x.d.ts.map → tool-formatters-DdchId_3.d.ts.map} +1 -1
  84. package/dist/tools/fetch-url.d.ts +1 -1
  85. package/dist/tools/web-search.d.ts +1 -1
  86. package/dist/tools.d.ts +1 -1
  87. package/dist/tools.js +1 -1
  88. package/dist/{transcript-anchors-Ded7iJjG.js → transcript-anchors-DOuzgpJx.js} +88 -12
  89. package/dist/transcript-anchors-DOuzgpJx.js.map +1 -0
  90. package/dist/{transcript-anchors-CJ2dmpQk.d.ts → transcript-anchors-DvXLpbIc.d.ts} +2 -2
  91. package/dist/transcript-anchors-DvXLpbIc.d.ts.map +1 -0
  92. package/dist/tui.d.ts +44 -7
  93. package/dist/tui.d.ts.map +1 -1
  94. package/dist/tui.js +1387 -1106
  95. package/dist/tui.js.map +1 -1
  96. package/dist/{turn-operations-ClGa1vnb.js → turn-operations-4tnvKwfJ.js} +77 -13
  97. package/dist/turn-operations-4tnvKwfJ.js.map +1 -0
  98. package/dist/{turn-operations-DZYpXcU1.d.ts → turn-operations-HRTRJIcy.d.ts} +3 -3
  99. package/dist/{turn-operations-DZYpXcU1.d.ts.map → turn-operations-HRTRJIcy.d.ts.map} +1 -1
  100. package/dist/types-BIarq1qE.js.map +1 -1
  101. package/dist/types.d.ts +1 -1
  102. package/package.json +2 -1
  103. package/dist/agent-tLfGSSQw.js.map +0 -1
  104. package/dist/anthropic-uKvj1yle.js.map +0 -1
  105. package/dist/contexts-A4ep-iJs.js.map +0 -1
  106. package/dist/index-8zm_hwdY.d.ts.map +0 -1
  107. package/dist/inline-autocomplete-BAwBcZtD.js.map +0 -1
  108. package/dist/login-DfJGuHUj.js.map +0 -1
  109. package/dist/messages-CvHfYB7f.js.map +0 -1
  110. package/dist/output-cap-kBUHBlnq.js.map +0 -1
  111. package/dist/run-summary-nD3IQ4ZQ.js.map +0 -1
  112. package/dist/transcript-anchors-CJ2dmpQk.d.ts.map +0 -1
  113. package/dist/transcript-anchors-Ded7iJjG.js.map +0 -1
  114. package/dist/turn-operations-ClGa1vnb.js.map +0 -1
@@ -5,9 +5,10 @@ import { a as hydrateReadStateFromSessionAsync, c as resolveReadStateMap, n as g
5
5
  import { n as estimateTokens, r as utf8ByteLength } from "./utils-ngQzYzZD.js";
6
6
  import { t as buildContextBreakdown } from "./context-breakdown-Dzo25N45.js";
7
7
  import { a as formatDuration, c as previewLine, o as formatTaskStatus, s as formatTaskSummary } from "./format-BF745pLL.js";
8
- import { c as parseGitLsFiles, r as isOutputExceededMessage, s as GIT_LS_FILES_COMMAND } from "./output-cap-kBUHBlnq.js";
8
+ import { i as parseGitLsFiles, r as GIT_LS_FILES_COMMAND } from "./glob-DluQFSw1.js";
9
9
  import { n as alwaysQuote, r as shellQuote } from "./exec-file-size-PXkr-MGA.js";
10
- import { r as createProcessContext, t as resolveDetachedTasksCapability } from "./contexts-A4ep-iJs.js";
10
+ import { i as isOutputExceededMessage } from "./output-cap-DM1HrgIp.js";
11
+ import { r as createProcessContext, t as resolveDetachedTasksCapability } from "./contexts-0rmrBhTK.js";
11
12
  import { i as AgentProviderError, l as errorMessage, m as toTypedError, n as AgentBudgetExceededError, o as AgentToolPairingError, t as AgentAbortedError, u as isAbortLikeError } from "./errors-DJUxZg9b.js";
12
13
  import { C as stripShellInterpolations, O as validateResourcePathReal, S as interpolateShellCommands, _ as lastLine, b as resolveSubdirRoot, d as installAllowedToolsGate, f as createSkillActivationState, g as fetchInstallSource, h as defaultWhich, j as projectUserPaths, l as buildCatalog, m as defaultExec, p as InstallSourceError, t as resolveSkills, v as parseInstallSource, x as escapeXml, y as resolveNpmVersion } from "./resolve-BPTA1RpX.js";
13
14
  import { n as writeFileAtomicAsync, t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
@@ -16,8 +17,8 @@ import { n as reconcileImageMediaType } from "./media-sniff-Bn76JxAu.js";
16
17
  import { c as wrapDiscoveredMcpTools, i as nonOwningMcpView, n as connectMcpServers, r as deriveMcpToolMeta } from "./mcp-H6bqyM94.js";
17
18
  import { C as canonicalizePrompt, S as buildPromptMessage } from "./openai-compat-pHI0kBWH.js";
18
19
  import { appendStaticSection, renderSystemForWire } from "./prompt.js";
19
- import { a as detectTurnInterruption, c as filterUnresolvedToolUses, d as remintDuplicateToolCallIds, f as resolveContentRefsInMessages, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureEndsWithUserMessage, s as ensureToolResultPairing } from "./messages-CvHfYB7f.js";
20
- import { a as getExtensionSessionState, c as setExtensionSessionState, l as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as resumeChainRunIds } from "./session-jLsc_kMH.js";
20
+ import { a as detectTurnInterruption, c as filterUnresolvedToolUses, f as remintDuplicateToolCallIds, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureEndsWithUserMessage, p as resolveContentRefsInMessages, s as ensureToolResultPairing } from "./messages-Dy9t7DbQ.js";
21
+ import { a as getExtensionSessionState, c as setExtensionSessionState, l as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as resumeChainRunIds } from "./session-DkVnYGm9.js";
21
22
  import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
22
23
  import { a as withInactivityTimeout, i as settleWithinTimeout, n as linkAbortSignal, o as withTimeout$1, r as resolveTimeoutMs, t as OperationTimeoutError } from "./timeout-BDetbuN6.js";
23
24
  import { createRequire } from "node:module";
@@ -25,7 +26,7 @@ import { basename, delimiter, dirname, isAbsolute, join, resolve, sep } from "no
25
26
  import { createHooks } from "hookable";
26
27
  import { Buffer } from "node:buffer";
27
28
  import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
28
- import { readdir, rm, stat, unlink } from "node:fs/promises";
29
+ import { copyFile, mkdir, readdir, rm, stat, unlink } from "node:fs/promises";
29
30
  import { pathToFileURL } from "node:url";
30
31
  import { homedir, tmpdir } from "node:os";
31
32
  import { createHash } from "node:crypto";
@@ -215,6 +216,7 @@ const DEFAULT_FAILED_MUTATIONS = 3;
215
216
  const DEFAULT_NOOP_MUTATIONS = 3;
216
217
  const DEFAULT_VALIDATION_REJECTS = 3;
217
218
  const DEFAULT_TOOL_ERRORS = 3;
219
+ const DEFAULT_SINGLE_CALL_TURNS = 3;
218
220
  const DEFAULT_MAX_NUDGES$1 = 3;
219
221
  const DEFAULT_READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
220
222
  "read_file",
@@ -228,6 +230,17 @@ const DEFAULT_READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
228
230
  "skills_read",
229
231
  "todoread"
230
232
  ]);
233
+ /**
234
+ * Discovery chains are inherently sequential — each call's input needs the
235
+ * prior result (`tool_search` → load → call, `skills_read` after `skills_use`)
236
+ * and `todoread` is a singleton by nature. Their single-call turns are not
237
+ * "unbatched", so they reset the streak instead of extending it.
238
+ */
239
+ const SINGLE_CALL_EXEMPT_TOOLS = /* @__PURE__ */ new Set([
240
+ "tool_search",
241
+ "skills_read",
242
+ "todoread"
243
+ ]);
231
244
  function threshold(value, fallback) {
232
245
  if (value === false) return false;
233
246
  return clampPositiveInt(value, fallback);
@@ -239,6 +252,7 @@ function defaultMessage(kind) {
239
252
  case "noopMutations": return "Several mutation calls produced no file changes. Check whether the requested state is already reached; if so, report that instead of issuing more edits.";
240
253
  case "validationRejects": return "Several tool calls had invalid arguments. Re-check the advertised tool schema and emit one well-formed native tool call.";
241
254
  case "toolErrors": return "Several tool calls errored. Pivot from repeating the failing call: inspect the error, adjust the input or approach, then continue with one concrete action.";
255
+ case "singleCallTurns": return "You are issuing one tool call per response. Independent read-only calls (file reads, searches, listings) have no dependencies between them — batch all of them into a single response instead of one per turn. Only sequence calls whose inputs depend on a previous result.";
242
256
  }
243
257
  }
244
258
  function messageFor(config, kind, count) {
@@ -256,10 +270,12 @@ function installProgressNudges(hooks, config, deps) {
256
270
  const noopMutations = threshold(resolvedConfig.noopMutations, DEFAULT_NOOP_MUTATIONS);
257
271
  const validationRejects = threshold(resolvedConfig.validationRejects, DEFAULT_VALIDATION_REJECTS);
258
272
  const toolErrors = threshold(resolvedConfig.toolErrors, DEFAULT_TOOL_ERRORS);
273
+ const singleCallTurns = resolvedConfig.singleCallTurns === void 0 ? false : threshold(resolvedConfig.singleCallTurns, DEFAULT_SINGLE_CALL_TURNS);
259
274
  const readOnlyTools = /* @__PURE__ */ new Set([...resolvedConfig.readOnlyTools ?? DEFAULT_READ_ONLY_TOOLS]);
260
275
  let nudgeCount = 0;
261
276
  const fired = /* @__PURE__ */ new Set();
262
277
  let consecutiveReadOnlyTurns = 0;
278
+ let consecutiveSingleCallTurns = 0;
263
279
  let consecutiveFailedMutations = 0;
264
280
  let consecutiveNoopMutations = 0;
265
281
  let validationRejectCount = 0;
@@ -301,8 +317,17 @@ function installProgressNudges(hooks, config, deps) {
301
317
  }),
302
318
  hooks.hook("turn:after", (ctx) => {
303
319
  const entries = Object.entries(ctx.toolCounts.turn).filter(([, count]) => count > 0);
304
- if (entries.length === 0) return;
305
- if (!entries.every(([tool]) => readOnlyTools.has(tool))) {
320
+ if (entries.length === 0) {
321
+ consecutiveSingleCallTurns = 0;
322
+ return;
323
+ }
324
+ const readOnly = entries.every(([tool]) => readOnlyTools.has(tool));
325
+ const totalCalls = entries.reduce((sum, [, count]) => sum + count, 0);
326
+ if (readOnly && totalCalls === 1 && !SINGLE_CALL_EXEMPT_TOOLS.has(entries[0][0])) {
327
+ consecutiveSingleCallTurns++;
328
+ maybeNudge("singleCallTurns", consecutiveSingleCallTurns, singleCallTurns);
329
+ } else consecutiveSingleCallTurns = 0;
330
+ if (!readOnly) {
306
331
  consecutiveReadOnlyTurns = 0;
307
332
  return;
308
333
  }
@@ -3316,6 +3341,11 @@ function createInteractionTool(options) {
3316
3341
  function entryPath(entry) {
3317
3342
  return typeof entry === "string" ? entry : entry.path;
3318
3343
  }
3344
+ /**
3345
+ * Entry ceiling — matches glob's default limit so a pathological
3346
+ * 100k-entry directory can't flood the context window.
3347
+ */
3348
+ const MAX_ENTRIES = 1e3;
3319
3349
  const listFiles = {
3320
3350
  isConcurrencySafe: true,
3321
3351
  spec: {
@@ -3333,7 +3363,9 @@ const listFiles = {
3333
3363
  async execute({ path }, ctx) {
3334
3364
  const target = path || ".";
3335
3365
  try {
3336
- return (await ctx.execution.listFiles(ctx.handle, target)).map(entryPath).join("\n") || "(empty directory)";
3366
+ const entries = await ctx.execution.listFiles(ctx.handle, target);
3367
+ if (entries.length > MAX_ENTRIES) return `${entries.slice(0, MAX_ENTRIES).map(entryPath).join("\n")}\n…(${entries.length - MAX_ENTRIES} more entries omitted; use glob with a narrower pattern)`;
3368
+ return entries.map(entryPath).join("\n") || "(empty directory)";
3337
3369
  } catch (err) {
3338
3370
  ctx.reportOutcome?.("failed");
3339
3371
  const message = errorMessage(err);
@@ -3964,6 +3996,8 @@ const readFile$1 = {
3964
3996
  if (extMedia) {
3965
3997
  const sizeCap = normalizeReadFileInteger(maxBytes, defaults.attachmentMaxBytes);
3966
3998
  try {
3999
+ const probed = await ctx.execution.fileSize?.(ctx.handle, path) ?? null;
4000
+ if (probed !== null && sizeCap > 0 && probed > sizeCap) return `[image too large to inline: ${path}, ${probed} bytes (cap ${sizeCap}). Raise maxBytes, or use shell to inspect.]`;
3967
4001
  const { base64, byteLength } = await readFileAsBase64(ctx.execution, ctx.handle, path);
3968
4002
  if (sizeCap > 0 && byteLength > sizeCap) return `[image too large to inline: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes, or use shell to inspect.]`;
3969
4003
  const imgMedia = reconcileImageMediaType(extMedia, base64);
@@ -3984,6 +4018,8 @@ const readFile$1 = {
3984
4018
  if (docMedia) {
3985
4019
  const sizeCap = normalizeReadFileInteger(maxBytes, defaults.attachmentMaxBytes);
3986
4020
  try {
4021
+ const probed = await ctx.execution.fileSize?.(ctx.handle, path) ?? null;
4022
+ if (probed !== null && sizeCap > 0 && probed > sizeCap) return `[document too large to attach: ${path}, ${probed} bytes (cap ${sizeCap}). Raise maxBytes, or use shell/read text extraction to inspect.]`;
3987
4023
  const { base64, byteLength } = await readFileAsBase64(ctx.execution, ctx.handle, path);
3988
4024
  if (sizeCap > 0 && byteLength > sizeCap) return `[document too large to attach: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes, or use shell/read text extraction to inspect.]`;
3989
4025
  if (docMedia === "application/pdf" && !isPdfBase64(base64)) return `[binary file: ${path}, ${byteLength} bytes; extension suggests PDF but file header does not start with %PDF-]`;
@@ -5344,7 +5380,8 @@ function buildSpawnTool(options, state) {
5344
5380
  let index = childRuns.findIndex((r) => r.label === wanted);
5345
5381
  if (index < 0) {
5346
5382
  const labelMatch = /^child-(\d+)$/.exec(wanted);
5347
- index = labelMatch ? Number(labelMatch[1]) - 1 : childRuns.findIndex((r) => r.id === wanted);
5383
+ const anyLabeled = childRuns.some((r) => r.label !== void 0);
5384
+ index = labelMatch && !anyLabeled ? Number(labelMatch[1]) - 1 : childRuns.findIndex((r) => r.id === wanted);
5348
5385
  }
5349
5386
  const run = index >= 0 ? childRuns[index] : void 0;
5350
5387
  if (!run) return {
@@ -9415,6 +9452,158 @@ async function persistElidedToolResults(messages, items, opts) {
9415
9452
  });
9416
9453
  }
9417
9454
  /**
9455
+ * Subdirectory of the session persist dir holding blobs written by
9456
+ * {@link releaseReplacedTurnContent}. Deliberately separate from the
9457
+ * top-level `<callId>.txt` blobs: once a released stub round-trips into the
9458
+ * session store (`session.save()` is delete-all-and-reinsert), the blob is
9459
+ * the ONLY remaining copy of that content — it must never be LRU-evicted by
9460
+ * {@link enforcePersistDirCap}, whose sweep only scans top-level `*.txt`
9461
+ * files. Released blobs live and die with the session
9462
+ * ({@link cleanupPersistedSession} removes the dir recursively).
9463
+ */
9464
+ const RELEASED_BLOB_SUBDIR = "released";
9465
+ /**
9466
+ * Post-compaction memory release — swap the in-memory bodies of turns a
9467
+ * compaction summary just replaced for recoverable `<persisted-output …>`
9468
+ * stubs, so a long-lived host process stops retaining tool output the model
9469
+ * can never see again (`applyCompactSummaryCutoff` elides these turns from
9470
+ * every future wire projection).
9471
+ *
9472
+ * MUTATES the passed turn objects in place — that is the point: the session's
9473
+ * `data.turns` is the single canonical in-memory copy, and releasing anywhere
9474
+ * else would leave it pinned. Only `tool_result` blocks with large string /
9475
+ * all-text outputs are touched (media already lives behind content refs);
9476
+ * everything else — prompts, assistant text, thinking — is kept verbatim.
9477
+ *
9478
+ * Durability contract: each released output is written to
9479
+ * `<persistDir>/released/<callId>.txt` first, so even if a host later
9480
+ * round-trips the stubbed turns to the session store, the content stays
9481
+ * retrievable from the stub's path. Blobs are always written HOST-side
9482
+ * (never through an execution context): the session store is host-local,
9483
+ * so its recovery copy must be too — a sandboxed context write would
9484
+ * strand the only durable copy inside an ephemeral container. A blob write
9485
+ * failure skips that block — the original stays in memory rather than
9486
+ * being lost.
9487
+ *
9488
+ * Opt-in via `autoCompact.releaseReplacedTurns`: hosts that need the lossless
9489
+ * inline transcript (SFT capture, exporters) keep the default-off behavior.
9490
+ */
9491
+ async function releaseReplacedTurnContent(turns, replacedTurnIds, opts) {
9492
+ const result = {
9493
+ releasedBlocks: 0,
9494
+ releasedBytes: 0
9495
+ };
9496
+ if (replacedTurnIds.size === 0 || !isAbsolute(opts.persistDir)) return result;
9497
+ const minBytes = opts.minBytes ?? 2048;
9498
+ const maxDirBytes = opts.maxDirBytes ?? 268435456;
9499
+ const blobDir = join(opts.persistDir, RELEASED_BLOB_SUBDIR);
9500
+ let dirBytes = 0;
9501
+ try {
9502
+ for (const entry of await readdir(blobDir)) try {
9503
+ dirBytes += (await stat(join(blobDir, entry))).size;
9504
+ } catch {}
9505
+ } catch {}
9506
+ const replaced = turns.filter((t) => t.id !== void 0 && replacedTurnIds.has(t.id));
9507
+ const toolNameByCallId = /* @__PURE__ */ new Map();
9508
+ for (const turn of replaced) for (const block of turn.content) if (block.type === "tool_call") toolNameByCallId.set(block.id, block.name);
9509
+ const callIdOccurrences = /* @__PURE__ */ new Map();
9510
+ for (const turn of replaced) for (let i = 0; i < turn.content.length; i++) {
9511
+ const block = turn.content[i];
9512
+ if (block.type !== "tool_result") continue;
9513
+ const ordinal = (callIdOccurrences.get(block.callId) ?? 0) + 1;
9514
+ callIdOccurrences.set(block.callId, ordinal);
9515
+ const text = textForPersistence(block.output);
9516
+ if (text === null || text.startsWith("<persisted-output tool=\"")) continue;
9517
+ const bytes = utf8ByteLength(text);
9518
+ if (bytes < minBytes) continue;
9519
+ if (!SAFE_CALL_ID.test(block.callId) || block.callId.includes("..")) continue;
9520
+ const persistedPath = join(blobDir, ordinal === 1 ? `${block.callId}.txt` : `${block.callId}.${ordinal}.txt`);
9521
+ let exists = false;
9522
+ try {
9523
+ exists = (await stat(persistedPath)).isFile();
9524
+ } catch {
9525
+ exists = false;
9526
+ }
9527
+ if (!exists) {
9528
+ if (dirBytes + bytes > maxDirBytes) continue;
9529
+ try {
9530
+ await writeAtomic(persistedPath, text);
9531
+ dirBytes += bytes;
9532
+ } catch {
9533
+ continue;
9534
+ }
9535
+ }
9536
+ turn.content[i] = {
9537
+ ...block,
9538
+ output: buildPersistedStub({
9539
+ toolName: toolNameByCallId.get(block.callId) ?? "tool",
9540
+ originalBytes: bytes,
9541
+ persistedPath,
9542
+ output: text,
9543
+ previewBytes: ELIDE_PERSIST_PREVIEW_BYTES
9544
+ })
9545
+ };
9546
+ result.releasedBlocks++;
9547
+ result.releasedBytes += bytes;
9548
+ }
9549
+ return result;
9550
+ }
9551
+ /**
9552
+ * Re-home `<persisted-output …>` stubs when turns are copied into a NEW
9553
+ * session (fork): released blob paths are per-session
9554
+ * (`resolvePersistDir` keys on the session id), so a fork that keeps the
9555
+ * origin's paths loses its content the moment the origin session is
9556
+ * deleted ({@link cleanupPersistedSession} removes the origin's blob dir).
9557
+ * Copies each referenced blob under `fromPersistDir` into the same
9558
+ * relative location under `toPersistDir` and rewrites the stub's `path`
9559
+ * attribute. Copy-on-write: input turns are never mutated (they are
9560
+ * usually shared with the origin session's live `data.turns`); a new turn
9561
+ * object is returned only where a stub was rewritten. A missing/uncopyable
9562
+ * blob leaves that stub pointing at the origin — no worse than before.
9563
+ *
9564
+ * Prefix matching happens on the XML-escaped attribute text: `escapeXml`
9565
+ * maps characters independently, so `escapeXml(a + b)` ===
9566
+ * `escapeXml(a) + escapeXml(b)` and a prefix match on escaped text is
9567
+ * exact. Blob filenames are `SAFE_CALL_ID`-constrained (`[\w.-]`), so the
9568
+ * relative remainder is escape-invariant.
9569
+ */
9570
+ async function rehomePersistedStubs(turns, fromPersistDir, toPersistDir) {
9571
+ if (!isAbsolute(fromPersistDir) || !isAbsolute(toPersistDir) || fromPersistDir === toPersistDir) return [...turns];
9572
+ const fromPrefix = `path="${escapeXml(fromPersistDir)}/`;
9573
+ const out = [];
9574
+ for (const turn of turns) {
9575
+ let content;
9576
+ for (let i = 0; i < turn.content.length; i++) {
9577
+ const block = turn.content[i];
9578
+ if (block.type !== "tool_result" || typeof block.output !== "string" || !block.output.startsWith("<persisted-output tool=\"")) continue;
9579
+ const start = block.output.indexOf(fromPrefix);
9580
+ if (start === -1) continue;
9581
+ const restStart = start + fromPrefix.length;
9582
+ const end = block.output.indexOf("\"", restStart);
9583
+ if (end === -1) continue;
9584
+ const relative = block.output.slice(restStart, end);
9585
+ if (relative.includes("..")) continue;
9586
+ try {
9587
+ const target = join(toPersistDir, relative);
9588
+ await mkdir(dirname(target), { recursive: true });
9589
+ await copyFile(join(fromPersistDir, relative), target);
9590
+ } catch {
9591
+ continue;
9592
+ }
9593
+ content ??= [...turn.content];
9594
+ content[i] = {
9595
+ ...block,
9596
+ output: `${block.output.slice(0, start)}path="${escapeXml(toPersistDir)}/${relative}${block.output.slice(end)}`
9597
+ };
9598
+ }
9599
+ out.push(content ? {
9600
+ ...turn,
9601
+ content
9602
+ } : turn);
9603
+ }
9604
+ return out;
9605
+ }
9606
+ /**
9418
9607
  * Write-then-rename for atomicity — delegates to the shared
9419
9608
  * {@link writeFileAtomicAsync} helper (unique pid+counter tmp suffix,
9420
9609
  * fsync before rename, tmp cleanup on failure). Creates the parent
@@ -9769,6 +9958,24 @@ function applyCompactSummaryCutoff(turns) {
9769
9958
  const COMPACTION_STUB = "[…elided: older tool output trimmed to fit the context budget.]";
9770
9959
  const COMPACTION_STUB_BYTES = toolOutputByteLength(COMPACTION_STUB);
9771
9960
  const COMPACT_CHUNK_TURNS = 8;
9961
+ /**
9962
+ * Cross-turn memo for `toolOutputBudgetByteLength`, keyed on the tool_result
9963
+ * block object. The wire pipeline preserves block references for unchanged
9964
+ * blocks (sanitize/alias/elision copy only what they touch), so the same
9965
+ * block objects flow through the byte-accounting loops on every turn of a
9966
+ * run. Without the memo, tail compaction re-measures the ENTIRE transcript's
9967
+ * tool output each turn — O(total transcript bytes) per turn, O(n²) per run.
9968
+ * Blocks are immutable by pipeline convention; a transform that changes an
9969
+ * output produces a new object, which simply misses the memo once.
9970
+ */
9971
+ const toolResultBudgetBytesMemo = /* @__PURE__ */ new WeakMap();
9972
+ function memoizedToolResultBudgetBytes(block) {
9973
+ const hit = toolResultBudgetBytesMemo.get(block);
9974
+ if (hit !== void 0) return hit;
9975
+ const bytes = toolOutputBudgetByteLength(block.output);
9976
+ toolResultBudgetBytesMemo.set(block, bytes);
9977
+ return bytes;
9978
+ }
9772
9979
  function applyTailCompaction(messages, threshold, keepTurns, options) {
9773
9980
  const elidedReadPaths = [];
9774
9981
  const persistableElided = [];
@@ -9780,7 +9987,7 @@ function applyTailCompaction(messages, threshold, keepTurns, options) {
9780
9987
  persistableElided
9781
9988
  };
9782
9989
  let totalBytes = 0;
9783
- for (const msg of messages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += toolOutputBudgetByteLength(block.output);
9990
+ for (const msg of messages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += memoizedToolResultBudgetBytes(block);
9784
9991
  if (totalBytes <= threshold) return {
9785
9992
  messages,
9786
9993
  elidedReadPaths,
@@ -9796,7 +10003,7 @@ function applyTailCompaction(messages, threshold, keepTurns, options) {
9796
10003
  let keptBytes = 0;
9797
10004
  let budgetCutoff = floorCutoff;
9798
10005
  for (let i = messages.length - 1; i >= 0; i--) {
9799
- for (const block of messages[i].content) if (block.type === "tool_result") keptBytes += toolOutputBudgetByteLength(block.output);
10006
+ for (const block of messages[i].content) if (block.type === "tool_result") keptBytes += memoizedToolResultBudgetBytes(block);
9800
10007
  if (keptBytes > threshold) {
9801
10008
  budgetCutoff = i + 1;
9802
10009
  break;
@@ -10418,11 +10625,58 @@ async function runLoop(ctx) {
10418
10625
  unregisterTtftTool();
10419
10626
  }
10420
10627
  }
10628
+ /**
10629
+ * Per-run LRU for resolved content-ref payloads, keyed by `type:ref`.
10630
+ *
10631
+ * Refs are content-addressed (`attachment://<hash>` and persist-dir blobs are
10632
+ * immutable once written), so within a run the same ref always resolves to the
10633
+ * same bytes — but the wire pipeline re-resolves EVERY ref in the history on
10634
+ * EVERY turn (resolution runs before media age-out, which then discards most
10635
+ * of the resolved base64 again). Without this cache an image-heavy session
10636
+ * re-reads and re-base64s each attachment from disk once per turn, O(refs)
10637
+ * disk I/O per turn → O(turns × refs) per run.
10638
+ *
10639
+ * Keyed on the LoopContext via WeakMap so the cache dies with the run and no
10640
+ * cross-run staleness question arises. Byte-bounded (UTF-8 bytes, measured
10641
+ * once at insert — `.length` counts UTF-16 code units and would let the
10642
+ * budget retain up to 2× its nominal size on multi-byte text), evicting
10643
+ * oldest-first (Map insertion order; hits re-insert to refresh recency).
10644
+ */
10645
+ const CONTENT_REF_CACHE_MAX_BYTES = 64 * 1024 * 1024;
10646
+ const contentRefCaches = /* @__PURE__ */ new WeakMap();
10421
10647
  async function resolveContentRefsForWire(ctx, messages) {
10422
10648
  return resolveContentRefsInMessages(messages, { resolveContentRef: async (block) => {
10649
+ const key = `${block.type}:${block.ref}`;
10650
+ let cache = contentRefCaches.get(ctx);
10651
+ if (!cache) {
10652
+ cache = {
10653
+ bytes: 0,
10654
+ entries: /* @__PURE__ */ new Map()
10655
+ };
10656
+ contentRefCaches.set(ctx, cache);
10657
+ }
10658
+ const hit = cache.entries.get(key);
10659
+ if (hit !== void 0) {
10660
+ cache.entries.delete(key);
10661
+ cache.entries.set(key, hit);
10662
+ return { data: hit.data };
10663
+ }
10423
10664
  const hookCtx = { block };
10424
10665
  await ctx.hooks.callHook("content-ref:resolve", hookCtx);
10425
10666
  if (typeof hookCtx.data !== "string") throw new Error(`Content ref "${block.ref}" (${block.type}) was not resolved by a content-ref:resolve hook.`);
10667
+ const dataBytes = Buffer.byteLength(hookCtx.data, "utf8");
10668
+ if (dataBytes <= CONTENT_REF_CACHE_MAX_BYTES) {
10669
+ cache.entries.set(key, {
10670
+ data: hookCtx.data,
10671
+ bytes: dataBytes
10672
+ });
10673
+ cache.bytes += dataBytes;
10674
+ for (const [k, v] of cache.entries) {
10675
+ if (cache.bytes <= CONTENT_REF_CACHE_MAX_BYTES) break;
10676
+ cache.entries.delete(k);
10677
+ cache.bytes -= v.bytes;
10678
+ }
10679
+ }
10426
10680
  return { data: hookCtx.data };
10427
10681
  } });
10428
10682
  }
@@ -10587,10 +10841,15 @@ function stripInvalidAnthropicThinking(content) {
10587
10841
  };
10588
10842
  }
10589
10843
  /**
10590
- * Permanently quarantine historical Anthropic reasoning after the API proves
10591
- * its opaque signature is invalid. Runtime turns and the bound session are
10592
- * separate arrays on resume, so repair both; persist the session with its
10593
- * full turn set (including child runs invisible to the parent runtime).
10844
+ * Quarantine historical Anthropic reasoning after the API proves its opaque
10845
+ * signature is invalid. Runtime turns and the bound session are separate
10846
+ * arrays on resume, so repair both — IN MEMORY ONLY. Durable persistence
10847
+ * (`session.save()`) is deliberately deferred to the stream-settle boundary
10848
+ * in {@link streamWithWatchdogs}: this function runs inside the provider's
10849
+ * `stream()` call stack, where durable-execution hosts (e.g. Restate
10850
+ * journaling the provider call) cannot touch their context — an inline
10851
+ * save would silently no-op there and the session would re-poison on every
10852
+ * run.
10594
10853
  *
10595
10854
  * Turns minted by the CURRENT run are never touched: their signatures are
10596
10855
  * fresh (same account/model that is streaming right now), and Anthropic
@@ -10598,7 +10857,8 @@ function stripInvalidAnthropicThinking(content) {
10598
10857
  * stripping them would trade one 400 for another. An invalid signature is
10599
10858
  * by definition historical (prior run, prior account, or a session edit).
10600
10859
  */
10601
- async function repairInvalidAnthropicThinkingHistory(ctx, streamOptions, repair) {
10860
+ function repairInvalidAnthropicThinkingHistory(ctx, streamOptions, repair) {
10861
+ const turnIds = /* @__PURE__ */ new Set();
10602
10862
  let removed = 0;
10603
10863
  for (const turn of ctx.turns) {
10604
10864
  if (turn.runId === ctx.runId) continue;
@@ -10606,6 +10866,7 @@ async function repairInvalidAnthropicThinkingHistory(ctx, streamOptions, repair)
10606
10866
  if (scrubbed.removed === 0) continue;
10607
10867
  turn.content = scrubbed.content;
10608
10868
  removed += scrubbed.removed;
10869
+ if (turn.id !== void 0) turnIds.add(turn.id);
10609
10870
  }
10610
10871
  const limit = repair.wireMessageIndex;
10611
10872
  streamOptions.messages = streamOptions.messages.map((message, index) => {
@@ -10617,34 +10878,72 @@ async function repairInvalidAnthropicThinkingHistory(ctx, streamOptions, repair)
10617
10878
  } : message;
10618
10879
  });
10619
10880
  const session = ctx.session;
10620
- if (!session) return removed;
10881
+ if (!session) return {
10882
+ removed,
10883
+ turnIds
10884
+ };
10621
10885
  let sessionRemoved = 0;
10622
10886
  const sessionTurns = session.turns.map((turn) => {
10623
10887
  if (turn.runId === ctx.runId) return turn;
10624
10888
  const scrubbed = stripInvalidAnthropicThinking(turn.content);
10625
10889
  sessionRemoved += scrubbed.removed;
10890
+ if (scrubbed.removed > 0 && turn.id !== void 0) turnIds.add(turn.id);
10626
10891
  return scrubbed.removed > 0 ? {
10627
10892
  ...turn,
10628
10893
  content: scrubbed.content
10629
10894
  } : turn;
10630
10895
  });
10631
- if (sessionRemoved === 0 && removed === 0) return 0;
10896
+ if (sessionRemoved === 0 && removed === 0) return {
10897
+ removed: 0,
10898
+ turnIds
10899
+ };
10632
10900
  session.setTurns(sessionTurns);
10633
- try {
10634
- await session.save();
10901
+ return {
10902
+ removed: removed + sessionRemoved,
10903
+ turnIds
10904
+ };
10905
+ }
10906
+ /**
10907
+ * Flush a deferred thinking-signature repair at the stream-settle boundary:
10908
+ * persist the already-repaired session and fire the host-facing
10909
+ * `history:repair` hook. Runs OUTSIDE the provider call stack (safe for
10910
+ * journaled hosts) and on both stream outcomes — a scrubbed block is
10911
+ * invalid regardless of whether the retry ultimately succeeded, and
10912
+ * persisting prevents the next run from re-poisoning.
10913
+ */
10914
+ async function flushThinkingRepair(ctx, pending) {
10915
+ let persisted = false;
10916
+ if (ctx.session?.store) try {
10917
+ await ctx.session.save();
10918
+ persisted = true;
10635
10919
  } catch (err) {
10636
10920
  if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/loop] persisting thinking-signature repair failed: ${errorMessage(err)}\n`);
10637
10921
  }
10638
- return Math.max(removed, sessionRemoved);
10922
+ await settledHook(ctx, "hook history:repair", ctx.hooks.callHook("history:repair", {
10923
+ reason: "invalid-anthropic-thinking-signature",
10924
+ runId: ctx.runId,
10925
+ ...ctx.session ? { sessionId: ctx.session.id } : {},
10926
+ turnIds: [...pending.turnIds],
10927
+ removedBlocks: pending.removed,
10928
+ persisted
10929
+ }));
10639
10930
  }
10640
10931
  async function streamWithWatchdogs(ctx, options, callbacks, turnId, opts) {
10641
10932
  let { startTimeoutMs, totalTimeoutMs } = providerWatchdogTimeouts(ctx);
10642
10933
  if (opts?.startTimeoutCapMs !== void 0 && startTimeoutMs > 0) startTimeoutMs = Math.min(startTimeoutMs, opts.startTimeoutCapMs);
10643
10934
  const callerHistoryRepair = callbacks.onHistoryRepair;
10935
+ let pendingRepair;
10644
10936
  callbacks = {
10645
10937
  ...callbacks,
10646
10938
  async onHistoryRepair(repair) {
10647
- if (repair.type === "invalid-anthropic-thinking-signature") await repairInvalidAnthropicThinkingHistory(ctx, options, repair);
10939
+ if (repair.type === "invalid-anthropic-thinking-signature") {
10940
+ const result = repairInvalidAnthropicThinkingHistory(ctx, options, repair);
10941
+ if (result.removed > 0) if (pendingRepair === void 0) pendingRepair = result;
10942
+ else {
10943
+ pendingRepair.removed += result.removed;
10944
+ for (const id of result.turnIds) pendingRepair.turnIds.add(id);
10945
+ }
10946
+ }
10648
10947
  await callerHistoryRepair?.(repair);
10649
10948
  }
10650
10949
  };
@@ -10705,7 +11004,7 @@ async function streamWithWatchdogs(ctx, options, callbacks, turnId, opts) {
10705
11004
  throw err;
10706
11005
  }
10707
11006
  streamPromise.catch(() => {});
10708
- return await new Promise((resolve, reject) => {
11007
+ const raced = new Promise((resolve, reject) => {
10709
11008
  const failTimeout = (kind, timeoutMs) => {
10710
11009
  if (settled) return;
10711
11010
  settled = true;
@@ -10732,6 +11031,11 @@ async function streamWithWatchdogs(ctx, options, callbacks, turnId, opts) {
10732
11031
  reject(err);
10733
11032
  });
10734
11033
  });
11034
+ try {
11035
+ return await raced;
11036
+ } finally {
11037
+ if (pendingRepair !== void 0) await flushThinkingRepair(ctx, pendingRepair);
11038
+ }
10735
11039
  }
10736
11040
  /** Max bytes of provider error text inlined into the assistant turn placeholder. */
10737
11041
  const ERROR_PLACEHOLDER_MAX = 280;
@@ -10864,7 +11168,8 @@ function resolveAutoCompact(raw, opts = {}) {
10864
11168
  restore: cfg.restore !== false,
10865
11169
  maxFailures: typeof cfg.maxFailures === "number" && Number.isFinite(cfg.maxFailures) && cfg.maxFailures > 0 ? Math.floor(cfg.maxFailures) : DEFAULT_AUTO_COMPACT_MAX_FAILURES,
10866
11170
  provider: cfg.provider ?? null,
10867
- model: typeof cfg.model === "string" && cfg.model.length > 0 ? cfg.model : null
11171
+ model: typeof cfg.model === "string" && cfg.model.length > 0 ? cfg.model : null,
11172
+ releaseReplacedTurns: cfg.releaseReplacedTurns === true
10868
11173
  };
10869
11174
  }
10870
11175
  /**
@@ -10966,6 +11271,11 @@ async function maybeAutoCompact(ctx, turn, lastTurnUsage, state) {
10966
11271
  }
10967
11272
  ctx.turns.push(summaryTurn, ...restorationTurns);
10968
11273
  await ctx.persistTurns?.();
11274
+ if (cfg.releaseReplacedTurns && ctx.persistDir && result.summarizedTurnIds.length > 0) try {
11275
+ await releaseReplacedTurnContent(ctx.turns, new Set(result.summarizedTurnIds), { persistDir: ctx.persistDir });
11276
+ } catch (err) {
11277
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/loop] post-compact turn release failed: ${errorMessage(err)}\n`);
11278
+ }
10969
11279
  const absorbedIds = /* @__PURE__ */ new Set([...result.summarizedTurnIds, ...result.droppedDueToPtl]);
10970
11280
  const onWireReads = new Set(readFilePathsInTurns(applyCompactSummaryCutoff(ctx.turns)));
10971
11281
  markReadStateForElidedPaths(ctx, ctx.handle.cwd, readFilePathsInTurns(ctx.turns, absorbedIds).filter((p) => !onWireReads.has(p)));
@@ -11113,10 +11423,12 @@ async function executeTurn(ctx, turn, priorUsage) {
11113
11423
  }
11114
11424
  const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps);
11115
11425
  let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages);
11426
+ let callMapsMemo;
11427
+ const callMaps = () => callMapsMemo ??= collectCallMaps(canonicalMessages);
11116
11428
  if (ctx.compactStrategy === "tail") {
11117
11429
  const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
11118
11430
  const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
11119
- const { readPathByCallId, toolNameByCallId } = collectCallMaps(canonicalMessages);
11431
+ const { readPathByCallId, toolNameByCallId } = callMaps();
11120
11432
  const compacted = applyTailCompaction(sanitizedMessages, threshold, keep, {
11121
11433
  readPathByCallId,
11122
11434
  toolNameByCallId,
@@ -11134,7 +11446,7 @@ async function executeTurn(ctx, turn, priorUsage) {
11134
11446
  const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
11135
11447
  const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
11136
11448
  let totalBytes = 0;
11137
- for (const msg of sanitizedMessages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += toolOutputBudgetByteLength(block.output);
11449
+ for (const msg of sanitizedMessages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += memoizedToolResultBudgetBytes(block);
11138
11450
  try {
11139
11451
  const compacted = await ctx.compactStrategy(sanitizedMessages, {
11140
11452
  threshold,
@@ -11147,7 +11459,7 @@ async function executeTurn(ctx, turn, priorUsage) {
11147
11459
  }
11148
11460
  }
11149
11461
  if (typeof ctx.mediaKeepTurns === "number" && ctx.mediaKeepTurns >= 0 && ctx.tools.read_file) {
11150
- const { readPathByCallId } = collectCallMaps(canonicalMessages);
11462
+ const { readPathByCallId } = callMaps();
11151
11463
  const aged = applyMediaAgeout(sanitizedMessages, ctx.mediaKeepTurns, {
11152
11464
  chunkTurns: COMPACT_CHUNK_TURNS,
11153
11465
  readPathByCallId,
@@ -11293,7 +11605,7 @@ async function executeTurn(ctx, turn, priorUsage) {
11293
11605
  }
11294
11606
  }
11295
11607
  if (!wasAborted && classified?.kind === "context_exceeded" && cleanSlate && overflowRecoveries < CONTEXT_OVERFLOW_KEEP_TURNS.length) {
11296
- const { readPathByCallId, toolNameByCallId } = collectCallMaps(canonicalMessages);
11608
+ const { readPathByCallId, toolNameByCallId } = callMaps();
11297
11609
  let recoveredMessages;
11298
11610
  const elidedReadPaths = [];
11299
11611
  for (let k = overflowRecoveries; k < CONTEXT_OVERFLOW_KEEP_TURNS.length; k++) {
@@ -12993,6 +13305,7 @@ const HOOK_EVENT_SET = /* @__PURE__ */ new Set([
12993
13305
  "compact:start",
12994
13306
  "compact:progress",
12995
13307
  "compact:end",
13308
+ "history:repair",
12996
13309
  "oauth:refresh",
12997
13310
  "content-ref:resolve",
12998
13311
  "tool:gate",
@@ -15100,6 +15413,6 @@ function createAgent(rawOptions) {
15100
15413
  };
15101
15414
  }
15102
15415
  //#endregion
15103
- export { composeExtensionPreset as $, discoverExtensions as $t, ANCHOR_PREVIEW_MAX_CHARS as A, createInteractionTool as At, ExtensionInstallError as B, ensureKeybindingsFile as Bt, NO_TOOLS_PREAMBLE as C, readFile$1 as Ct, buildFullCompactPrompt as D, formatModelLine as Dt, buildFromCompactPrompt as E, createModelSearchTool as Et, truncateHeadForPtlRetry as F, resolveStorageDirs as Ft, readExtensionInstallProvenance as G, mergeKeybindings as Gt, installExtensionFromSpec as H, groupBindings as Ht, CompactInvalidInputError as I, DEFAULT_KEYBINDINGS as It, createExtensionHud as J, readKeybindingsRaw as Jt, removeInstalledExtension as K, parseBindingSpec as Kt, CompactPromptTooLongError as L, KEYBINDING_DEFS as Lt, sliceForCompaction as M, glob as Mt, stripImagesFromTurns as N, edit as Nt, buildTailCompactPrompt as O, matchModelEntries as Ot, summaryToTurn as P, validateToolArgs as Pt, buildExtensionRegistry as Q, defaultExtensionScanPaths as Qt, EMPTY_EXTENSION_REGISTRY as R, KEYBINDING_DEF_BY_ACTION as Rt, BASE_INSTRUCTIONS as S, tailTruncate as St, buildCompactPrompt as T, multiEdit as Tt, listExtensionCandidates as U, keybindingsPath as Ut, fetchExtensionSource as V, formatBindingForDisplay as Vt, parseExtensionInstallSource as W, matchesBinding as Wt, discoverAndBuildExtensionRegistry as X, stripJsonComments as Xt, registerExtensionHostModules as Y, resolveExtensionKeybindings as Yt, buildExtensionPreset as Z, ENTRY_CANDIDATES as Zt, buildPostCompactAttachments as _, defaultRepeatGuardNormalize as _n, createSkillsRunScriptTool as _t, SHELL_CASCADE_CANCEL_MESSAGE as a, clampExtensionConfigNumber as an, createWorkspaceSurface as at, selectRecentFiles as b, stableStringify as bn, createShellTool as bt, applyCompactSummaryCutoff as c, isValidExtensionConfigValue as cn, basicTools as ct, buildPersistedStub as d, sanitizeExtensionConfigActionPatch as dn, WAIT_TASK_TIMED_OUT_PREFIX as dt, inlineLoadedExtension as en, disposeExtensionRegistry as et, cleanupPersistedSession as f, toWireConfigSchema as fn, waitTask as ft, resolveTasksDir as g, shouldAutoCompact as gn, createSkillsUseTool as gt, resolvePersistDir as h, effectiveContextWindow as hn, isSpawnTool as ht, INTERRUPT_MESSAGE_FOR_TOOL_USE as i, defineExtension as in, createExtensionHostRuntimeBridge as it, anchorPreviewFor as j, grep as jt, buildUpToCompactPrompt as k, listFiles as kt, PERSISTED_STUB_PREFIX as l, resolveExtensionConfig as ln, basic_default as lt, resolveMcpWarningsDir as m, OUTPUT_RESERVE_TOKENS as mn, createSpawnTool as mt, normalizeProviders as n, parseExtensionScanPathEnv as nn, mergeContributions as nt, TOOL_USE_CANCELLED_MESSAGE as o, coerceExtensionConfigInput as on, composePresets as ot, maybePersistToolResult as p, AUTO_COMPACT_MIN_GROWTH_FRACTION as pn, createToolSearchTool as pt, updateInstalledExtension as q, readKeybindings as qt, resolveModelTargets as r, scanExtensionDirectories as rn, partitionExtensionCatalog as rt, TOOL_USE_SKIPPED_MESSAGE as s, isValidExtensionConfigItem as sn, definePreset as st, createAgent as t, isExtensionDefinition as tn, filterExtensionRegistry as tt, PERSISTENCE_PREVIEW_BYTES as u, resolveExtensionConfigValue as un, writeFile$1 as ut, selectFilesFromReadState as v, defaultRepeatGuardTracked as vn, createSkillsReadTool as vt, TRAILER as w, looksBinary as wt, compactConversation as x, shell as xt, selectFilesFromSession as y, normalizeShellCommand as yn, shellKill as yt, createExtensionPromptBridge as z, KEYBINDING_KEY_COL_WIDTH as zt };
15416
+ export { buildExtensionRegistry as $, defaultExtensionScanPaths as $t, buildUpToCompactPrompt as A, listFiles as At, createExtensionPromptBridge as B, KEYBINDING_KEY_COL_WIDTH as Bt, BASE_INSTRUCTIONS as C, tailTruncate as Ct, buildFromCompactPrompt as D, createModelSearchTool as Dt, buildCompactPrompt as E, multiEdit as Et, summaryToTurn as F, validateToolArgs as Ft, parseExtensionInstallSource as G, matchesBinding as Gt, fetchExtensionSource as H, formatBindingForDisplay as Ht, truncateHeadForPtlRetry as I, resolveStorageDirs as It, updateInstalledExtension as J, readKeybindings as Jt, readExtensionInstallProvenance as K, mergeKeybindings as Kt, CompactInvalidInputError as L, DEFAULT_KEYBINDINGS as Lt, anchorPreviewFor as M, grep as Mt, sliceForCompaction as N, glob as Nt, buildFullCompactPrompt as O, formatModelLine as Ot, stripImagesFromTurns as P, edit as Pt, buildExtensionPreset as Q, ENTRY_CANDIDATES as Qt, CompactPromptTooLongError as R, KEYBINDING_DEFS as Rt, compactConversation as S, shell as St, TRAILER as T, looksBinary as Tt, installExtensionFromSpec as U, groupBindings as Ut, ExtensionInstallError as V, ensureKeybindingsFile as Vt, listExtensionCandidates as W, keybindingsPath as Wt, registerExtensionHostModules as X, resolveExtensionKeybindings as Xt, createExtensionHud as Y, readKeybindingsRaw as Yt, discoverAndBuildExtensionRegistry as Z, stripJsonComments as Zt, resolveTasksDir as _, shouldAutoCompact as _n, createSkillsUseTool as _t, SHELL_CASCADE_CANCEL_MESSAGE as a, defineExtension as an, createExtensionHostRuntimeBridge as at, selectFilesFromSession as b, normalizeShellCommand as bn, shellKill as bt, applyCompactSummaryCutoff as c, isValidExtensionConfigItem as cn, definePreset as ct, buildPersistedStub as d, resolveExtensionConfigValue as dn, writeFile$1 as dt, discoverExtensions as en, composeExtensionPreset as et, cleanupPersistedSession as f, sanitizeExtensionConfigActionPatch as fn, WAIT_TASK_TIMED_OUT_PREFIX as ft, resolvePersistDir as g, effectiveContextWindow as gn, isSpawnTool as gt, resolveMcpWarningsDir as h, OUTPUT_RESERVE_TOKENS as hn, createSpawnTool as ht, INTERRUPT_MESSAGE_FOR_TOOL_USE as i, scanExtensionDirectories as in, partitionExtensionCatalog as it, ANCHOR_PREVIEW_MAX_CHARS as j, createInteractionTool as jt, buildTailCompactPrompt as k, matchModelEntries as kt, PERSISTED_STUB_PREFIX as l, isValidExtensionConfigValue as ln, basicTools as lt, rehomePersistedStubs as m, AUTO_COMPACT_MIN_GROWTH_FRACTION as mn, createToolSearchTool as mt, normalizeProviders as n, isExtensionDefinition as nn, filterExtensionRegistry as nt, TOOL_USE_CANCELLED_MESSAGE as o, clampExtensionConfigNumber as on, createWorkspaceSurface as ot, maybePersistToolResult as p, toWireConfigSchema as pn, waitTask as pt, removeInstalledExtension as q, parseBindingSpec as qt, resolveModelTargets as r, parseExtensionScanPathEnv as rn, mergeContributions as rt, TOOL_USE_SKIPPED_MESSAGE as s, coerceExtensionConfigInput as sn, composePresets as st, createAgent as t, inlineLoadedExtension as tn, disposeExtensionRegistry as tt, PERSISTENCE_PREVIEW_BYTES as u, resolveExtensionConfig as un, basic_default as ut, buildPostCompactAttachments as v, defaultRepeatGuardNormalize as vn, createSkillsRunScriptTool as vt, NO_TOOLS_PREAMBLE as w, readFile$1 as wt, selectRecentFiles as x, stableStringify as xn, createShellTool as xt, selectFilesFromReadState as y, defaultRepeatGuardTracked as yn, createSkillsReadTool as yt, EMPTY_EXTENSION_REGISTRY as z, KEYBINDING_DEF_BY_ACTION as zt };
15104
15417
 
15105
- //# sourceMappingURL=agent-tLfGSSQw.js.map
15418
+ //# sourceMappingURL=agent-DIe8yzcb.js.map