zidane 5.10.2 → 5.10.4

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 (71) hide show
  1. package/README.md +6 -4
  2. package/dist/{agent-Bt123Fdy.d.ts → agent-Dbhh2fr7.d.ts} +216 -3
  3. package/dist/agent-Dbhh2fr7.d.ts.map +1 -0
  4. package/dist/chat/pure.d.ts +3 -3
  5. package/dist/chat.d.ts +6 -6
  6. package/dist/chat.js +2 -2
  7. package/dist/eval.d.ts +2 -2
  8. package/dist/eval.js +179 -11
  9. package/dist/eval.js.map +1 -1
  10. package/dist/{headless-Cn6XXmr3.js → headless-WsGaqG1W.js} +5 -5
  11. package/dist/{headless-Cn6XXmr3.js.map → headless-WsGaqG1W.js.map} +1 -1
  12. package/dist/headless.d.ts +1 -1
  13. package/dist/headless.js +1 -1
  14. package/dist/{index-C7BvI1Hi.d.ts → index-CgsSvsR5.d.ts} +24 -3
  15. package/dist/index-CgsSvsR5.d.ts.map +1 -0
  16. package/dist/{index-BodGKXBV.d.ts → index-DbMQsGZP.d.ts} +2 -2
  17. package/dist/{index-BodGKXBV.d.ts.map → index-DbMQsGZP.d.ts.map} +1 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +9 -9
  20. package/dist/{login-DoGslmKC.js → login-D-SWsD7j.js} +3 -3
  21. package/dist/{login-DoGslmKC.js.map → login-D-SWsD7j.js.map} +1 -1
  22. package/dist/{mcp-BdN9UjTO.js → mcp-BVuDO44W.js} +3 -3
  23. package/dist/{mcp-BdN9UjTO.js.map → mcp-BVuDO44W.js.map} +1 -1
  24. package/dist/mcp.d.ts +1 -1
  25. package/dist/mcp.js +1 -1
  26. package/dist/{messages-DdfOKKx_.js → messages-_E1RxSxV.js} +135 -13
  27. package/dist/messages-_E1RxSxV.js.map +1 -0
  28. package/dist/output/stream-json.d.ts +2 -2
  29. package/dist/output/stream-json.js +1 -1
  30. package/dist/output/terminal.d.ts +2 -2
  31. package/dist/{presets-DCrQmY3b.js → presets-9NpXoxzg.js} +2 -2
  32. package/dist/{presets-DCrQmY3b.js.map → presets-9NpXoxzg.js.map} +1 -1
  33. package/dist/presets.d.ts +2 -2
  34. package/dist/presets.js +1 -1
  35. package/dist/{providers-BxHepM_P.js → providers-B6M0Oer3.js} +68 -7
  36. package/dist/providers-B6M0Oer3.js.map +1 -0
  37. package/dist/providers.d.ts +2 -2
  38. package/dist/providers.js +3 -3
  39. package/dist/restate.d.ts +1 -1
  40. package/dist/session/sqlite.d.ts +1 -1
  41. package/dist/{session-C0uGIWm_.js → session-CZniOWFD.js} +2 -2
  42. package/dist/{session-C0uGIWm_.js.map → session-CZniOWFD.js.map} +1 -1
  43. package/dist/session.d.ts +1 -1
  44. package/dist/session.js +2 -2
  45. package/dist/skills.d.ts +2 -2
  46. package/dist/{tool-formatters-BuB31L-c.d.ts → tool-formatters-DkcN6HZt.d.ts} +2 -2
  47. package/dist/tool-formatters-DkcN6HZt.d.ts.map +1 -0
  48. package/dist/tools/fetch-url.d.ts +1 -1
  49. package/dist/tools/web-search.d.ts +1 -1
  50. package/dist/{tools-Bk9TqmCV.js → tools-BbVXIpFo.js} +256 -18
  51. package/dist/tools-BbVXIpFo.js.map +1 -0
  52. package/dist/tools.d.ts +2 -2
  53. package/dist/tools.js +1 -1
  54. package/dist/{transcript-anchors-Bkuspqgn.js → transcript-anchors-BtRC9WEQ.js} +6 -6
  55. package/dist/{transcript-anchors-Bkuspqgn.js.map → transcript-anchors-BtRC9WEQ.js.map} +1 -1
  56. package/dist/{transcript-anchors-DhVgKmEl.d.ts → transcript-anchors-DS8yTwq3.d.ts} +5 -5
  57. package/dist/{transcript-anchors-DhVgKmEl.d.ts.map → transcript-anchors-DS8yTwq3.d.ts.map} +1 -1
  58. package/dist/tui.d.ts +3 -3
  59. package/dist/tui.js +7 -7
  60. package/dist/{turn-operations-DYKtoVd9.d.ts → turn-operations-CTZwjdxD.d.ts} +3 -3
  61. package/dist/{turn-operations-DYKtoVd9.d.ts.map → turn-operations-CTZwjdxD.d.ts.map} +1 -1
  62. package/dist/types-BiobHM1D.js.map +1 -1
  63. package/dist/types.d.ts +2 -2
  64. package/package.json +1 -1
  65. package/scripts/eval.ts +18 -1
  66. package/dist/agent-Bt123Fdy.d.ts.map +0 -1
  67. package/dist/index-C7BvI1Hi.d.ts.map +0 -1
  68. package/dist/messages-DdfOKKx_.js.map +0 -1
  69. package/dist/providers-BxHepM_P.js.map +0 -1
  70. package/dist/tool-formatters-BuB31L-c.d.ts.map +0 -1
  71. package/dist/tools-Bk9TqmCV.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 arcee, f as ANTHROPIC_EXTRA_MODELS, l as anthropic, n as openai, o as generatePkce, p as FAST_MODE_OPTIONS, r as local, s as cerebras, t as openrouter } from "./providers-BxHepM_P.js";
4
+ import { a as createCursorOAuthProvider, c as baseten, d as anthropic, h as FAST_MODE_OPTIONS, m as ANTHROPIC_EXTRA_MODELS, n as openai, o as generatePkce, r as local, s as cerebras, t as openrouter, u as arcee } from "./providers-B6M0Oer3.js";
5
5
  import { i as AgentProviderError, l as errorMessage, n as AgentBudgetExceededError, o as AgentToolPairingError, p as toTypedError, t as AgentAbortedError } from "./errors-B-GeaKTX.js";
6
- import { E as appendStaticSection, a as detectTurnInterruption, c as filterUnresolvedToolUses, k as renderSystemForWire, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureEndsWithUserMessage, s as ensureToolResultPairing } from "./messages-DdfOKKx_.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-_E1RxSxV.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-GKAWYq07.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 invalidateReadStatePath, n as getToolDedupState, o as readStateKey, r as hashContent, s as resolveReadStateMap } from "./read-state-CDVYj7q-.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-DwVIJpB3.js";
13
- import { n as connectMcpServers } from "./mcp-BdN9UjTO.js";
13
+ import { n as connectMcpServers } from "./mcp-BVuDO44W.js";
14
14
  import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
15
15
  import { dirname, isAbsolute, join, resolve } from "node:path";
16
16
  import { createHooks } from "hookable";
@@ -857,6 +857,113 @@ const arceeDescriptor = {
857
857
  }
858
858
  ]
859
859
  };
860
+ const basetenDescriptor = {
861
+ key: "baseten",
862
+ label: "Baseten",
863
+ factory: baseten,
864
+ defaultModel: "zai-org/GLM-5.1",
865
+ envKey: "BASETEN_API_KEY",
866
+ apiKeyPlaceholder: "baseten-api-key…",
867
+ models: [
868
+ {
869
+ id: "zai-org/GLM-5.1",
870
+ name: "GLM 5.1",
871
+ contextWindow: 202e3,
872
+ reasoning: true,
873
+ input: ["text"]
874
+ },
875
+ {
876
+ id: "zai-org/GLM-5",
877
+ name: "GLM 5",
878
+ contextWindow: 202e3,
879
+ reasoning: true,
880
+ input: ["text"],
881
+ cost: {
882
+ input: .95,
883
+ output: 3.15,
884
+ cacheRead: .2
885
+ }
886
+ },
887
+ {
888
+ id: "zai-org/GLM-4.7",
889
+ name: "GLM 4.7",
890
+ contextWindow: 2e5,
891
+ reasoning: true,
892
+ input: ["text"],
893
+ cost: {
894
+ input: .6,
895
+ output: 2.2,
896
+ cacheRead: .12
897
+ }
898
+ },
899
+ {
900
+ id: "moonshotai/Kimi-K2.6",
901
+ name: "Kimi K2.6",
902
+ contextWindow: 262e3,
903
+ reasoning: true,
904
+ input: ["text", "image"],
905
+ cost: {
906
+ input: 1,
907
+ output: 3.9,
908
+ cacheRead: .2
909
+ }
910
+ },
911
+ {
912
+ id: "moonshotai/Kimi-K2.5",
913
+ name: "Kimi K2.5",
914
+ contextWindow: 262e3,
915
+ reasoning: true,
916
+ input: ["text", "image"],
917
+ cost: {
918
+ input: .6,
919
+ output: 3,
920
+ cacheRead: .12
921
+ }
922
+ },
923
+ {
924
+ id: "deepseek-ai/DeepSeek-V4-Pro",
925
+ name: "DeepSeek V4 Pro",
926
+ contextWindow: 131e3,
927
+ reasoning: true,
928
+ input: ["text"],
929
+ cost: {
930
+ input: 1.74,
931
+ output: 3.48,
932
+ cacheRead: .145
933
+ }
934
+ },
935
+ {
936
+ id: "nvidia/Nemotron-120B-A12B",
937
+ name: "Nemotron Super",
938
+ contextWindow: 202e3,
939
+ reasoning: true,
940
+ input: ["text"],
941
+ cost: {
942
+ input: .3,
943
+ output: .75,
944
+ cacheRead: .06
945
+ }
946
+ },
947
+ {
948
+ id: "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B",
949
+ name: "Nemotron Ultra",
950
+ contextWindow: 202e3,
951
+ reasoning: true,
952
+ input: ["text"]
953
+ },
954
+ {
955
+ id: "openai/gpt-oss-120b",
956
+ name: "GPT-OSS 120B",
957
+ contextWindow: 128e3,
958
+ reasoning: true,
959
+ input: ["text"],
960
+ cost: {
961
+ input: .1,
962
+ output: .5
963
+ }
964
+ }
965
+ ]
966
+ };
860
967
  /**
861
968
  * Conservative context-window assumption for a user-configured local model.
862
969
  * Local runtimes expose no reliable per-model metadata over the OpenAI-compat
@@ -958,6 +1065,7 @@ const BUILTIN_PROVIDERS = {
958
1065
  openrouter: openrouterDescriptor,
959
1066
  cerebras: cerebrasDescriptor,
960
1067
  arcee: arceeDescriptor,
1068
+ baseten: basetenDescriptor,
961
1069
  local: localDescriptor
962
1070
  };
963
1071
  /**
@@ -1641,9 +1749,49 @@ const FALSE_STRINGS = new Set([
1641
1749
  "No",
1642
1750
  "NO"
1643
1751
  ]);
1752
+ /**
1753
+ * Longest path most filesystems accept (Linux PATH_MAX). Anything beyond
1754
+ * this would fail at the syscall with ENAMETOOLONG anyway — rejecting here
1755
+ * produces an actionable message instead of a raw errno.
1756
+ */
1757
+ const MAX_PATH_LENGTH = 4096;
1758
+ /** Longest single path segment (Linux/APFS NAME_MAX, in bytes ≈ chars here). */
1759
+ const MAX_PATH_SEGMENT_LENGTH = 255;
1760
+ /**
1761
+ * Sanity-check a model-supplied filesystem path argument.
1762
+ *
1763
+ * Returns an actionable error string when the value cannot be a real path,
1764
+ * `undefined` when it looks plausible. This is NOT path *security*
1765
+ * validation (traversal etc. is the execution context's job) — it's a
1766
+ * steering backstop for a failure mode observed in production: reasoning
1767
+ * models leaking chain-of-thought into path arguments
1768
+ * (`src/components/Trust<think>The user wants me to build…`), which
1769
+ * otherwise surfaces as a raw `ENAMETOOLONG` the model rarely recovers
1770
+ * from.
1771
+ *
1772
+ * Checks, all objective:
1773
+ * - control characters (newlines, tabs, NUL, …) — never legal in a path
1774
+ * the model should be writing;
1775
+ * - `<think` / `</think` fragments — thinking-tag leakage;
1776
+ * - a segment over 255 chars or total length over 4096 — exceeds
1777
+ * filesystem limits (the ENAMETOOLONG shape).
1778
+ */
1779
+ function pathArgError(value, field = "path") {
1780
+ for (let i = 0; i < value.length; i++) {
1781
+ const c = value.charCodeAt(i);
1782
+ if (c < 32 || c === 127) return `invalid \`${field}\`: contains a control character (e.g. a newline) — this usually means reasoning text leaked into the argument. Re-issue the call with a clean file path.`;
1783
+ }
1784
+ if (/<\/?think/i.test(value)) return `invalid \`${field}\`: contains a thinking tag — reasoning text leaked into the argument. Re-issue the call with a clean file path.`;
1785
+ if (value.length > MAX_PATH_LENGTH) return `invalid \`${field}\`: ${value.length} characters exceeds the filesystem limit of ${MAX_PATH_LENGTH} — this usually means non-path text leaked into the argument. Re-issue the call with a clean file path.`;
1786
+ for (const segment of value.split(/[/\\]/)) if (segment.length > MAX_PATH_SEGMENT_LENGTH) return `invalid \`${field}\`: segment "${segment.slice(0, 40)}…" is ${segment.length} characters (filesystem limit: ${MAX_PATH_SEGMENT_LENGTH} per segment) — this usually means non-path text leaked into the argument. Re-issue the call with a clean file path.`;
1787
+ }
1644
1788
  function validateToolArgs(input, schema) {
1645
1789
  const required = schema.required ?? [];
1646
1790
  const properties = schema.properties ?? {};
1791
+ if (required.length > 0 && Object.keys(input).length === 0) return {
1792
+ valid: false,
1793
+ error: `Missing required field: ${required[0]} (no arguments received — they may have been lost or truncated in streaming; re-issue the call with all required fields: ${required.join(", ")})`
1794
+ };
1647
1795
  for (const field of required) if (!(field in input) || input[field] === void 0 || input[field] === null) return {
1648
1796
  valid: false,
1649
1797
  error: `Missing required field: ${field}`
@@ -2315,6 +2463,13 @@ function applyPairingRepair(ctx, messages, turnId) {
2315
2463
  ...ctx.providerName ? { provider: ctx.providerName } : {},
2316
2464
  repairs
2317
2465
  });
2466
+ const rawLimit = ctx.maxPairingRepairsPerTurn;
2467
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? rawLimit : Number.POSITIVE_INFINITY;
2468
+ if (repairs.length > limit) throw new AgentToolPairingError({
2469
+ message: `Pairing-repair storm: ${repairs.length} repairs in a single turn (limit ${limit}). The transcript is systematically corrupt (likely duplicate tool_use ids from the provider); continuing would loop without forward progress.`,
2470
+ ...ctx.providerName ? { provider: ctx.providerName } : {},
2471
+ repairs
2472
+ });
2318
2473
  for (const repair of repairs) ctx.hooks.callHook("pairing:repair", {
2319
2474
  ...repair,
2320
2475
  turnId
@@ -2365,6 +2520,8 @@ async function runLoop(ctx) {
2365
2520
  let turnsCompleted = 0;
2366
2521
  const pauseCap = ctx.maxConsecutivePauseTurns ?? 5;
2367
2522
  let consecutiveEmptyPauseTurns = 0;
2523
+ const maxTurnsWarnAt = Number.isFinite(maxTurns) && typeof ctx.maxTurnsWarning === "number" && Number.isFinite(ctx.maxTurnsWarning) && ctx.maxTurnsWarning > 0 && ctx.maxTurnsWarning < maxTurns ? Math.floor(ctx.maxTurnsWarning) : void 0;
2524
+ let maxTurnsWarned = false;
2368
2525
  const ttft = { mark: void 0 };
2369
2526
  const markTtft = () => {
2370
2527
  if (ttft.mark === void 0) ttft.mark = Date.now() - ctx.runStartMs;
@@ -2449,6 +2606,19 @@ async function runLoop(ctx) {
2449
2606
  createdAt: await ctx.clock.now()
2450
2607
  });
2451
2608
  }
2609
+ if (!result.ended && !maxTurnsWarned && maxTurnsWarnAt !== void 0 && maxTurns - (turn + 1) <= maxTurnsWarnAt) {
2610
+ maxTurnsWarned = true;
2611
+ const warning = `[Turn limit approaching: ${maxTurns - (turn + 1)} of ${maxTurns} turns remaining. Stop exploring, finish the most important remaining change, and summarize what you did and what is left.]`;
2612
+ await ctx.hooks.callHook("steer:inject", { message: warning });
2613
+ const warnMsg = ctx.provider.userMessage(warning);
2614
+ ctx.turns.push({
2615
+ id: await ctx.generateTurnId(),
2616
+ runId: ctx.runId,
2617
+ role: warnMsg.role,
2618
+ content: warnMsg.content,
2619
+ createdAt: await ctx.clock.now()
2620
+ });
2621
+ }
2452
2622
  if (result.ended) {
2453
2623
  if (ctx.followUpQueue.length > 0) {
2454
2624
  const followUp = ctx.followUpQueue.shift();
@@ -2907,11 +3077,26 @@ async function executeTurn(ctx, turn, priorUsage) {
2907
3077
  turnId
2908
3078
  });
2909
3079
  if (turnTtftMs === void 0 && result.toolCalls.length > 0) turnTtftMs = Date.now() - streamStartedAt;
2910
- const canonicalToolCalls = result.toolCalls.map((tc) => ({
3080
+ let canonicalToolCalls = result.toolCalls.map((tc) => ({
2911
3081
  ...tc,
2912
3082
  name: toCanonicalName(tc.name, ctx.aliasMaps)
2913
3083
  }));
2914
- const canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
3084
+ let canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
3085
+ if (canonicalToolCalls.length > 0 && !ctx.strictToolPairing) {
3086
+ const priorIds = /* @__PURE__ */ new Set();
3087
+ for (const t of ctx.turns) for (const block of t.content) if (block.type === "tool_call") priorIds.add(block.id);
3088
+ const reminted = remintDuplicateToolCallIds(canonicalToolCalls, canonicalContent, priorIds);
3089
+ if (reminted.reminted.length > 0) {
3090
+ canonicalToolCalls = reminted.toolCalls;
3091
+ canonicalContent = reminted.content;
3092
+ for (const r of reminted.reminted) ctx.hooks.callHook("pairing:repair", {
3093
+ mode: "duplicate-tool-use-remint",
3094
+ callId: r.oldId,
3095
+ messageIndex: ctx.turns.length,
3096
+ turnId
3097
+ });
3098
+ }
3099
+ }
2915
3100
  if (turnTtftMs !== void 0 && result.usage.timeToFirstTokenMs === void 0) result.usage.timeToFirstTokenMs = turnTtftMs;
2916
3101
  const assistantTurn = {
2917
3102
  id: turnId,
@@ -4265,6 +4450,7 @@ function formatBlockReason(reason, name, count) {
4265
4450
  */
4266
4451
  function installRepeatGuard(hooks, getConfig, abort) {
4267
4452
  const streaks = /* @__PURE__ */ new Map();
4453
+ const windows = /* @__PURE__ */ new Map();
4268
4454
  function streakKey(runId, name) {
4269
4455
  return `${runId ?? "-"}::${name}`;
4270
4456
  }
@@ -4272,19 +4458,27 @@ function installRepeatGuard(hooks, getConfig, abort) {
4272
4458
  const blockThreshold = Math.max(2, Math.floor(typeof config.blockThreshold === "number" && Number.isFinite(config.blockThreshold) ? config.blockThreshold : DEFAULT_BLOCK_THRESHOLD));
4273
4459
  const rawAbort = typeof config.abortThreshold === "number" && Number.isFinite(config.abortThreshold) ? config.abortThreshold : DEFAULT_ABORT_THRESHOLD;
4274
4460
  const abortThreshold = rawAbort > 0 ? Math.max(blockThreshold + 1, Math.floor(rawAbort)) : Infinity;
4461
+ let windowSize;
4462
+ if (typeof config.windowSize === "number" && Number.isFinite(config.windowSize) && config.windowSize >= 2) {
4463
+ const reachable = Number.isFinite(abortThreshold) ? abortThreshold : blockThreshold;
4464
+ windowSize = Math.max(Math.floor(config.windowSize), reachable);
4465
+ }
4275
4466
  return {
4276
4467
  isTracked: compileMatchers(config.tools),
4277
4468
  normalize: config.normalize ?? defaultRepeatGuardNormalize,
4278
4469
  blockThreshold,
4279
- abortThreshold
4470
+ abortThreshold,
4471
+ countBlockedCalls: config.countBlockedCalls === true,
4472
+ windowSize
4280
4473
  };
4281
4474
  }
4282
4475
  async function gateHandler(ctx) {
4283
- if (ctx.block || ctx.result !== void 0) return;
4476
+ if (ctx.result !== void 0) return;
4284
4477
  const config = getConfig();
4285
4478
  if (!config) return;
4286
- const { isTracked, normalize, blockThreshold, abortThreshold } = resolve(config);
4479
+ const { isTracked, normalize, blockThreshold, abortThreshold, countBlockedCalls, windowSize } = resolve(config);
4287
4480
  if (!isTracked(ctx.name)) return;
4481
+ if (ctx.block && !countBlockedCalls) return;
4288
4482
  let key;
4289
4483
  try {
4290
4484
  key = normalize(ctx.name, ctx.input);
@@ -4292,17 +4486,30 @@ function installRepeatGuard(hooks, getConfig, abort) {
4292
4486
  return;
4293
4487
  }
4294
4488
  if (typeof key !== "string" || key.length === 0) return;
4489
+ const blockedByPriorGate = ctx.block;
4295
4490
  const slot = streakKey(ctx.runId, ctx.name);
4296
- const prior = streaks.get(slot);
4297
- const count = prior && prior.key === key ? prior.count + 1 : 1;
4298
- streaks.set(slot, {
4299
- key,
4300
- count
4301
- });
4491
+ let count;
4492
+ if (windowSize !== void 0) {
4493
+ const recent = windows.get(slot) ?? [];
4494
+ recent.push(key);
4495
+ if (recent.length > windowSize) recent.shift();
4496
+ windows.set(slot, recent);
4497
+ count = 0;
4498
+ for (const k of recent) if (k === key) count++;
4499
+ } else {
4500
+ const prior = streaks.get(slot);
4501
+ count = prior && prior.key === key ? prior.count + 1 : 1;
4502
+ streaks.set(slot, {
4503
+ key,
4504
+ count
4505
+ });
4506
+ }
4302
4507
  if (count < blockThreshold) return;
4303
4508
  if (count >= abortThreshold) {
4304
- ctx.block = true;
4305
- ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4509
+ if (!blockedByPriorGate) {
4510
+ ctx.block = true;
4511
+ ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4512
+ }
4306
4513
  await hooks.callHook("repeat-guard:exceeded", {
4307
4514
  tool: ctx.name,
4308
4515
  count,
@@ -4313,6 +4520,7 @@ function installRepeatGuard(hooks, getConfig, abort) {
4313
4520
  abort();
4314
4521
  return;
4315
4522
  }
4523
+ if (blockedByPriorGate) return;
4316
4524
  ctx.block = true;
4317
4525
  ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4318
4526
  await hooks.callHook("repeat-guard:exceeded", {
@@ -4327,6 +4535,7 @@ function installRepeatGuard(hooks, getConfig, abort) {
4327
4535
  return function uninstall() {
4328
4536
  unregister();
4329
4537
  streaks.clear();
4538
+ windows.clear();
4330
4539
  };
4331
4540
  }
4332
4541
  //#endregion
@@ -4383,6 +4592,11 @@ function installToolBudgetsGate(hooks, getToolBudgets, enqueueSteer) {
4383
4592
  mode = "steer";
4384
4593
  message = defaultSteerMessage(ctx.name, count, max);
4385
4594
  }
4595
+ const hardMax = budget.hardMax;
4596
+ if (mode === "steer" && typeof hardMax === "number" && Number.isFinite(hardMax) && hardMax > 0 && count >= hardMax) {
4597
+ mode = "block";
4598
+ message = defaultHardBlockMessage(ctx.name, count, hardMax);
4599
+ } else if (mode === "steer") approvedCounts[ctx.name] = count + 1;
4386
4600
  if (mode === "block") {
4387
4601
  ctx.block = true;
4388
4602
  ctx.reason = message;
@@ -4419,6 +4633,9 @@ function defaultSteerMessage(tool, count, max) {
4419
4633
  function defaultBlockMessage(tool, max) {
4420
4634
  return `Tool '${tool}' has reached its per-run budget of ${max} calls; further invocations are refused.`;
4421
4635
  }
4636
+ function defaultHardBlockMessage(tool, count, hardMax) {
4637
+ return `Tool '${tool}' has been called ${count} times this run, past its hard ceiling of ${hardMax} — the earlier budget warning was not heeded. Further invocations are refused; finish the task with the information you already have.`;
4638
+ }
4422
4639
  //#endregion
4423
4640
  //#region src/tools/shell-semantics.ts
4424
4641
  const DEFAULT_SEMANTIC = (exitCode) => ({
@@ -5724,6 +5941,7 @@ function resolveBehavior(agentBehavior, runBehavior) {
5724
5941
  toolBatchExecutor: runBehavior?.toolBatchExecutor ?? agentBehavior?.toolBatchExecutor,
5725
5942
  shouldRethrowToolError: runBehavior?.shouldRethrowToolError ?? agentBehavior?.shouldRethrowToolError,
5726
5943
  maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,
5944
+ maxTurnsWarning: runBehavior?.maxTurnsWarning ?? agentBehavior?.maxTurnsWarning,
5727
5945
  maxCostUsd: runBehavior?.maxCostUsd ?? agentBehavior?.maxCostUsd,
5728
5946
  retry: runBehavior?.retry ?? agentBehavior?.retry,
5729
5947
  maxTotalTokens: runBehavior?.maxTotalTokens ?? agentBehavior?.maxTotalTokens,
@@ -5762,6 +5980,7 @@ function resolveBehavior(agentBehavior, runBehavior) {
5762
5980
  ...runBehavior?.extensions
5763
5981
  } : void 0,
5764
5982
  strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false,
5983
+ maxPairingRepairsPerTurn: runBehavior?.maxPairingRepairsPerTurn ?? agentBehavior?.maxPairingRepairsPerTurn,
5765
5984
  maxConsecutivePauseTurns: runBehavior?.maxConsecutivePauseTurns ?? agentBehavior?.maxConsecutivePauseTurns,
5766
5985
  persistTurns: runBehavior?.persistTurns ?? agentBehavior?.persistTurns
5767
5986
  };
@@ -6172,7 +6391,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6172
6391
  const thinking = options.thinking ?? "off";
6173
6392
  const model = options.model ?? provider.meta.defaultModel;
6174
6393
  const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior);
6175
- const { maxConcurrentTools, toolBatchExecutor, shouldRethrowToolError, maxTurns, maxCostUsd, maxTotalTokens, 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, maxConsecutivePauseTurns, persistTurns } = resolvedBehavior;
6394
+ const { maxConcurrentTools, toolBatchExecutor, shouldRethrowToolError, maxTurns, maxTurnsWarning, maxCostUsd, maxTotalTokens, 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;
6176
6395
  const modelOptions = options.modelOptions ?? behaviorModelOptions;
6177
6396
  let system = options.system || agentSystem || "You are a helpful assistant.";
6178
6397
  const baseSystemForBreakdown = renderSystemForWire(system);
@@ -6507,6 +6726,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6507
6726
  generateTurnId: mintTurnId,
6508
6727
  clock,
6509
6728
  maxTurns,
6729
+ ...maxTurnsWarning !== void 0 ? { maxTurnsWarning } : {},
6510
6730
  ...maxCostUsd !== void 0 ? { maxCostUsd } : {},
6511
6731
  ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {},
6512
6732
  ...retry !== void 0 ? { retry } : {},
@@ -6531,6 +6751,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6531
6751
  ...persistDir !== void 0 ? { persistDir } : {},
6532
6752
  ...persistMaxBytes !== void 0 ? { persistMaxBytes } : {},
6533
6753
  ...strictToolPairing ? { strictToolPairing: true } : {},
6754
+ ...maxPairingRepairsPerTurn !== void 0 ? { maxPairingRepairsPerTurn } : {},
6534
6755
  ...maxConsecutivePauseTurns !== void 0 ? { maxConsecutivePauseTurns } : {},
6535
6756
  providerName: provider.name,
6536
6757
  runStartMs,
@@ -7122,6 +7343,11 @@ const edit = {
7122
7343
  const find = old_string;
7123
7344
  const replacement = new_string;
7124
7345
  const replaceAll = replace_all === true;
7346
+ const pathErr = pathArgError(target);
7347
+ if (pathErr) {
7348
+ ctx.reportOutcome?.("failed");
7349
+ return `Edit error: ${pathErr}`;
7350
+ }
7125
7351
  if (find === replacement) return `Edit error: old_string and new_string are identical — nothing to change in ${target}.`;
7126
7352
  if (find.length === 0) return `Edit error: old_string is empty. Use write_file to create or fully overwrite a file.`;
7127
7353
  let original;
@@ -7640,6 +7866,11 @@ const multiEdit = {
7640
7866
  async execute({ path, edits }, ctx) {
7641
7867
  const target = path;
7642
7868
  const steps = edits;
7869
+ const pathErr = pathArgError(target);
7870
+ if (pathErr) {
7871
+ ctx.reportOutcome?.("failed");
7872
+ return `multi_edit error: ${pathErr}`;
7873
+ }
7643
7874
  if (!Array.isArray(steps) || steps.length === 0) {
7644
7875
  ctx.reportOutcome?.("failed");
7645
7876
  return `multi_edit error: edits must be a non-empty array.`;
@@ -7866,6 +8097,8 @@ const readFile$1 = {
7866
8097
  }
7867
8098
  },
7868
8099
  async execute({ path, offset, limit, maxBytes, lineNumbers }, ctx) {
8100
+ const pathErr = pathArgError(path);
8101
+ if (pathErr) return `Read error: ${pathErr}`;
7869
8102
  const extMedia = imageMediaTypeFor(path);
7870
8103
  if (extMedia) {
7871
8104
  const sizeCap = maxBytes !== void 0 ? normalizeInteger(maxBytes, DEFAULT_ATTACHMENT_BYTE_CAP) : DEFAULT_ATTACHMENT_BYTE_CAP;
@@ -8560,6 +8793,11 @@ const writeFile$1 = {
8560
8793
  async execute({ path, content }, ctx) {
8561
8794
  const targetPath = path;
8562
8795
  const targetContent = content;
8796
+ const pathErr = pathArgError(targetPath);
8797
+ if (pathErr) {
8798
+ ctx.reportOutcome?.("failed");
8799
+ return `Write error: ${pathErr}`;
8800
+ }
8563
8801
  let existing;
8564
8802
  try {
8565
8803
  existing = await ctx.execution.readFile(ctx.handle, targetPath);
@@ -8585,4 +8823,4 @@ const writeFile$1 = {
8585
8823
  //#endregion
8586
8824
  export { maybePersistToolResult as A, enabledModelOptions as B, TOOL_USE_CANCELLED_MESSAGE as C, PERSISTENCE_PREVIEW_BYTES as D, PERSISTED_STUB_PREFIX as E, OUTPUT_RESERVE_TOKENS as F, modelSupportsReasoning as G, getModelInfo as H, anthropicDescriptor as I, openrouterDescriptor as J, modelsForDescriptor as K, cerebrasDescriptor as L, resolvePersistDir as M, resolveTasksDir as N, buildPersistedStub as O, BUILTIN_PROVIDERS as P, credKeyOf as R, SHELL_CASCADE_CANCEL_MESSAGE as S, validateToolArgs as T, localDescriptor as U, getContextWindow as V, modelOptionsFor as W, restoreModelOptions as X, piIdOf as Y, alwaysQuote as _, multiEdit as a, shell 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, resolveMcpWarningsDir as j, cleanupPersistedSession as k, glob$1 as l, createToolSearchTool as m, createSpawnTool as n, listFiles as o, waitTask as p, openaiDescriptor as q, shellKill as r, createInteractionTool as s, writeFile$1 as t, edit as u, createSkillsReadTool as v, TOOL_USE_SKIPPED_MESSAGE as w, INTERRUPT_MESSAGE_FOR_TOOL_USE as x, createShellTool as y, effectiveContextWindow as z };
8587
8825
 
8588
- //# sourceMappingURL=tools-Bk9TqmCV.js.map
8826
+ //# sourceMappingURL=tools-BbVXIpFo.js.map