zidane 6.2.23 → 6.2.26
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.
- package/dist/{acp-B7wH6LhE.js → acp-CNZqD1Fw.js} +5 -5
- package/dist/{acp-B7wH6LhE.js.map → acp-CNZqD1Fw.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-BSIMsEtD.js → agent--VPMZ0nT.js} +324 -39
- package/dist/agent--VPMZ0nT.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{auth-CpdQebUy.js → auth-DIT_DMwa.js} +2 -2
- package/dist/{auth-CpdQebUy.js.map → auth-DIT_DMwa.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +4 -4
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +6 -6
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +9 -2
- package/dist/contexts/daytona.js.map +1 -1
- package/dist/contexts/docker.js +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +3 -4
- package/dist/contexts/e2b.js.map +1 -1
- package/dist/{contexts-A4ep-iJs.js → contexts-0rmrBhTK.js} +8 -5
- package/dist/contexts-0rmrBhTK.js.map +1 -0
- package/dist/contexts.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +2 -2
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/{output-cap-kBUHBlnq.js → glob-DluQFSw1.js} +2 -50
- package/dist/glob-DluQFSw1.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +4 -4
- package/dist/history-search-worker.js +6 -1
- package/dist/history-search-worker.js.map +1 -1
- package/dist/{index-Bv9qhj4n.d.ts → index-AVZ6pYpi.d.ts} +254 -97
- package/dist/index-AVZ6pYpi.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -9
- package/dist/index.js.map +1 -1
- package/dist/{inline-autocomplete-BAwBcZtD.js → inline-autocomplete-CbcdZpfC.js} +2 -1
- package/dist/inline-autocomplete-CbcdZpfC.js.map +1 -0
- package/dist/{login-BkZ_d5sT.js → login-afiadwf0.js} +35 -19
- package/dist/login-afiadwf0.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/output-cap-DM1HrgIp.js +100 -0
- package/dist/output-cap-DM1HrgIp.js.map +1 -0
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/{resolve-x5mlFtLX.js → resolve-BPTA1RpX.js} +23 -3
- package/dist/resolve-BPTA1RpX.js.map +1 -0
- package/dist/restate.d.ts +1 -1
- package/dist/{run-summary-nD3IQ4ZQ.js → run-summary-BU_NgwUT.js} +17 -1
- package/dist/run-summary-BU_NgwUT.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-Dbvz1ZUE.js → session-jLsc_kMH.js} +22 -3
- package/dist/session-jLsc_kMH.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +2 -2
- package/dist/{skills-C1vGKbqv.js → skills-C7eIjWYm.js} +2 -2
- package/dist/{skills-C1vGKbqv.js.map → skills-C7eIjWYm.js.map} +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +2 -2
- package/dist/{tool-formatters-D9P6Znip.d.ts → tool-formatters-Dual3Hdl.d.ts} +2 -2
- package/dist/{tool-formatters-D9P6Znip.d.ts.map → tool-formatters-Dual3Hdl.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-Ci5888xS.d.ts → transcript-anchors-CG85bieG.d.ts} +2 -2
- package/dist/transcript-anchors-CG85bieG.d.ts.map +1 -0
- package/dist/{transcript-anchors-CqzIr74w.js → transcript-anchors-CnmxZ8QR.js} +96 -16
- package/dist/transcript-anchors-CnmxZ8QR.js.map +1 -0
- package/dist/tui.d.ts +55 -8
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1845 -1145
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CokV32wf.js → turn-operations-4tnvKwfJ.js} +82 -14
- package/dist/turn-operations-4tnvKwfJ.js.map +1 -0
- package/dist/{turn-operations-C3y6A82G.d.ts → turn-operations-BUYaSCL3.d.ts} +3 -3
- package/dist/{turn-operations-C3y6A82G.d.ts.map → turn-operations-BUYaSCL3.d.ts.map} +1 -1
- package/dist/types-BIarq1qE.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -1
- package/dist/agent-BSIMsEtD.js.map +0 -1
- package/dist/contexts-A4ep-iJs.js.map +0 -1
- package/dist/index-Bv9qhj4n.d.ts.map +0 -1
- package/dist/inline-autocomplete-BAwBcZtD.js.map +0 -1
- package/dist/login-BkZ_d5sT.js.map +0 -1
- package/dist/output-cap-kBUHBlnq.js.map +0 -1
- package/dist/resolve-x5mlFtLX.js.map +0 -1
- package/dist/run-summary-nD3IQ4ZQ.js.map +0 -1
- package/dist/session-Dbvz1ZUE.js.map +0 -1
- package/dist/transcript-anchors-Ci5888xS.d.ts.map +0 -1
- package/dist/transcript-anchors-CqzIr74w.js.map +0 -1
- package/dist/turn-operations-CokV32wf.js.map +0 -1
|
@@ -5,27 +5,28 @@ 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 {
|
|
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 {
|
|
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
|
-
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-
|
|
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";
|
|
14
15
|
import { r as createLogger, t as consoleSink } from "./logger-DBX9uYOw.js";
|
|
15
16
|
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
|
-
import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
|
|
18
18
|
import { C as canonicalizePrompt, S as buildPromptMessage } from "./openai-compat-pHI0kBWH.js";
|
|
19
19
|
import { appendStaticSection, renderSystemForWire } from "./prompt.js";
|
|
20
20
|
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";
|
|
21
|
-
import { a as getExtensionSessionState, c as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as
|
|
21
|
+
import { a as getExtensionSessionState, c as setExtensionSessionState, l as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as resumeChainRunIds } from "./session-jLsc_kMH.js";
|
|
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";
|
|
24
25
|
import { basename, delimiter, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
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)
|
|
305
|
-
|
|
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
|
-
|
|
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-]`;
|
|
@@ -4838,6 +4874,7 @@ const BUBBLED_EVENTS = [
|
|
|
4838
4874
|
"stream:error",
|
|
4839
4875
|
"stream:server_tool_use",
|
|
4840
4876
|
"stream:server_tool_result",
|
|
4877
|
+
"stream:retry",
|
|
4841
4878
|
"tool:dispatched",
|
|
4842
4879
|
"tool:before",
|
|
4843
4880
|
"tool:after",
|
|
@@ -4861,6 +4898,7 @@ const CHILD_EVENT_NAME = {
|
|
|
4861
4898
|
"stream:error": "child:stream:error",
|
|
4862
4899
|
"stream:server_tool_use": "child:stream:server_tool_use",
|
|
4863
4900
|
"stream:server_tool_result": "child:stream:server_tool_result",
|
|
4901
|
+
"stream:retry": "child:stream:retry",
|
|
4864
4902
|
"tool:dispatched": "child:tool:dispatched",
|
|
4865
4903
|
"tool:before": "child:tool:before",
|
|
4866
4904
|
"tool:after": "child:tool:after",
|
|
@@ -4883,19 +4921,27 @@ function childRunsOf(session) {
|
|
|
4883
4921
|
return session.runs.filter((r) => (r.depth ?? 0) > 0);
|
|
4884
4922
|
}
|
|
4885
4923
|
/**
|
|
4886
|
-
* Human/model-facing list of this session's child runs
|
|
4887
|
-
*
|
|
4888
|
-
*
|
|
4889
|
-
*
|
|
4924
|
+
* Human/model-facing list of this session's child runs. Prefers the label
|
|
4925
|
+
* persisted at `startRun` (`SessionRun.label`) and falls back to positional
|
|
4926
|
+
* numbering for legacy sessions recorded before labels were stored.
|
|
4927
|
+
* Rendered into resume-rejection messages so the model can self-correct
|
|
4928
|
+
* with a valid label.
|
|
4890
4929
|
*/
|
|
4891
4930
|
function describeChildRuns(session) {
|
|
4892
4931
|
const childRuns = childRunsOf(session);
|
|
4893
4932
|
if (childRuns.length === 0) return "No child runs exist in this session.";
|
|
4894
|
-
return childRuns.map((r, i) => `- child-${i + 1} · ${r.status} · ${r.prompt.slice(0, 80)}`).join("\n");
|
|
4933
|
+
return childRuns.map((r, i) => `- ${r.label ?? `child-${i + 1}`} · ${r.status} · ${r.prompt.slice(0, 80)}`).join("\n");
|
|
4895
4934
|
}
|
|
4896
4935
|
function reserveChildLabel(session) {
|
|
4897
4936
|
let counter = sessionChildCounters.get(session);
|
|
4898
|
-
if (counter === void 0)
|
|
4937
|
+
if (counter === void 0) {
|
|
4938
|
+
const childRuns = childRunsOf(session);
|
|
4939
|
+
counter = childRuns.length;
|
|
4940
|
+
for (const r of childRuns) {
|
|
4941
|
+
const m = r.label ? /^child-(\d+)$/.exec(r.label) : null;
|
|
4942
|
+
if (m) counter = Math.max(counter, Number(m[1]));
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4899
4945
|
counter += 1;
|
|
4900
4946
|
sessionChildCounters.set(session, counter);
|
|
4901
4947
|
return `child-${counter}`;
|
|
@@ -5318,7 +5364,12 @@ function buildSpawnTool(options, state) {
|
|
|
5318
5364
|
const announcedModel = childModel ?? childProvider.meta.defaultModel;
|
|
5319
5365
|
let resumeRun;
|
|
5320
5366
|
let resumeLabel;
|
|
5321
|
-
if (
|
|
5367
|
+
if (input.resume !== void 0 && input.resume !== null) {
|
|
5368
|
+
if (typeof input.resume !== "string" || input.resume.trim() === "") return {
|
|
5369
|
+
status: "rejected",
|
|
5370
|
+
text: "",
|
|
5371
|
+
message: `Cannot resume: invalid resume value ${JSON.stringify(input.resume)} — pass a child label like "child-2".`
|
|
5372
|
+
};
|
|
5322
5373
|
const wanted = input.resume.trim();
|
|
5323
5374
|
if (!options.persist || !ctx.session) return {
|
|
5324
5375
|
status: "rejected",
|
|
@@ -5326,26 +5377,32 @@ function buildSpawnTool(options, state) {
|
|
|
5326
5377
|
message: "Cannot resume: resuming a sub-agent requires the spawn tool to be configured with persist: true and the parent to have a session — no prior child turns are stored here."
|
|
5327
5378
|
};
|
|
5328
5379
|
const childRuns = childRunsOf(ctx.session);
|
|
5329
|
-
|
|
5330
|
-
|
|
5380
|
+
let index = childRuns.findIndex((r) => r.label === wanted);
|
|
5381
|
+
if (index < 0) {
|
|
5382
|
+
const labelMatch = /^child-(\d+)$/.exec(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);
|
|
5385
|
+
}
|
|
5331
5386
|
const run = index >= 0 ? childRuns[index] : void 0;
|
|
5332
5387
|
if (!run) return {
|
|
5333
5388
|
status: "rejected",
|
|
5334
5389
|
text: "",
|
|
5335
5390
|
message: `Cannot resume: no child run matches "${wanted}". Child runs in this session:\n${describeChildRuns(ctx.session)}`
|
|
5336
5391
|
};
|
|
5392
|
+
const runLabel = run.label ?? `child-${index + 1}`;
|
|
5337
5393
|
if (run.status === "running") return {
|
|
5338
5394
|
status: "rejected",
|
|
5339
5395
|
text: "",
|
|
5340
|
-
message: `Cannot resume:
|
|
5396
|
+
message: `Cannot resume: ${runLabel} is still running. Only terminal children (aborted / error / completed) can be resumed.`
|
|
5341
5397
|
};
|
|
5342
|
-
|
|
5398
|
+
const chain = resumeChainRunIds(ctx.session.runs, run.id);
|
|
5399
|
+
if (!ctx.session.turns.some((t) => t.runId !== void 0 && chain.has(t.runId))) return {
|
|
5343
5400
|
status: "rejected",
|
|
5344
5401
|
text: "",
|
|
5345
|
-
message: `Cannot resume:
|
|
5402
|
+
message: `Cannot resume: ${runLabel} has no persisted turns (it likely failed before producing anything). Spawn a fresh child with a full task instead.`
|
|
5346
5403
|
};
|
|
5347
5404
|
resumeRun = run;
|
|
5348
|
-
resumeLabel =
|
|
5405
|
+
resumeLabel = runLabel;
|
|
5349
5406
|
}
|
|
5350
5407
|
const parentDepth = ctx.depth ?? 0;
|
|
5351
5408
|
const childDepth = parentDepth + 1;
|
|
@@ -5438,6 +5495,7 @@ function buildSpawnTool(options, state) {
|
|
|
5438
5495
|
signal: ctx.signal,
|
|
5439
5496
|
depth: childDepth,
|
|
5440
5497
|
...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {},
|
|
5498
|
+
...options.persist && ctx.session ? { runLabel: id } : {},
|
|
5441
5499
|
...resumeRun ? { resumeRunId: resumeRun.id } : {},
|
|
5442
5500
|
...propagatedTracing ? { tracingContext: propagatedTracing } : {}
|
|
5443
5501
|
});
|
|
@@ -9394,6 +9452,147 @@ async function persistElidedToolResults(messages, items, opts) {
|
|
|
9394
9452
|
});
|
|
9395
9453
|
}
|
|
9396
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 blobDir = join(opts.persistDir, RELEASED_BLOB_SUBDIR);
|
|
9499
|
+
const replaced = turns.filter((t) => t.id !== void 0 && replacedTurnIds.has(t.id));
|
|
9500
|
+
const toolNameByCallId = /* @__PURE__ */ new Map();
|
|
9501
|
+
for (const turn of replaced) for (const block of turn.content) if (block.type === "tool_call") toolNameByCallId.set(block.id, block.name);
|
|
9502
|
+
const callIdOccurrences = /* @__PURE__ */ new Map();
|
|
9503
|
+
for (const turn of replaced) for (let i = 0; i < turn.content.length; i++) {
|
|
9504
|
+
const block = turn.content[i];
|
|
9505
|
+
if (block.type !== "tool_result") continue;
|
|
9506
|
+
const ordinal = (callIdOccurrences.get(block.callId) ?? 0) + 1;
|
|
9507
|
+
callIdOccurrences.set(block.callId, ordinal);
|
|
9508
|
+
const text = textForPersistence(block.output);
|
|
9509
|
+
if (text === null || text.startsWith("<persisted-output tool=\"")) continue;
|
|
9510
|
+
const bytes = utf8ByteLength(text);
|
|
9511
|
+
if (bytes < minBytes) continue;
|
|
9512
|
+
if (!SAFE_CALL_ID.test(block.callId) || block.callId.includes("..")) continue;
|
|
9513
|
+
const persistedPath = join(blobDir, ordinal === 1 ? `${block.callId}.txt` : `${block.callId}.${ordinal}.txt`);
|
|
9514
|
+
let exists = false;
|
|
9515
|
+
try {
|
|
9516
|
+
exists = (await stat(persistedPath)).isFile();
|
|
9517
|
+
} catch {
|
|
9518
|
+
exists = false;
|
|
9519
|
+
}
|
|
9520
|
+
if (!exists) try {
|
|
9521
|
+
await writeAtomic(persistedPath, text);
|
|
9522
|
+
} catch {
|
|
9523
|
+
continue;
|
|
9524
|
+
}
|
|
9525
|
+
turn.content[i] = {
|
|
9526
|
+
...block,
|
|
9527
|
+
output: buildPersistedStub({
|
|
9528
|
+
toolName: toolNameByCallId.get(block.callId) ?? "tool",
|
|
9529
|
+
originalBytes: bytes,
|
|
9530
|
+
persistedPath,
|
|
9531
|
+
output: text,
|
|
9532
|
+
previewBytes: ELIDE_PERSIST_PREVIEW_BYTES
|
|
9533
|
+
})
|
|
9534
|
+
};
|
|
9535
|
+
result.releasedBlocks++;
|
|
9536
|
+
result.releasedBytes += bytes;
|
|
9537
|
+
}
|
|
9538
|
+
return result;
|
|
9539
|
+
}
|
|
9540
|
+
/**
|
|
9541
|
+
* Re-home `<persisted-output …>` stubs when turns are copied into a NEW
|
|
9542
|
+
* session (fork): released blob paths are per-session
|
|
9543
|
+
* (`resolvePersistDir` keys on the session id), so a fork that keeps the
|
|
9544
|
+
* origin's paths loses its content the moment the origin session is
|
|
9545
|
+
* deleted ({@link cleanupPersistedSession} removes the origin's blob dir).
|
|
9546
|
+
* Copies each referenced blob under `fromPersistDir` into the same
|
|
9547
|
+
* relative location under `toPersistDir` and rewrites the stub's `path`
|
|
9548
|
+
* attribute. Copy-on-write: input turns are never mutated (they are
|
|
9549
|
+
* usually shared with the origin session's live `data.turns`); a new turn
|
|
9550
|
+
* object is returned only where a stub was rewritten. A missing/uncopyable
|
|
9551
|
+
* blob leaves that stub pointing at the origin — no worse than before.
|
|
9552
|
+
*
|
|
9553
|
+
* Prefix matching happens on the XML-escaped attribute text: `escapeXml`
|
|
9554
|
+
* maps characters independently, so `escapeXml(a + b)` ===
|
|
9555
|
+
* `escapeXml(a) + escapeXml(b)` and a prefix match on escaped text is
|
|
9556
|
+
* exact. Blob filenames are `SAFE_CALL_ID`-constrained (`[\w.-]`), so the
|
|
9557
|
+
* relative remainder is escape-invariant.
|
|
9558
|
+
*/
|
|
9559
|
+
async function rehomePersistedStubs(turns, fromPersistDir, toPersistDir) {
|
|
9560
|
+
if (!isAbsolute(fromPersistDir) || !isAbsolute(toPersistDir) || fromPersistDir === toPersistDir) return [...turns];
|
|
9561
|
+
const fromPrefix = `path="${escapeXml(fromPersistDir)}/`;
|
|
9562
|
+
const out = [];
|
|
9563
|
+
for (const turn of turns) {
|
|
9564
|
+
let content;
|
|
9565
|
+
for (let i = 0; i < turn.content.length; i++) {
|
|
9566
|
+
const block = turn.content[i];
|
|
9567
|
+
if (block.type !== "tool_result" || typeof block.output !== "string" || !block.output.startsWith("<persisted-output tool=\"")) continue;
|
|
9568
|
+
const start = block.output.indexOf(fromPrefix);
|
|
9569
|
+
if (start === -1) continue;
|
|
9570
|
+
const restStart = start + fromPrefix.length;
|
|
9571
|
+
const end = block.output.indexOf("\"", restStart);
|
|
9572
|
+
if (end === -1) continue;
|
|
9573
|
+
const relative = block.output.slice(restStart, end);
|
|
9574
|
+
if (relative.includes("..")) continue;
|
|
9575
|
+
try {
|
|
9576
|
+
const target = join(toPersistDir, relative);
|
|
9577
|
+
await mkdir(dirname(target), { recursive: true });
|
|
9578
|
+
await copyFile(join(fromPersistDir, relative), target);
|
|
9579
|
+
} catch {
|
|
9580
|
+
continue;
|
|
9581
|
+
}
|
|
9582
|
+
content ??= [...turn.content];
|
|
9583
|
+
content[i] = {
|
|
9584
|
+
...block,
|
|
9585
|
+
output: `${block.output.slice(0, start)}path="${escapeXml(toPersistDir)}/${relative}${block.output.slice(end)}`
|
|
9586
|
+
};
|
|
9587
|
+
}
|
|
9588
|
+
out.push(content ? {
|
|
9589
|
+
...turn,
|
|
9590
|
+
content
|
|
9591
|
+
} : turn);
|
|
9592
|
+
}
|
|
9593
|
+
return out;
|
|
9594
|
+
}
|
|
9595
|
+
/**
|
|
9397
9596
|
* Write-then-rename for atomicity — delegates to the shared
|
|
9398
9597
|
* {@link writeFileAtomicAsync} helper (unique pid+counter tmp suffix,
|
|
9399
9598
|
* fsync before rename, tmp cleanup on failure). Creates the parent
|
|
@@ -9748,6 +9947,24 @@ function applyCompactSummaryCutoff(turns) {
|
|
|
9748
9947
|
const COMPACTION_STUB = "[…elided: older tool output trimmed to fit the context budget.]";
|
|
9749
9948
|
const COMPACTION_STUB_BYTES = toolOutputByteLength(COMPACTION_STUB);
|
|
9750
9949
|
const COMPACT_CHUNK_TURNS = 8;
|
|
9950
|
+
/**
|
|
9951
|
+
* Cross-turn memo for `toolOutputBudgetByteLength`, keyed on the tool_result
|
|
9952
|
+
* block object. The wire pipeline preserves block references for unchanged
|
|
9953
|
+
* blocks (sanitize/alias/elision copy only what they touch), so the same
|
|
9954
|
+
* block objects flow through the byte-accounting loops on every turn of a
|
|
9955
|
+
* run. Without the memo, tail compaction re-measures the ENTIRE transcript's
|
|
9956
|
+
* tool output each turn — O(total transcript bytes) per turn, O(n²) per run.
|
|
9957
|
+
* Blocks are immutable by pipeline convention; a transform that changes an
|
|
9958
|
+
* output produces a new object, which simply misses the memo once.
|
|
9959
|
+
*/
|
|
9960
|
+
const toolResultBudgetBytesMemo = /* @__PURE__ */ new WeakMap();
|
|
9961
|
+
function memoizedToolResultBudgetBytes(block) {
|
|
9962
|
+
const hit = toolResultBudgetBytesMemo.get(block);
|
|
9963
|
+
if (hit !== void 0) return hit;
|
|
9964
|
+
const bytes = toolOutputBudgetByteLength(block.output);
|
|
9965
|
+
toolResultBudgetBytesMemo.set(block, bytes);
|
|
9966
|
+
return bytes;
|
|
9967
|
+
}
|
|
9751
9968
|
function applyTailCompaction(messages, threshold, keepTurns, options) {
|
|
9752
9969
|
const elidedReadPaths = [];
|
|
9753
9970
|
const persistableElided = [];
|
|
@@ -9759,7 +9976,7 @@ function applyTailCompaction(messages, threshold, keepTurns, options) {
|
|
|
9759
9976
|
persistableElided
|
|
9760
9977
|
};
|
|
9761
9978
|
let totalBytes = 0;
|
|
9762
|
-
for (const msg of messages) for (const block of msg.content) if (block.type === "tool_result") totalBytes +=
|
|
9979
|
+
for (const msg of messages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += memoizedToolResultBudgetBytes(block);
|
|
9763
9980
|
if (totalBytes <= threshold) return {
|
|
9764
9981
|
messages,
|
|
9765
9982
|
elidedReadPaths,
|
|
@@ -9775,7 +9992,7 @@ function applyTailCompaction(messages, threshold, keepTurns, options) {
|
|
|
9775
9992
|
let keptBytes = 0;
|
|
9776
9993
|
let budgetCutoff = floorCutoff;
|
|
9777
9994
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
9778
|
-
for (const block of messages[i].content) if (block.type === "tool_result") keptBytes +=
|
|
9995
|
+
for (const block of messages[i].content) if (block.type === "tool_result") keptBytes += memoizedToolResultBudgetBytes(block);
|
|
9779
9996
|
if (keptBytes > threshold) {
|
|
9780
9997
|
budgetCutoff = i + 1;
|
|
9781
9998
|
break;
|
|
@@ -10397,11 +10614,58 @@ async function runLoop(ctx) {
|
|
|
10397
10614
|
unregisterTtftTool();
|
|
10398
10615
|
}
|
|
10399
10616
|
}
|
|
10617
|
+
/**
|
|
10618
|
+
* Per-run LRU for resolved content-ref payloads, keyed by `type:ref`.
|
|
10619
|
+
*
|
|
10620
|
+
* Refs are content-addressed (`attachment://<hash>` and persist-dir blobs are
|
|
10621
|
+
* immutable once written), so within a run the same ref always resolves to the
|
|
10622
|
+
* same bytes — but the wire pipeline re-resolves EVERY ref in the history on
|
|
10623
|
+
* EVERY turn (resolution runs before media age-out, which then discards most
|
|
10624
|
+
* of the resolved base64 again). Without this cache an image-heavy session
|
|
10625
|
+
* re-reads and re-base64s each attachment from disk once per turn, O(refs)
|
|
10626
|
+
* disk I/O per turn → O(turns × refs) per run.
|
|
10627
|
+
*
|
|
10628
|
+
* Keyed on the LoopContext via WeakMap so the cache dies with the run and no
|
|
10629
|
+
* cross-run staleness question arises. Byte-bounded (UTF-8 bytes, measured
|
|
10630
|
+
* once at insert — `.length` counts UTF-16 code units and would let the
|
|
10631
|
+
* budget retain up to 2× its nominal size on multi-byte text), evicting
|
|
10632
|
+
* oldest-first (Map insertion order; hits re-insert to refresh recency).
|
|
10633
|
+
*/
|
|
10634
|
+
const CONTENT_REF_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
10635
|
+
const contentRefCaches = /* @__PURE__ */ new WeakMap();
|
|
10400
10636
|
async function resolveContentRefsForWire(ctx, messages) {
|
|
10401
10637
|
return resolveContentRefsInMessages(messages, { resolveContentRef: async (block) => {
|
|
10638
|
+
const key = `${block.type}:${block.ref}`;
|
|
10639
|
+
let cache = contentRefCaches.get(ctx);
|
|
10640
|
+
if (!cache) {
|
|
10641
|
+
cache = {
|
|
10642
|
+
bytes: 0,
|
|
10643
|
+
entries: /* @__PURE__ */ new Map()
|
|
10644
|
+
};
|
|
10645
|
+
contentRefCaches.set(ctx, cache);
|
|
10646
|
+
}
|
|
10647
|
+
const hit = cache.entries.get(key);
|
|
10648
|
+
if (hit !== void 0) {
|
|
10649
|
+
cache.entries.delete(key);
|
|
10650
|
+
cache.entries.set(key, hit);
|
|
10651
|
+
return { data: hit.data };
|
|
10652
|
+
}
|
|
10402
10653
|
const hookCtx = { block };
|
|
10403
10654
|
await ctx.hooks.callHook("content-ref:resolve", hookCtx);
|
|
10404
10655
|
if (typeof hookCtx.data !== "string") throw new Error(`Content ref "${block.ref}" (${block.type}) was not resolved by a content-ref:resolve hook.`);
|
|
10656
|
+
const dataBytes = Buffer.byteLength(hookCtx.data, "utf8");
|
|
10657
|
+
if (dataBytes <= CONTENT_REF_CACHE_MAX_BYTES) {
|
|
10658
|
+
cache.entries.set(key, {
|
|
10659
|
+
data: hookCtx.data,
|
|
10660
|
+
bytes: dataBytes
|
|
10661
|
+
});
|
|
10662
|
+
cache.bytes += dataBytes;
|
|
10663
|
+
for (const [k, v] of cache.entries) {
|
|
10664
|
+
if (cache.bytes <= CONTENT_REF_CACHE_MAX_BYTES) break;
|
|
10665
|
+
cache.entries.delete(k);
|
|
10666
|
+
cache.bytes -= v.bytes;
|
|
10667
|
+
}
|
|
10668
|
+
}
|
|
10405
10669
|
return { data: hookCtx.data };
|
|
10406
10670
|
} });
|
|
10407
10671
|
}
|
|
@@ -10843,7 +11107,8 @@ function resolveAutoCompact(raw, opts = {}) {
|
|
|
10843
11107
|
restore: cfg.restore !== false,
|
|
10844
11108
|
maxFailures: typeof cfg.maxFailures === "number" && Number.isFinite(cfg.maxFailures) && cfg.maxFailures > 0 ? Math.floor(cfg.maxFailures) : DEFAULT_AUTO_COMPACT_MAX_FAILURES,
|
|
10845
11109
|
provider: cfg.provider ?? null,
|
|
10846
|
-
model: typeof cfg.model === "string" && cfg.model.length > 0 ? cfg.model : null
|
|
11110
|
+
model: typeof cfg.model === "string" && cfg.model.length > 0 ? cfg.model : null,
|
|
11111
|
+
releaseReplacedTurns: cfg.releaseReplacedTurns === true
|
|
10847
11112
|
};
|
|
10848
11113
|
}
|
|
10849
11114
|
/**
|
|
@@ -10945,6 +11210,11 @@ async function maybeAutoCompact(ctx, turn, lastTurnUsage, state) {
|
|
|
10945
11210
|
}
|
|
10946
11211
|
ctx.turns.push(summaryTurn, ...restorationTurns);
|
|
10947
11212
|
await ctx.persistTurns?.();
|
|
11213
|
+
if (cfg.releaseReplacedTurns && ctx.persistDir && result.summarizedTurnIds.length > 0) try {
|
|
11214
|
+
await releaseReplacedTurnContent(ctx.turns, new Set(result.summarizedTurnIds), { persistDir: ctx.persistDir });
|
|
11215
|
+
} catch (err) {
|
|
11216
|
+
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/loop] post-compact turn release failed: ${errorMessage(err)}\n`);
|
|
11217
|
+
}
|
|
10948
11218
|
const absorbedIds = /* @__PURE__ */ new Set([...result.summarizedTurnIds, ...result.droppedDueToPtl]);
|
|
10949
11219
|
const onWireReads = new Set(readFilePathsInTurns(applyCompactSummaryCutoff(ctx.turns)));
|
|
10950
11220
|
markReadStateForElidedPaths(ctx, ctx.handle.cwd, readFilePathsInTurns(ctx.turns, absorbedIds).filter((p) => !onWireReads.has(p)));
|
|
@@ -11092,10 +11362,12 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
11092
11362
|
}
|
|
11093
11363
|
const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps);
|
|
11094
11364
|
let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages);
|
|
11365
|
+
let callMapsMemo;
|
|
11366
|
+
const callMaps = () => callMapsMemo ??= collectCallMaps(canonicalMessages);
|
|
11095
11367
|
if (ctx.compactStrategy === "tail") {
|
|
11096
11368
|
const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
|
|
11097
11369
|
const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
|
|
11098
|
-
const { readPathByCallId, toolNameByCallId } =
|
|
11370
|
+
const { readPathByCallId, toolNameByCallId } = callMaps();
|
|
11099
11371
|
const compacted = applyTailCompaction(sanitizedMessages, threshold, keep, {
|
|
11100
11372
|
readPathByCallId,
|
|
11101
11373
|
toolNameByCallId,
|
|
@@ -11113,7 +11385,7 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
11113
11385
|
const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
|
|
11114
11386
|
const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
|
|
11115
11387
|
let totalBytes = 0;
|
|
11116
|
-
for (const msg of sanitizedMessages) for (const block of msg.content) if (block.type === "tool_result") totalBytes +=
|
|
11388
|
+
for (const msg of sanitizedMessages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += memoizedToolResultBudgetBytes(block);
|
|
11117
11389
|
try {
|
|
11118
11390
|
const compacted = await ctx.compactStrategy(sanitizedMessages, {
|
|
11119
11391
|
threshold,
|
|
@@ -11126,7 +11398,7 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
11126
11398
|
}
|
|
11127
11399
|
}
|
|
11128
11400
|
if (typeof ctx.mediaKeepTurns === "number" && ctx.mediaKeepTurns >= 0 && ctx.tools.read_file) {
|
|
11129
|
-
const { readPathByCallId } =
|
|
11401
|
+
const { readPathByCallId } = callMaps();
|
|
11130
11402
|
const aged = applyMediaAgeout(sanitizedMessages, ctx.mediaKeepTurns, {
|
|
11131
11403
|
chunkTurns: COMPACT_CHUNK_TURNS,
|
|
11132
11404
|
readPathByCallId,
|
|
@@ -11256,6 +11528,7 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
11256
11528
|
turnId,
|
|
11257
11529
|
attempt,
|
|
11258
11530
|
nextAttempt: attempt + 1,
|
|
11531
|
+
maxAttempts: retryCfg.maxAttempts,
|
|
11259
11532
|
delayMs,
|
|
11260
11533
|
err: caught,
|
|
11261
11534
|
...meta
|
|
@@ -11271,7 +11544,7 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
11271
11544
|
}
|
|
11272
11545
|
}
|
|
11273
11546
|
if (!wasAborted && classified?.kind === "context_exceeded" && cleanSlate && overflowRecoveries < CONTEXT_OVERFLOW_KEEP_TURNS.length) {
|
|
11274
|
-
const { readPathByCallId, toolNameByCallId } =
|
|
11547
|
+
const { readPathByCallId, toolNameByCallId } = callMaps();
|
|
11275
11548
|
let recoveredMessages;
|
|
11276
11549
|
const elidedReadPaths = [];
|
|
11277
11550
|
for (let k = overflowRecoveries; k < CONTEXT_OVERFLOW_KEEP_TURNS.length; k++) {
|
|
@@ -12995,6 +13268,7 @@ const HOOK_EVENT_SET = /* @__PURE__ */ new Set([
|
|
|
12995
13268
|
"child:stream:server_tool_use",
|
|
12996
13269
|
"child:stream:server_tool_result",
|
|
12997
13270
|
"child:stream:error",
|
|
13271
|
+
"child:stream:retry",
|
|
12998
13272
|
"child:tool:gate",
|
|
12999
13273
|
"child:mcp:tool:gate",
|
|
13000
13274
|
"child:tool:dispatched",
|
|
@@ -13474,10 +13748,10 @@ function installLazyDisclosureGate(hooks, lazyCanonicalNames, unlocked, discover
|
|
|
13474
13748
|
* (`via: 'resume'`) for skills it newly activates. Shared by the run-start
|
|
13475
13749
|
* resume rehydration and the mid-loop durable-replay gate below.
|
|
13476
13750
|
*/
|
|
13477
|
-
async function rehydrateActiveSkillsFromTurns(
|
|
13751
|
+
async function rehydrateActiveSkillsFromTurns(turns, resolvedSkills, state, hooks) {
|
|
13478
13752
|
const skillsByName = new Map(resolvedSkills.map((s) => [s.name, s]));
|
|
13479
13753
|
const lastModeBySkill = /* @__PURE__ */ new Map();
|
|
13480
|
-
for (const turn of
|
|
13754
|
+
for (const turn of turns) {
|
|
13481
13755
|
if (turn.role !== "assistant") continue;
|
|
13482
13756
|
for (const block of turn.content) {
|
|
13483
13757
|
if (block.type !== "tool_call" || block.name !== "skills_use") continue;
|
|
@@ -13523,7 +13797,7 @@ function installSkillsActivationHydrationGate(hooks, deps) {
|
|
|
13523
13797
|
const skillName = typeof ctx.input?.name === "string" ? ctx.input.name : void 0;
|
|
13524
13798
|
if (!skillName || deps.state.isActive(skillName)) return;
|
|
13525
13799
|
try {
|
|
13526
|
-
await rehydrateActiveSkillsFromTurns(session, resolvedSkills, deps.state, hooks);
|
|
13800
|
+
await rehydrateActiveSkillsFromTurns(session.turns, resolvedSkills, deps.state, hooks);
|
|
13527
13801
|
} catch {}
|
|
13528
13802
|
});
|
|
13529
13803
|
}
|
|
@@ -13941,7 +14215,8 @@ function createAgent(rawOptions) {
|
|
|
13941
14215
|
if (options.resumeRunId) {
|
|
13942
14216
|
if (!session) throw new Error("resumeRunId requires a session (the prior run's turns live there)");
|
|
13943
14217
|
if (!options.prompt) throw new Error("resumeRunId requires a prompt — pass the continuation instruction");
|
|
13944
|
-
|
|
14218
|
+
const chain = resumeChainRunIds(session.runs, options.resumeRunId);
|
|
14219
|
+
if (!session.turns.some((t) => t.runId !== void 0 && chain.has(t.runId))) throw new Error(`resumeRunId "${options.resumeRunId}" has no persisted turns in this session — nothing to resume`);
|
|
13945
14220
|
}
|
|
13946
14221
|
const clock = options.clock ?? agentClock ?? DEFAULT_AGENT_CLOCK;
|
|
13947
14222
|
let externalAbortListener;
|
|
@@ -13963,7 +14238,8 @@ function createAgent(rawOptions) {
|
|
|
13963
14238
|
session?.startRun(runId, promptLabel, {
|
|
13964
14239
|
...options.parentRunId ? { parentRunId: options.parentRunId } : {},
|
|
13965
14240
|
depth: typeof options.depth === "number" ? options.depth : 0,
|
|
13966
|
-
...options.resumeRunId ? { resumedFromRunId: options.resumeRunId } : {}
|
|
14241
|
+
...options.resumeRunId ? { resumedFromRunId: options.resumeRunId } : {},
|
|
14242
|
+
...options.runLabel ? { label: options.runLabel } : {}
|
|
13967
14243
|
});
|
|
13968
14244
|
const sessionRun = session?.runs.find((r) => r.id === runId);
|
|
13969
14245
|
if (sessionRun) sessionRun.startedAt = runStartedAt;
|
|
@@ -14022,7 +14298,14 @@ function createAgent(rawOptions) {
|
|
|
14022
14298
|
});
|
|
14023
14299
|
}
|
|
14024
14300
|
});
|
|
14025
|
-
if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0)
|
|
14301
|
+
if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0) {
|
|
14302
|
+
let rehydrationTurns = session.turns;
|
|
14303
|
+
if (options.resumeRunId) {
|
|
14304
|
+
const chain = resumeChainRunIds(session.runs, options.resumeRunId);
|
|
14305
|
+
rehydrationTurns = session.turns.filter((t) => t.runId !== void 0 && chain.has(t.runId));
|
|
14306
|
+
}
|
|
14307
|
+
await rehydrateActiveSkillsFromTurns(rehydrationTurns, resolvedSkills, skillActivationState, hooks);
|
|
14308
|
+
}
|
|
14026
14309
|
const thinking = options.thinking ?? "off";
|
|
14027
14310
|
const model = targets[0].model;
|
|
14028
14311
|
if (runProvider.meta?.clearsContextServerSide) resolvedBehavior.dedupReads = false;
|
|
@@ -14191,8 +14474,10 @@ function createAgent(rawOptions) {
|
|
|
14191
14474
|
const turns = [];
|
|
14192
14475
|
const isResume = session && session.turns.length > 0 && (session.runs.length > 0 || !options.prompt) && !options.parentRunId && !options.resumeRunId;
|
|
14193
14476
|
let seededTurns;
|
|
14194
|
-
if (options.resumeRunId && session)
|
|
14195
|
-
|
|
14477
|
+
if (options.resumeRunId && session) {
|
|
14478
|
+
const chain = resumeChainRunIds(session.runs, options.resumeRunId);
|
|
14479
|
+
seededTurns = filterUnresolvedToolUses(session.turns.filter((t) => t.runId !== void 0 && chain.has(t.runId)));
|
|
14480
|
+
} else if (isResume) {
|
|
14196
14481
|
const childRunIds = childRunIdSet(session);
|
|
14197
14482
|
const resumed = childRunIds.size === 0 ? session.turns : session.turns.filter((t) => !t.runId || !childRunIds.has(t.runId));
|
|
14198
14483
|
seededTurns = resumeFilteredTurns && resumed === session.turns ? resumeFilteredTurns : filterUnresolvedToolUses(resumed);
|
|
@@ -15066,6 +15351,6 @@ function createAgent(rawOptions) {
|
|
|
15066
15351
|
};
|
|
15067
15352
|
}
|
|
15068
15353
|
//#endregion
|
|
15069
|
-
export {
|
|
15354
|
+
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 };
|
|
15070
15355
|
|
|
15071
|
-
//# sourceMappingURL=agent
|
|
15356
|
+
//# sourceMappingURL=agent--VPMZ0nT.js.map
|