zidane 5.11.0 → 5.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/{agent-B7ilLoh8.d.ts → agent-D0W9yClt.d.ts} +52 -24
- package/dist/agent-D0W9yClt.d.ts.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +7 -7
- package/dist/chat.js +2 -2
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/eval.js.map +1 -1
- package/dist/{headless-CYHU4ZB-.js → headless-Bb5gU8AR.js} +5 -5
- package/dist/{headless-CYHU4ZB-.js.map → headless-Bb5gU8AR.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-CsRdmSh6.d.ts → index-D60tX5XC.d.ts} +2 -2
- package/dist/{index-CsRdmSh6.d.ts.map → index-D60tX5XC.d.ts.map} +1 -1
- package/dist/{index-C-zEbeFI.d.ts → index-DZR99FD4.d.ts} +19 -12
- package/dist/{index-C-zEbeFI.d.ts.map → index-DZR99FD4.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/{logger-Bd4Wn9Hc.d.ts → logger-n4LsLISE.d.ts} +2 -2
- package/dist/{logger-Bd4Wn9Hc.d.ts.map → logger-n4LsLISE.d.ts.map} +1 -1
- package/dist/{login-DVLJzf-S.js → login-BHhOdTp9.js} +3 -3
- package/dist/{login-DVLJzf-S.js.map → login-BHhOdTp9.js.map} +1 -1
- package/dist/{mcp-DPneQbw2.js → mcp-Cy9mgCcr.js} +2 -2
- package/dist/mcp-Cy9mgCcr.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-DJ7xdoUg.js → messages-RPKrEPvH.js} +2 -2
- package/dist/messages-RPKrEPvH.js.map +1 -0
- package/dist/output/stream-json.d.ts +2 -2
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +2 -2
- package/dist/{presets-Q0yUDRA6.js → presets-D5ibZTml.js} +2 -2
- package/dist/{presets-Q0yUDRA6.js.map → presets-D5ibZTml.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-BPdi7cUU.js → providers-C2cxujp_.js} +14 -11
- package/dist/providers-C2cxujp_.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-D4GckETs.js → session-Do_TQV7c.js} +3 -3
- package/dist/session-Do_TQV7c.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/{tool-formatters-DXO8rGut.d.ts → tool-formatters-RT5-gyE2.d.ts} +2 -2
- package/dist/{tool-formatters-DXO8rGut.d.ts.map → tool-formatters-RT5-gyE2.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-DqNkGwfd.js → tools-ZHKOh44k.js} +266 -54
- package/dist/tools-ZHKOh44k.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-C3tVPR5n.js → transcript-anchors-0zzqcSm5.js} +111 -9
- package/dist/transcript-anchors-0zzqcSm5.js.map +1 -0
- package/dist/{transcript-anchors-p-ZsBSEF.d.ts → transcript-anchors-B4FxkG-8.d.ts} +4 -4
- package/dist/{transcript-anchors-p-ZsBSEF.d.ts.map → transcript-anchors-B4FxkG-8.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +24 -8
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DD1D5VFx.d.ts → turn-operations-CoRj3mYZ.d.ts} +3 -3
- package/dist/{turn-operations-DD1D5VFx.d.ts.map → turn-operations-CoRj3mYZ.d.ts.map} +1 -1
- package/dist/types-BiobHM1D.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CHAT.md +3 -3
- package/docs/SKILL.md +3 -3
- package/package.json +1 -1
- package/dist/agent-B7ilLoh8.d.ts.map +0 -1
- package/dist/mcp-DPneQbw2.js.map +0 -1
- package/dist/messages-DJ7xdoUg.js.map +0 -1
- package/dist/providers-BPdi7cUU.js.map +0 -1
- package/dist/session-D4GckETs.js.map +0 -1
- package/dist/tools-DqNkGwfd.js.map +0 -1
- package/dist/transcript-anchors-C3tVPR5n.js.map +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { r as utf8ByteLength } from "./utils-ngQzYzZD.js";
|
|
2
2
|
import { t as buildContextBreakdown } from "./context-breakdown-kO-pDsay.js";
|
|
3
3
|
import { a as formatTaskStatus, i as formatDuration, o as formatTaskSummary, s as previewLine } from "./format-BNOXpl-1.js";
|
|
4
|
-
import { a as createCursorOAuthProvider, c as baseten, d as anthropic, g as FAST_MODE_OPTIONS, h as ANTHROPIC_EXTRA_MODELS, m as writeFileAtomicAsync, n as openai, o as generatePkce, r as local, s as cerebras, t as openrouter, u as arcee } from "./providers-
|
|
4
|
+
import { a as createCursorOAuthProvider, c as baseten, d as anthropic, g as FAST_MODE_OPTIONS, h as ANTHROPIC_EXTRA_MODELS, m as writeFileAtomicAsync, n as openai, o as generatePkce, r as local, s as cerebras, t as openrouter, u as arcee } from "./providers-C2cxujp_.js";
|
|
5
5
|
import { i as AgentProviderError, l as errorMessage, n as AgentBudgetExceededError, o as AgentToolPairingError, p as toTypedError, t as AgentAbortedError } from "./errors-DkR6GPJw.js";
|
|
6
|
-
import { A as renderSystemForWire, D as appendStaticSection, a as detectTurnInterruption, c as filterUnresolvedToolUses, d as remintDuplicateToolCallIds, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureEndsWithUserMessage, s as ensureToolResultPairing } from "./messages-
|
|
6
|
+
import { A as renderSystemForWire, D as appendStaticSection, a as detectTurnInterruption, c as filterUnresolvedToolUses, d as remintDuplicateToolCallIds, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureEndsWithUserMessage, s as ensureToolResultPairing } from "./messages-RPKrEPvH.js";
|
|
7
7
|
import { a as toolResultToText, i as toolOutputByteLength, n as documentBlockMarker, r as toolOutputBudgetByteLength, t as DEFAULT_AGENT_CLOCK } from "./types-BiobHM1D.js";
|
|
8
8
|
import { t as reconcileImageMediaType } from "./image-sniff-B7uFSNO1.js";
|
|
9
9
|
import { r as createProcessContext, t as resolveDetachedTasksCapability } from "./contexts-DglWSzmR.js";
|
|
10
10
|
import { i as styleReplacementForVia, n as resolveOldString, r as stripLineNumberPrefixes, t as describeVia } from "./edit-utils-EGosADZq.js";
|
|
11
11
|
import { a as markReadStateElided, n as getToolDedupState, o as readStateKey, r as hashContent, s as resolveReadStateMap } from "./read-state-BFqpQRc5.js";
|
|
12
12
|
import { S as escapeXml, d as buildCatalog, n as stripShellInterpolations, p as installAllowedToolsGate, r as resolveSkills, t as interpolateShellCommands, v as validateResourcePathReal, x as createSkillActivationState } from "./interpolate-CTfr0GdR.js";
|
|
13
|
-
import { n as connectMcpServers } from "./mcp-
|
|
13
|
+
import { n as connectMcpServers } from "./mcp-Cy9mgCcr.js";
|
|
14
14
|
import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
|
|
15
15
|
import { n as shellQuote, t as alwaysQuote } from "./shell-quote-BmnhZmdM.js";
|
|
16
16
|
import { isAbsolute, join, resolve } from "node:path";
|
|
@@ -83,8 +83,8 @@ function toCanonicalName(wire, maps) {
|
|
|
83
83
|
* Why: SDK consumers and tool descriptions across the ecosystem
|
|
84
84
|
* inconsistently reference one form or the other (Anthropic's docs use
|
|
85
85
|
* double, zidane uses single). A Claude-Code-trained model emitting
|
|
86
|
-
* `
|
|
87
|
-
* `
|
|
86
|
+
* `mcp__acme__apply_migration` against a server zidane registered as
|
|
87
|
+
* `mcp_acme_apply_migration` would otherwise trip the `tool:unknown`
|
|
88
88
|
* path and waste a turn on the correction.
|
|
89
89
|
*
|
|
90
90
|
* Idempotent: a canonical that already has a double-underscore alias
|
|
@@ -1404,6 +1404,14 @@ function installDedupToolsGate(hooks, getDedupTools, getSession) {
|
|
|
1404
1404
|
*/
|
|
1405
1405
|
const PERSISTENCE_PREVIEW_BYTES = 2 * 1024;
|
|
1406
1406
|
/**
|
|
1407
|
+
* Preview size for persist-on-elide stubs. Much smaller than
|
|
1408
|
+
* {@link PERSISTENCE_PREVIEW_BYTES}: this content is being COMPACTED OUT (it's
|
|
1409
|
+
* old), so the stub only needs a hint plus the recovery path, not a full
|
|
1410
|
+
* preview. Keeping it small stops the elided region from ballooning ~40× over
|
|
1411
|
+
* the lossy stub it replaces while still being recoverable by reading `path`.
|
|
1412
|
+
*/
|
|
1413
|
+
const ELIDE_PERSIST_PREVIEW_BYTES = 256;
|
|
1414
|
+
/**
|
|
1407
1415
|
* Byte-stable prefix every {@link buildPersistedStub} output starts with.
|
|
1408
1416
|
* Exported so wire-level passes (tail compaction, future stale-output
|
|
1409
1417
|
* elision) can recognize a persisted stub and preserve its path attribute
|
|
@@ -1633,7 +1641,7 @@ async function enforcePersistDirCap(persistDir, maxBytes, opts = {}) {
|
|
|
1633
1641
|
* surface.
|
|
1634
1642
|
*/
|
|
1635
1643
|
function buildPersistedStub(input) {
|
|
1636
|
-
const { slice: previewSlice, bytes: previewBytes } = sliceFirstBytes(input.output,
|
|
1644
|
+
const { slice: previewSlice, bytes: previewBytes } = sliceFirstBytes(input.output, input.previewBytes ?? 2048);
|
|
1637
1645
|
const previewMarker = previewSlice.length < input.output.length ? `\n…(${input.originalBytes - previewBytes} more bytes in persisted file)` : "";
|
|
1638
1646
|
return [
|
|
1639
1647
|
`${PERSISTED_STUB_PREFIX}${escapeXml(input.toolName)}" bytes="${input.originalBytes}" path="${escapeXml(input.persistedPath)}">`,
|
|
@@ -1663,6 +1671,68 @@ async function cleanupPersistedSession(persistRoot) {
|
|
|
1663
1671
|
}
|
|
1664
1672
|
}
|
|
1665
1673
|
/**
|
|
1674
|
+
* Persist-on-elide: belatedly write tool results that tail compaction is
|
|
1675
|
+
* about to replace with the lossy `[…elided…]` stub, swapping in a
|
|
1676
|
+
* recoverable `<persisted-output … path=…>` stub instead. This is the lazy
|
|
1677
|
+
* counterpart to {@link maybePersistToolResult}'s eager (emit-time)
|
|
1678
|
+
* persistence — it only fires when a result is actually being compacted out,
|
|
1679
|
+
* so recent results stay full inline while OLD ones become recoverable rather
|
|
1680
|
+
* than destroyed. Closes the context-loss gap for non-read results between
|
|
1681
|
+
* the lossy stub and re-runnable side effects: after this, a compacted result
|
|
1682
|
+
* is always retrievable by `read_file`-ing its path.
|
|
1683
|
+
*
|
|
1684
|
+
* `items` are the {@link TailCompactionResult.persistableElided} entries (the
|
|
1685
|
+
* caller has already filtered out reads — which recover via the read-state
|
|
1686
|
+
* dedup re-serve — and results too small to be worth persisting). Each
|
|
1687
|
+
* `output` is the ORIGINAL content captured before the lossy stub replaced it.
|
|
1688
|
+
*
|
|
1689
|
+
* Idempotency: blobs are immutable (a tool result never changes), so we write
|
|
1690
|
+
* `<callId>.txt` only when it's absent and otherwise just rebuild the
|
|
1691
|
+
* byte-stable stub. The session keeps the original content; only the wire copy
|
|
1692
|
+
* carries the stub — consistent with compaction being wire-only. Returns the
|
|
1693
|
+
* messages with the lossy stubs swapped; unchanged when nothing was persisted.
|
|
1694
|
+
*/
|
|
1695
|
+
async function persistElidedToolResults(messages, items, opts) {
|
|
1696
|
+
if (items.length === 0 || !isAbsolute(opts.persistDir)) return messages;
|
|
1697
|
+
const stubByCallId = /* @__PURE__ */ new Map();
|
|
1698
|
+
let wroteAny = false;
|
|
1699
|
+
for (const item of items) {
|
|
1700
|
+
if (!SAFE_CALL_ID.test(item.callId) || item.callId.includes("..")) continue;
|
|
1701
|
+
const persistedPath = join(opts.persistDir, `${item.callId}.txt`);
|
|
1702
|
+
let exists = false;
|
|
1703
|
+
try {
|
|
1704
|
+
exists = (await stat(persistedPath)).isFile();
|
|
1705
|
+
} catch {
|
|
1706
|
+
exists = false;
|
|
1707
|
+
}
|
|
1708
|
+
if (!exists) try {
|
|
1709
|
+
await writeAtomic(persistedPath, item.output);
|
|
1710
|
+
wroteAny = true;
|
|
1711
|
+
} catch {
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
stubByCallId.set(item.callId, buildPersistedStub({
|
|
1715
|
+
toolName: item.toolName,
|
|
1716
|
+
originalBytes: toolOutputByteLength(item.output),
|
|
1717
|
+
persistedPath,
|
|
1718
|
+
output: item.output,
|
|
1719
|
+
previewBytes: ELIDE_PERSIST_PREVIEW_BYTES
|
|
1720
|
+
}));
|
|
1721
|
+
}
|
|
1722
|
+
if (wroteAny && typeof opts.maxBytes === "number" && Number.isFinite(opts.maxBytes) && opts.maxBytes > 0) await enforcePersistDirCap(opts.persistDir, opts.maxBytes);
|
|
1723
|
+
if (stubByCallId.size === 0) return messages;
|
|
1724
|
+
return messages.map((msg) => {
|
|
1725
|
+
if (!msg.content.some((b) => b.type === "tool_result" && stubByCallId.has(b.callId))) return msg;
|
|
1726
|
+
return {
|
|
1727
|
+
...msg,
|
|
1728
|
+
content: msg.content.map((b) => b.type === "tool_result" && stubByCallId.has(b.callId) ? {
|
|
1729
|
+
...b,
|
|
1730
|
+
output: stubByCallId.get(b.callId)
|
|
1731
|
+
} : b)
|
|
1732
|
+
};
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1666
1736
|
* Write-then-rename for atomicity — delegates to the shared
|
|
1667
1737
|
* {@link writeFileAtomicAsync} helper (unique pid+counter tmp suffix,
|
|
1668
1738
|
* fsync before rename, tmp cleanup on failure). Creates the parent
|
|
@@ -2276,32 +2346,56 @@ function applyCompactSummaryCutoff(turns) {
|
|
|
2276
2346
|
* alone — those are consumer-controlled and the consumer is responsible for
|
|
2277
2347
|
* matching prompt parts to the provider's capabilities.
|
|
2278
2348
|
*/
|
|
2279
|
-
const COMPACTION_STUB = "[…elided
|
|
2349
|
+
const COMPACTION_STUB = "[…elided: older tool output trimmed to fit the context budget.]";
|
|
2280
2350
|
const COMPACTION_STUB_BYTES = toolOutputByteLength(COMPACTION_STUB);
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
* For Anthropic users, prefer the server-side `context-management-2025-06-27`
|
|
2293
|
-
* beta (token-accurate, no client-side approximation). This function is the
|
|
2294
|
-
* client-side fallback for OpenAI-compatible / OpenRouter / Cerebras runs
|
|
2295
|
-
* against OSS models that lack a server-side equivalent.
|
|
2296
|
-
*/
|
|
2297
|
-
function applyTailCompaction(messages, threshold, keepTurns) {
|
|
2298
|
-
if (messages.length === 0) return messages;
|
|
2351
|
+
const COMPACT_CHUNK_TURNS = 8;
|
|
2352
|
+
function applyTailCompaction(messages, threshold, keepTurns, options) {
|
|
2353
|
+
const elidedReadPaths = [];
|
|
2354
|
+
const persistableElided = [];
|
|
2355
|
+
const toolNameByCallId = options?.toolNameByCallId;
|
|
2356
|
+
const persistElideMinBytes = options?.persistElideMinBytes ?? 0;
|
|
2357
|
+
if (messages.length === 0) return {
|
|
2358
|
+
messages,
|
|
2359
|
+
elidedReadPaths,
|
|
2360
|
+
persistableElided
|
|
2361
|
+
};
|
|
2299
2362
|
let totalBytes = 0;
|
|
2300
2363
|
for (const msg of messages) for (const block of msg.content) if (block.type === "tool_result") totalBytes += toolOutputByteLength(block.output);
|
|
2301
|
-
if (totalBytes <= threshold) return
|
|
2364
|
+
if (totalBytes <= threshold) return {
|
|
2365
|
+
messages,
|
|
2366
|
+
elidedReadPaths,
|
|
2367
|
+
persistableElided
|
|
2368
|
+
};
|
|
2302
2369
|
const keep = Math.max(0, keepTurns);
|
|
2303
|
-
const
|
|
2304
|
-
if (
|
|
2370
|
+
const floorCutoff = messages.length - keep;
|
|
2371
|
+
if (floorCutoff <= 0) return {
|
|
2372
|
+
messages,
|
|
2373
|
+
elidedReadPaths,
|
|
2374
|
+
persistableElided
|
|
2375
|
+
};
|
|
2376
|
+
let keptBytes = 0;
|
|
2377
|
+
let budgetCutoff = floorCutoff;
|
|
2378
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2379
|
+
for (const block of messages[i].content) if (block.type === "tool_result") keptBytes += toolOutputByteLength(block.output);
|
|
2380
|
+
if (keptBytes > threshold) {
|
|
2381
|
+
budgetCutoff = i + 1;
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
const rawCutoff = Math.min(budgetCutoff, floorCutoff);
|
|
2386
|
+
if (rawCutoff <= 0) return {
|
|
2387
|
+
messages,
|
|
2388
|
+
elidedReadPaths,
|
|
2389
|
+
persistableElided
|
|
2390
|
+
};
|
|
2391
|
+
const chunk = Math.max(1, options?.chunkTurns ?? 1);
|
|
2392
|
+
const cutoff = Math.floor(rawCutoff / chunk) * chunk;
|
|
2393
|
+
if (cutoff <= 0) return {
|
|
2394
|
+
messages,
|
|
2395
|
+
elidedReadPaths,
|
|
2396
|
+
persistableElided
|
|
2397
|
+
};
|
|
2398
|
+
const readPathByCallId = options?.readPathByCallId;
|
|
2305
2399
|
let changed = false;
|
|
2306
2400
|
const out = messages.slice();
|
|
2307
2401
|
for (let i = 0; i < cutoff; i++) {
|
|
@@ -2309,10 +2403,18 @@ function applyTailCompaction(messages, threshold, keepTurns) {
|
|
|
2309
2403
|
let msgChanged = false;
|
|
2310
2404
|
const newContent = msg.content.map((block) => {
|
|
2311
2405
|
if (block.type !== "tool_result") return block;
|
|
2312
|
-
|
|
2406
|
+
const existingBytes = toolOutputByteLength(block.output);
|
|
2407
|
+
if (existingBytes <= COMPACTION_STUB_BYTES) return block;
|
|
2313
2408
|
if (typeof block.output === "string" && block.output.startsWith("<persisted-output tool=\"")) return block;
|
|
2314
2409
|
msgChanged = true;
|
|
2315
2410
|
changed = true;
|
|
2411
|
+
const readPath = readPathByCallId?.get(block.callId);
|
|
2412
|
+
if (readPath !== void 0) elidedReadPaths.push(readPath);
|
|
2413
|
+
else if (persistElideMinBytes > 0 && typeof block.output === "string" && existingBytes > persistElideMinBytes) persistableElided.push({
|
|
2414
|
+
callId: block.callId,
|
|
2415
|
+
toolName: toolNameByCallId?.get(block.callId) ?? "tool",
|
|
2416
|
+
output: block.output
|
|
2417
|
+
});
|
|
2316
2418
|
return {
|
|
2317
2419
|
...block,
|
|
2318
2420
|
output: COMPACTION_STUB
|
|
@@ -2323,7 +2425,34 @@ function applyTailCompaction(messages, threshold, keepTurns) {
|
|
|
2323
2425
|
content: newContent
|
|
2324
2426
|
};
|
|
2325
2427
|
}
|
|
2326
|
-
return
|
|
2428
|
+
return {
|
|
2429
|
+
messages: changed ? out : messages,
|
|
2430
|
+
elidedReadPaths,
|
|
2431
|
+
persistableElided
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* Build a call_id → file path map for `read_file` tool calls in `messages`.
|
|
2436
|
+
* Tail compaction uses it to learn which stubbed `tool_result`s were file
|
|
2437
|
+
* reads, so their read-state dedup can be invalidated. Resolve this from the
|
|
2438
|
+
* canonical, pre-alias history — call_ids are stable across the alias rewrite,
|
|
2439
|
+
* so the map still keys the wire-level `tool_result` blocks.
|
|
2440
|
+
*/
|
|
2441
|
+
function collectCallMaps(messages) {
|
|
2442
|
+
const readPathByCallId = /* @__PURE__ */ new Map();
|
|
2443
|
+
const toolNameByCallId = /* @__PURE__ */ new Map();
|
|
2444
|
+
for (const msg of messages) for (const block of msg.content) {
|
|
2445
|
+
if (block.type !== "tool_call") continue;
|
|
2446
|
+
toolNameByCallId.set(block.id, block.name);
|
|
2447
|
+
if (block.name === "read_file") {
|
|
2448
|
+
const path = block.input.path;
|
|
2449
|
+
if (typeof path === "string" && path.length > 0) readPathByCallId.set(block.id, path);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
return {
|
|
2453
|
+
readPathByCallId,
|
|
2454
|
+
toolNameByCallId
|
|
2455
|
+
};
|
|
2327
2456
|
}
|
|
2328
2457
|
/**
|
|
2329
2458
|
* Replace `read_file` `tool_result` blocks with a short stub when a later
|
|
@@ -2342,7 +2471,7 @@ function applyTailCompaction(messages, threshold, keepTurns) {
|
|
|
2342
2471
|
* Pure function, exported for tests and so downstream tooling can preview
|
|
2343
2472
|
* elision without spinning up the loop.
|
|
2344
2473
|
*/
|
|
2345
|
-
const STALE_READ_STUB = "[…elided: file edited later in
|
|
2474
|
+
const STALE_READ_STUB = "[…elided: this file was edited later in the run, so this earlier read is stale.]";
|
|
2346
2475
|
function applyStaleReadElision(messages) {
|
|
2347
2476
|
if (messages.length === 0) return {
|
|
2348
2477
|
messages,
|
|
@@ -2398,7 +2527,7 @@ function applyStaleReadElision(messages) {
|
|
|
2398
2527
|
let msgChanged = false;
|
|
2399
2528
|
const newContent = msg.content.map((block) => {
|
|
2400
2529
|
if (block.type !== "tool_result" || !staleCallIds.has(block.callId)) return block;
|
|
2401
|
-
if (block.output === "[…elided: file edited later in
|
|
2530
|
+
if (block.output === "[…elided: this file was edited later in the run, so this earlier read is stale.]") return block;
|
|
2402
2531
|
if (typeof block.output === "string" && block.output.startsWith("<persisted-output tool=\"")) return block;
|
|
2403
2532
|
msgChanged = true;
|
|
2404
2533
|
changed = true;
|
|
@@ -3005,7 +3134,19 @@ async function executeTurn(ctx, turn, priorUsage) {
|
|
|
3005
3134
|
if (ctx.compactStrategy === "tail") {
|
|
3006
3135
|
const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
|
|
3007
3136
|
const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
|
|
3008
|
-
|
|
3137
|
+
const { readPathByCallId, toolNameByCallId } = collectCallMaps(canonicalMessages);
|
|
3138
|
+
const compacted = applyTailCompaction(sanitizedMessages, threshold, keep, {
|
|
3139
|
+
readPathByCallId,
|
|
3140
|
+
toolNameByCallId,
|
|
3141
|
+
chunkTurns: COMPACT_CHUNK_TURNS,
|
|
3142
|
+
persistElideMinBytes: ctx.persistDir ? PERSISTENCE_PREVIEW_BYTES : 0
|
|
3143
|
+
});
|
|
3144
|
+
sanitizedMessages = compacted.messages;
|
|
3145
|
+
markReadStateForElidedPaths(ctx, ctx.handle.cwd, compacted.elidedReadPaths);
|
|
3146
|
+
if (ctx.persistDir && compacted.persistableElided.length > 0) sanitizedMessages = await persistElidedToolResults(sanitizedMessages, compacted.persistableElided, {
|
|
3147
|
+
persistDir: ctx.persistDir,
|
|
3148
|
+
maxBytes: ctx.persistMaxBytes
|
|
3149
|
+
});
|
|
3009
3150
|
} else if (typeof ctx.compactStrategy === "function") {
|
|
3010
3151
|
const threshold = typeof ctx.compactThreshold === "number" && ctx.compactThreshold > 0 ? ctx.compactThreshold : 131072;
|
|
3011
3152
|
const keep = typeof ctx.compactKeepTurns === "number" && ctx.compactKeepTurns >= 0 ? ctx.compactKeepTurns : 4;
|
|
@@ -4444,13 +4585,43 @@ function buildPromptMessage(provider, parts) {
|
|
|
4444
4585
|
const DEFAULT_BLOCK_THRESHOLD = 4;
|
|
4445
4586
|
const DEFAULT_ABORT_THRESHOLD = 8;
|
|
4446
4587
|
/**
|
|
4447
|
-
*
|
|
4448
|
-
*
|
|
4449
|
-
*
|
|
4450
|
-
*
|
|
4588
|
+
* Tools watched in sliding-window mode even without an explicit `windowSize`.
|
|
4589
|
+
* A re-read loop cycles through a handful of slices of the same file (offset
|
|
4590
|
+
* 1 → 52 → 199 → 1 …), so consecutive-streak counting — which resets on every
|
|
4591
|
+
* changed offset — never trips. Exact-payload keying in a window catches the
|
|
4592
|
+
* revisited slice while leaving linear chunked reads (distinct slices, never
|
|
4593
|
+
* repeated) untouched. Other tracked tools (shell, SQL) keep streak mode.
|
|
4594
|
+
*/
|
|
4595
|
+
const READ_LOOP_TOOLS = new Set(["read_file"]);
|
|
4596
|
+
/**
|
|
4597
|
+
* Default window for {@link READ_LOOP_TOOLS} when the consumer sets no global
|
|
4598
|
+
* `windowSize`. Sized so a short revisit cycle still reaches the abort
|
|
4599
|
+
* threshold: a 3-slice cycle fills a 24-call window with 8 occurrences of each
|
|
4600
|
+
* slice.
|
|
4601
|
+
*/
|
|
4602
|
+
const DEFAULT_READ_LOOP_WINDOW = 24;
|
|
4603
|
+
/**
|
|
4604
|
+
* Universal repeat ceiling: the last-resort loop breaker. Independent of the
|
|
4605
|
+
* tracked-tool set, it counts exact `(tool, args)` repeats across EVERY tool —
|
|
4606
|
+
* catching loops the per-tool streak/window misses (a `grep`/`glob`/`edit`
|
|
4607
|
+
* cycle, or a model hammering a tool that keeps getting blocked). Set high
|
|
4608
|
+
* enough that legitimate identical repeats (a handful of retries) don't trip,
|
|
4609
|
+
* but a genuine spin does.
|
|
4610
|
+
*/
|
|
4611
|
+
const DEFAULT_GLOBAL_CEILING = 12;
|
|
4612
|
+
/** Sliding window (per run+tool) the global ceiling counts repeats within. */
|
|
4613
|
+
const DEFAULT_GLOBAL_CEILING_WINDOW = 40;
|
|
4614
|
+
/**
|
|
4615
|
+
* Default tracked-tool predicate: the built-in `shell` and `read_file` tools.
|
|
4616
|
+
* `shell` is an auto-approved, side-effect-light retry magnet; `read_file` is
|
|
4617
|
+
* the re-read loop magnet — a compaction/dedup interaction can strand the
|
|
4618
|
+
* model re-reading one file forever (see {@link READ_LOOP_TOOLS}). Both are the
|
|
4619
|
+
* loop shapes the abort escalation exists for. Consumers that want to track
|
|
4620
|
+
* additional tools (e.g. an MCP query runner) pass them via
|
|
4621
|
+
* {@link RepeatGuardConfig.tools}.
|
|
4451
4622
|
*/
|
|
4452
4623
|
function defaultRepeatGuardTracked(name) {
|
|
4453
|
-
return name === "shell" || name
|
|
4624
|
+
return name === "shell" || name === "read_file";
|
|
4454
4625
|
}
|
|
4455
4626
|
function compileMatchers(tools) {
|
|
4456
4627
|
if (tools === void 0) return defaultRepeatGuardTracked;
|
|
@@ -4496,10 +4667,12 @@ function normalizeShellCommand(command) {
|
|
|
4496
4667
|
}
|
|
4497
4668
|
/**
|
|
4498
4669
|
* Built-in normalizer used when the consumer doesn't supply one. Shell-shaped
|
|
4499
|
-
* tools get {@link normalizeShellCommand};
|
|
4500
|
-
*
|
|
4501
|
-
*
|
|
4502
|
-
*
|
|
4670
|
+
* tools get {@link normalizeShellCommand}; everything else falls back to a
|
|
4671
|
+
* stable JSON encoding. Returns `undefined` when there's no meaningful payload
|
|
4672
|
+
* to key on (so the call is excluded from streak tracking rather than keyed on
|
|
4673
|
+
* `'{}'`). Consumers needing semantic keying for their own tools (e.g.
|
|
4674
|
+
* collapsing a query's whitespace) supply a normalizer via
|
|
4675
|
+
* {@link RepeatGuardConfig.normalize}.
|
|
4503
4676
|
*/
|
|
4504
4677
|
function defaultRepeatGuardNormalize(name, input) {
|
|
4505
4678
|
if (name === "shell") {
|
|
@@ -4507,11 +4680,6 @@ function defaultRepeatGuardNormalize(name, input) {
|
|
|
4507
4680
|
if (typeof cmd !== "string" || cmd.trim().length === 0) return void 0;
|
|
4508
4681
|
return `shell:${normalizeShellCommand(cmd)}`;
|
|
4509
4682
|
}
|
|
4510
|
-
if (name.endsWith("_execute_sql")) {
|
|
4511
|
-
const q = input.query ?? input.sql ?? input.statement;
|
|
4512
|
-
if (typeof q !== "string" || q.trim().length === 0) return void 0;
|
|
4513
|
-
return `sql:${q.replace(/\s+/g, " ").trim().toLowerCase()}`;
|
|
4514
|
-
}
|
|
4515
4683
|
try {
|
|
4516
4684
|
return `json:${stableStringify(input)}`;
|
|
4517
4685
|
} catch {
|
|
@@ -4553,6 +4721,7 @@ function formatBlockReason(reason, name, count) {
|
|
|
4553
4721
|
function installRepeatGuard(hooks, getConfig, abort) {
|
|
4554
4722
|
const streaks = /* @__PURE__ */ new Map();
|
|
4555
4723
|
const windows = /* @__PURE__ */ new Map();
|
|
4724
|
+
const globalWindows = /* @__PURE__ */ new Map();
|
|
4556
4725
|
function streakKey(runId, name) {
|
|
4557
4726
|
return `${runId ?? "-"}::${name}`;
|
|
4558
4727
|
}
|
|
@@ -4565,20 +4734,60 @@ function installRepeatGuard(hooks, getConfig, abort) {
|
|
|
4565
4734
|
const reachable = Number.isFinite(abortThreshold) ? abortThreshold : blockThreshold;
|
|
4566
4735
|
windowSize = Math.max(Math.floor(config.windowSize), reachable);
|
|
4567
4736
|
}
|
|
4737
|
+
const rawGlobalCeiling = typeof config.globalCeiling === "number" && Number.isFinite(config.globalCeiling) ? Math.floor(config.globalCeiling) : DEFAULT_GLOBAL_CEILING;
|
|
4738
|
+
const globalCeiling = rawGlobalCeiling > 0 && Number.isFinite(abortThreshold) ? Math.max(2, rawGlobalCeiling, abortThreshold) : 0;
|
|
4739
|
+
const globalWindow = Math.max(globalCeiling, typeof config.globalCeilingWindow === "number" && Number.isFinite(config.globalCeilingWindow) ? Math.floor(config.globalCeilingWindow) : DEFAULT_GLOBAL_CEILING_WINDOW);
|
|
4740
|
+
const isGloballyExcluded = config.globalCeilingExclude === void 0 ? () => false : compileMatchers(config.globalCeilingExclude);
|
|
4568
4741
|
return {
|
|
4569
4742
|
isTracked: compileMatchers(config.tools),
|
|
4570
4743
|
normalize: config.normalize ?? defaultRepeatGuardNormalize,
|
|
4571
4744
|
blockThreshold,
|
|
4572
4745
|
abortThreshold,
|
|
4573
4746
|
countBlockedCalls: config.countBlockedCalls === true,
|
|
4574
|
-
windowSize
|
|
4747
|
+
windowSize,
|
|
4748
|
+
globalCeiling,
|
|
4749
|
+
globalWindow,
|
|
4750
|
+
isGloballyExcluded
|
|
4575
4751
|
};
|
|
4576
4752
|
}
|
|
4577
4753
|
async function gateHandler(ctx) {
|
|
4578
4754
|
if (ctx.result !== void 0) return;
|
|
4579
4755
|
const config = getConfig();
|
|
4580
4756
|
if (!config) return;
|
|
4581
|
-
const
|
|
4757
|
+
const resolved = resolve(config);
|
|
4758
|
+
const { isTracked, normalize, blockThreshold, abortThreshold, countBlockedCalls, windowSize } = resolved;
|
|
4759
|
+
if (resolved.globalCeiling > 0 && !resolved.isGloballyExcluded(ctx.name)) {
|
|
4760
|
+
let gkey;
|
|
4761
|
+
try {
|
|
4762
|
+
gkey = `${ctx.name}\u0000${stableStringify(ctx.input)}`;
|
|
4763
|
+
} catch {
|
|
4764
|
+
gkey = "";
|
|
4765
|
+
}
|
|
4766
|
+
if (gkey.length > 0) {
|
|
4767
|
+
const gslot = streakKey(ctx.runId, ctx.name);
|
|
4768
|
+
const recent = globalWindows.get(gslot) ?? [];
|
|
4769
|
+
recent.push(gkey);
|
|
4770
|
+
if (recent.length > resolved.globalWindow) recent.shift();
|
|
4771
|
+
globalWindows.set(gslot, recent);
|
|
4772
|
+
let gcount = 0;
|
|
4773
|
+
for (const k of recent) if (k === gkey) gcount++;
|
|
4774
|
+
if (gcount >= resolved.globalCeiling) {
|
|
4775
|
+
if (!ctx.block) {
|
|
4776
|
+
ctx.block = true;
|
|
4777
|
+
ctx.reason = formatBlockReason(config.blockReason, ctx.name, gcount);
|
|
4778
|
+
}
|
|
4779
|
+
await hooks.callHook("repeat-guard:exceeded", {
|
|
4780
|
+
tool: ctx.name,
|
|
4781
|
+
count: gcount,
|
|
4782
|
+
threshold: resolved.globalCeiling,
|
|
4783
|
+
turnId: ctx.turnId,
|
|
4784
|
+
action: "abort"
|
|
4785
|
+
});
|
|
4786
|
+
abort();
|
|
4787
|
+
return;
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4582
4791
|
if (!isTracked(ctx.name)) return;
|
|
4583
4792
|
if (ctx.block && !countBlockedCalls) return;
|
|
4584
4793
|
let key;
|
|
@@ -4590,11 +4799,12 @@ function installRepeatGuard(hooks, getConfig, abort) {
|
|
|
4590
4799
|
if (typeof key !== "string" || key.length === 0) return;
|
|
4591
4800
|
const blockedByPriorGate = ctx.block;
|
|
4592
4801
|
const slot = streakKey(ctx.runId, ctx.name);
|
|
4802
|
+
const effectiveWindow = windowSize ?? (READ_LOOP_TOOLS.has(ctx.name) ? Math.max(DEFAULT_READ_LOOP_WINDOW, Number.isFinite(abortThreshold) ? abortThreshold : DEFAULT_READ_LOOP_WINDOW) : void 0);
|
|
4593
4803
|
let count;
|
|
4594
|
-
if (
|
|
4804
|
+
if (effectiveWindow !== void 0) {
|
|
4595
4805
|
const recent = windows.get(slot) ?? [];
|
|
4596
4806
|
recent.push(key);
|
|
4597
|
-
if (recent.length >
|
|
4807
|
+
if (recent.length > effectiveWindow) recent.shift();
|
|
4598
4808
|
windows.set(slot, recent);
|
|
4599
4809
|
count = 0;
|
|
4600
4810
|
for (const k of recent) if (k === key) count++;
|
|
@@ -4638,6 +4848,7 @@ function installRepeatGuard(hooks, getConfig, abort) {
|
|
|
4638
4848
|
unregister();
|
|
4639
4849
|
streaks.clear();
|
|
4640
4850
|
windows.clear();
|
|
4851
|
+
globalWindows.clear();
|
|
4641
4852
|
};
|
|
4642
4853
|
}
|
|
4643
4854
|
//#endregion
|
|
@@ -6230,7 +6441,7 @@ function installLazyDisclosureGate(hooks, lazyCanonicalNames, unlocked, discover
|
|
|
6230
6441
|
*
|
|
6231
6442
|
* # MCP Server Instructions
|
|
6232
6443
|
*
|
|
6233
|
-
* ##
|
|
6444
|
+
* ## acme
|
|
6234
6445
|
* The project is provisioned. Use `apply_migration` directly.
|
|
6235
6446
|
*
|
|
6236
6447
|
* ## linear
|
|
@@ -6475,6 +6686,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
6475
6686
|
const thinking = options.thinking ?? "off";
|
|
6476
6687
|
const model = options.model ?? provider.meta.defaultModel;
|
|
6477
6688
|
const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior);
|
|
6689
|
+
if (provider.meta?.clearsContextServerSide) resolvedBehavior.dedupReads = false;
|
|
6478
6690
|
const { maxConcurrentTools, toolBatchExecutor, shouldRethrowToolError, maxTurns, maxTurnsWarning, maxCostUsd, maxTotalTokens, maxWallMs, maxTokens, retry, thinkingBudget, modelOptions: behaviorModelOptions, schema, cache, toolOutputBudget, toolOutputBudgetExcludeTools, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, repeatGuard, elideStaleReads, toolDisclosure, mcpToolNameSeparator, toolSearch, surfaceMcpInstructions, persistThreshold, persistExcludeTools, persistDir, persistMaxBytes, strictToolPairing, maxPairingRepairsPerTurn, maxConsecutivePauseTurns, persistTurns } = resolvedBehavior;
|
|
6479
6691
|
const modelOptions = options.modelOptions ?? behaviorModelOptions;
|
|
6480
6692
|
let system = options.system || agentSystem || "You are a helpful assistant.";
|
|
@@ -8261,7 +8473,7 @@ const readFile$1 = {
|
|
|
8261
8473
|
const prior = readState.get(absKey);
|
|
8262
8474
|
if (prior && prior.contentHash === currentHash && prior.offset === offsetForKey && prior.limit === limitForKey && prior.maxBytes === maxBytesForKey && prior.lineNumbers === showLineNumbers && prior.elided !== true) {
|
|
8263
8475
|
rememberRead();
|
|
8264
|
-
return `File ${path} unchanged since the previous read in this session — the prior result is still current
|
|
8476
|
+
return `File ${path} unchanged since the previous read in this session — the prior result above is still current, so this duplicate read was skipped to save tokens.`;
|
|
8265
8477
|
}
|
|
8266
8478
|
}
|
|
8267
8479
|
if (looksBinary(raw)) return `[binary file: ${path}, ${totalBytes} bytes; use shell with hexdump | xxd | od to inspect]`;
|
|
@@ -8912,4 +9124,4 @@ const writeFile$1 = {
|
|
|
8912
9124
|
//#endregion
|
|
8913
9125
|
export { restoreModelOptions as $, PERSISTENCE_PREVIEW_BYTES as A, cerebrasDescriptor as B, stableStringify as C, TOOL_USE_SKIPPED_MESSAGE as D, TOOL_USE_CANCELLED_MESSAGE as E, resolvePersistDir as F, getModelInfo as G, effectiveContextWindow as H, resolveTasksDir as I, modelSupportsReasoning as J, localDescriptor as K, BUILTIN_PROVIDERS as L, cleanupPersistedSession as M, maybePersistToolResult as N, validateToolArgs as O, resolveMcpWarningsDir as P, piIdOf as Q, OUTPUT_RESERVE_TOKENS as R, normalizeShellCommand as S, SHELL_CASCADE_CANCEL_MESSAGE as T, enabledModelOptions as U, credKeyOf as V, getContextWindow as W, openaiDescriptor as X, modelsForDescriptor as Y, openrouterDescriptor as Z, createSkillsReadTool as _, multiEdit as a, defaultRepeatGuardNormalize as b, grep as c, createAgent as d, WAIT_TASK_TIMED_OUT_PREFIX as f, createSkillsRunScriptTool as g, createSkillsUseTool as h, readFile$1 as i, buildPersistedStub as j, PERSISTED_STUB_PREFIX as k, glob$1 as l, createToolSearchTool as m, createSpawnTool as n, listFiles as o, waitTask as p, modelOptionsFor as q, shellKill as r, createInteractionTool as s, writeFile$1 as t, edit as u, createShellTool as v, INTERRUPT_MESSAGE_FOR_TOOL_USE as w, defaultRepeatGuardTracked as x, shell as y, anthropicDescriptor as z };
|
|
8914
9126
|
|
|
8915
|
-
//# sourceMappingURL=tools-
|
|
9127
|
+
//# sourceMappingURL=tools-ZHKOh44k.js.map
|