zidane 5.10.2 → 5.10.3

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-MbmvNVAP.d.ts} +168 -3
  3. package/dist/agent-MbmvNVAP.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-DHdHSA2s.js} +5 -5
  11. package/dist/{headless-Cn6XXmr3.js.map → headless-DHdHSA2s.js.map} +1 -1
  12. package/dist/headless.d.ts +1 -1
  13. package/dist/headless.js +1 -1
  14. package/dist/{index-BodGKXBV.d.ts → index-CCHh9Yca.d.ts} +2 -2
  15. package/dist/{index-BodGKXBV.d.ts.map → index-CCHh9Yca.d.ts.map} +1 -1
  16. package/dist/{index-C7BvI1Hi.d.ts → index-Iki5q09p.d.ts} +24 -3
  17. package/dist/index-Iki5q09p.d.ts.map +1 -0
  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-BtAXrPCY.js} +2 -2
  32. package/dist/{presets-DCrQmY3b.js.map → presets-BtAXrPCY.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-CgE32BNa.d.ts} +2 -2
  47. package/dist/tool-formatters-CgE32BNa.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-0IqJRRj8.js} +254 -18
  51. package/dist/tools-0IqJRRj8.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-CXheYWkt.js} +6 -6
  55. package/dist/{transcript-anchors-Bkuspqgn.js.map → transcript-anchors-CXheYWkt.js.map} +1 -1
  56. package/dist/{transcript-anchors-DhVgKmEl.d.ts → transcript-anchors-DkJotRvR.d.ts} +5 -5
  57. package/dist/{transcript-anchors-DhVgKmEl.d.ts.map → transcript-anchors-DkJotRvR.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-BuL1RjGN.d.ts} +3 -3
  61. package/dist/{turn-operations-DYKtoVd9.d.ts.map → turn-operations-BuL1RjGN.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,7 @@ 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;
2368
2524
  const ttft = { mark: void 0 };
2369
2525
  const markTtft = () => {
2370
2526
  if (ttft.mark === void 0) ttft.mark = Date.now() - ctx.runStartMs;
@@ -2449,6 +2605,18 @@ async function runLoop(ctx) {
2449
2605
  createdAt: await ctx.clock.now()
2450
2606
  });
2451
2607
  }
2608
+ if (!result.ended && maxTurnsWarnAt !== void 0 && maxTurns - (turn + 1) === maxTurnsWarnAt) {
2609
+ const warning = `[Turn limit approaching: ${maxTurnsWarnAt} of ${maxTurns} turns remaining. Stop exploring, finish the most important remaining change, and summarize what you did and what is left.]`;
2610
+ await ctx.hooks.callHook("steer:inject", { message: warning });
2611
+ const warnMsg = ctx.provider.userMessage(warning);
2612
+ ctx.turns.push({
2613
+ id: await ctx.generateTurnId(),
2614
+ runId: ctx.runId,
2615
+ role: warnMsg.role,
2616
+ content: warnMsg.content,
2617
+ createdAt: await ctx.clock.now()
2618
+ });
2619
+ }
2452
2620
  if (result.ended) {
2453
2621
  if (ctx.followUpQueue.length > 0) {
2454
2622
  const followUp = ctx.followUpQueue.shift();
@@ -2907,11 +3075,26 @@ async function executeTurn(ctx, turn, priorUsage) {
2907
3075
  turnId
2908
3076
  });
2909
3077
  if (turnTtftMs === void 0 && result.toolCalls.length > 0) turnTtftMs = Date.now() - streamStartedAt;
2910
- const canonicalToolCalls = result.toolCalls.map((tc) => ({
3078
+ let canonicalToolCalls = result.toolCalls.map((tc) => ({
2911
3079
  ...tc,
2912
3080
  name: toCanonicalName(tc.name, ctx.aliasMaps)
2913
3081
  }));
2914
- const canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
3082
+ let canonicalContent = rewriteContentToCanonical(result.assistantMessage?.content ?? [], ctx.aliasMaps);
3083
+ if (canonicalToolCalls.length > 0 && !ctx.strictToolPairing) {
3084
+ const priorIds = /* @__PURE__ */ new Set();
3085
+ for (const t of ctx.turns) for (const block of t.content) if (block.type === "tool_call") priorIds.add(block.id);
3086
+ const reminted = remintDuplicateToolCallIds(canonicalToolCalls, canonicalContent, priorIds);
3087
+ if (reminted.reminted.length > 0) {
3088
+ canonicalToolCalls = reminted.toolCalls;
3089
+ canonicalContent = reminted.content;
3090
+ for (const r of reminted.reminted) ctx.hooks.callHook("pairing:repair", {
3091
+ mode: "duplicate-tool-use-remint",
3092
+ callId: r.oldId,
3093
+ messageIndex: ctx.turns.length,
3094
+ turnId
3095
+ });
3096
+ }
3097
+ }
2915
3098
  if (turnTtftMs !== void 0 && result.usage.timeToFirstTokenMs === void 0) result.usage.timeToFirstTokenMs = turnTtftMs;
2916
3099
  const assistantTurn = {
2917
3100
  id: turnId,
@@ -4265,6 +4448,7 @@ function formatBlockReason(reason, name, count) {
4265
4448
  */
4266
4449
  function installRepeatGuard(hooks, getConfig, abort) {
4267
4450
  const streaks = /* @__PURE__ */ new Map();
4451
+ const windows = /* @__PURE__ */ new Map();
4268
4452
  function streakKey(runId, name) {
4269
4453
  return `${runId ?? "-"}::${name}`;
4270
4454
  }
@@ -4272,19 +4456,27 @@ function installRepeatGuard(hooks, getConfig, abort) {
4272
4456
  const blockThreshold = Math.max(2, Math.floor(typeof config.blockThreshold === "number" && Number.isFinite(config.blockThreshold) ? config.blockThreshold : DEFAULT_BLOCK_THRESHOLD));
4273
4457
  const rawAbort = typeof config.abortThreshold === "number" && Number.isFinite(config.abortThreshold) ? config.abortThreshold : DEFAULT_ABORT_THRESHOLD;
4274
4458
  const abortThreshold = rawAbort > 0 ? Math.max(blockThreshold + 1, Math.floor(rawAbort)) : Infinity;
4459
+ let windowSize;
4460
+ if (typeof config.windowSize === "number" && Number.isFinite(config.windowSize) && config.windowSize >= 2) {
4461
+ const reachable = Number.isFinite(abortThreshold) ? abortThreshold : blockThreshold;
4462
+ windowSize = Math.max(Math.floor(config.windowSize), reachable);
4463
+ }
4275
4464
  return {
4276
4465
  isTracked: compileMatchers(config.tools),
4277
4466
  normalize: config.normalize ?? defaultRepeatGuardNormalize,
4278
4467
  blockThreshold,
4279
- abortThreshold
4468
+ abortThreshold,
4469
+ countBlockedCalls: config.countBlockedCalls === true,
4470
+ windowSize
4280
4471
  };
4281
4472
  }
4282
4473
  async function gateHandler(ctx) {
4283
- if (ctx.block || ctx.result !== void 0) return;
4474
+ if (ctx.result !== void 0) return;
4284
4475
  const config = getConfig();
4285
4476
  if (!config) return;
4286
- const { isTracked, normalize, blockThreshold, abortThreshold } = resolve(config);
4477
+ const { isTracked, normalize, blockThreshold, abortThreshold, countBlockedCalls, windowSize } = resolve(config);
4287
4478
  if (!isTracked(ctx.name)) return;
4479
+ if (ctx.block && !countBlockedCalls) return;
4288
4480
  let key;
4289
4481
  try {
4290
4482
  key = normalize(ctx.name, ctx.input);
@@ -4292,17 +4484,30 @@ function installRepeatGuard(hooks, getConfig, abort) {
4292
4484
  return;
4293
4485
  }
4294
4486
  if (typeof key !== "string" || key.length === 0) return;
4487
+ const blockedByPriorGate = ctx.block;
4295
4488
  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
- });
4489
+ let count;
4490
+ if (windowSize !== void 0) {
4491
+ const recent = windows.get(slot) ?? [];
4492
+ recent.push(key);
4493
+ if (recent.length > windowSize) recent.shift();
4494
+ windows.set(slot, recent);
4495
+ count = 0;
4496
+ for (const k of recent) if (k === key) count++;
4497
+ } else {
4498
+ const prior = streaks.get(slot);
4499
+ count = prior && prior.key === key ? prior.count + 1 : 1;
4500
+ streaks.set(slot, {
4501
+ key,
4502
+ count
4503
+ });
4504
+ }
4302
4505
  if (count < blockThreshold) return;
4303
4506
  if (count >= abortThreshold) {
4304
- ctx.block = true;
4305
- ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4507
+ if (!blockedByPriorGate) {
4508
+ ctx.block = true;
4509
+ ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4510
+ }
4306
4511
  await hooks.callHook("repeat-guard:exceeded", {
4307
4512
  tool: ctx.name,
4308
4513
  count,
@@ -4313,6 +4518,7 @@ function installRepeatGuard(hooks, getConfig, abort) {
4313
4518
  abort();
4314
4519
  return;
4315
4520
  }
4521
+ if (blockedByPriorGate) return;
4316
4522
  ctx.block = true;
4317
4523
  ctx.reason = formatBlockReason(config.blockReason, ctx.name, count);
4318
4524
  await hooks.callHook("repeat-guard:exceeded", {
@@ -4327,6 +4533,7 @@ function installRepeatGuard(hooks, getConfig, abort) {
4327
4533
  return function uninstall() {
4328
4534
  unregister();
4329
4535
  streaks.clear();
4536
+ windows.clear();
4330
4537
  };
4331
4538
  }
4332
4539
  //#endregion
@@ -4383,6 +4590,11 @@ function installToolBudgetsGate(hooks, getToolBudgets, enqueueSteer) {
4383
4590
  mode = "steer";
4384
4591
  message = defaultSteerMessage(ctx.name, count, max);
4385
4592
  }
4593
+ const hardMax = budget.hardMax;
4594
+ if (mode === "steer" && typeof hardMax === "number" && Number.isFinite(hardMax) && hardMax > 0 && count >= hardMax) {
4595
+ mode = "block";
4596
+ message = defaultHardBlockMessage(ctx.name, count, hardMax);
4597
+ } else if (mode === "steer") approvedCounts[ctx.name] = count + 1;
4386
4598
  if (mode === "block") {
4387
4599
  ctx.block = true;
4388
4600
  ctx.reason = message;
@@ -4419,6 +4631,9 @@ function defaultSteerMessage(tool, count, max) {
4419
4631
  function defaultBlockMessage(tool, max) {
4420
4632
  return `Tool '${tool}' has reached its per-run budget of ${max} calls; further invocations are refused.`;
4421
4633
  }
4634
+ function defaultHardBlockMessage(tool, count, hardMax) {
4635
+ 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.`;
4636
+ }
4422
4637
  //#endregion
4423
4638
  //#region src/tools/shell-semantics.ts
4424
4639
  const DEFAULT_SEMANTIC = (exitCode) => ({
@@ -5724,6 +5939,7 @@ function resolveBehavior(agentBehavior, runBehavior) {
5724
5939
  toolBatchExecutor: runBehavior?.toolBatchExecutor ?? agentBehavior?.toolBatchExecutor,
5725
5940
  shouldRethrowToolError: runBehavior?.shouldRethrowToolError ?? agentBehavior?.shouldRethrowToolError,
5726
5941
  maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,
5942
+ maxTurnsWarning: runBehavior?.maxTurnsWarning ?? agentBehavior?.maxTurnsWarning,
5727
5943
  maxCostUsd: runBehavior?.maxCostUsd ?? agentBehavior?.maxCostUsd,
5728
5944
  retry: runBehavior?.retry ?? agentBehavior?.retry,
5729
5945
  maxTotalTokens: runBehavior?.maxTotalTokens ?? agentBehavior?.maxTotalTokens,
@@ -5762,6 +5978,7 @@ function resolveBehavior(agentBehavior, runBehavior) {
5762
5978
  ...runBehavior?.extensions
5763
5979
  } : void 0,
5764
5980
  strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false,
5981
+ maxPairingRepairsPerTurn: runBehavior?.maxPairingRepairsPerTurn ?? agentBehavior?.maxPairingRepairsPerTurn,
5765
5982
  maxConsecutivePauseTurns: runBehavior?.maxConsecutivePauseTurns ?? agentBehavior?.maxConsecutivePauseTurns,
5766
5983
  persistTurns: runBehavior?.persistTurns ?? agentBehavior?.persistTurns
5767
5984
  };
@@ -6172,7 +6389,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6172
6389
  const thinking = options.thinking ?? "off";
6173
6390
  const model = options.model ?? provider.meta.defaultModel;
6174
6391
  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;
6392
+ 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
6393
  const modelOptions = options.modelOptions ?? behaviorModelOptions;
6177
6394
  let system = options.system || agentSystem || "You are a helpful assistant.";
6178
6395
  const baseSystemForBreakdown = renderSystemForWire(system);
@@ -6507,6 +6724,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6507
6724
  generateTurnId: mintTurnId,
6508
6725
  clock,
6509
6726
  maxTurns,
6727
+ ...maxTurnsWarning !== void 0 ? { maxTurnsWarning } : {},
6510
6728
  ...maxCostUsd !== void 0 ? { maxCostUsd } : {},
6511
6729
  ...maxTotalTokens !== void 0 ? { maxTotalTokens } : {},
6512
6730
  ...retry !== void 0 ? { retry } : {},
@@ -6531,6 +6749,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
6531
6749
  ...persistDir !== void 0 ? { persistDir } : {},
6532
6750
  ...persistMaxBytes !== void 0 ? { persistMaxBytes } : {},
6533
6751
  ...strictToolPairing ? { strictToolPairing: true } : {},
6752
+ ...maxPairingRepairsPerTurn !== void 0 ? { maxPairingRepairsPerTurn } : {},
6534
6753
  ...maxConsecutivePauseTurns !== void 0 ? { maxConsecutivePauseTurns } : {},
6535
6754
  providerName: provider.name,
6536
6755
  runStartMs,
@@ -7122,6 +7341,11 @@ const edit = {
7122
7341
  const find = old_string;
7123
7342
  const replacement = new_string;
7124
7343
  const replaceAll = replace_all === true;
7344
+ const pathErr = pathArgError(target);
7345
+ if (pathErr) {
7346
+ ctx.reportOutcome?.("failed");
7347
+ return `Edit error: ${pathErr}`;
7348
+ }
7125
7349
  if (find === replacement) return `Edit error: old_string and new_string are identical — nothing to change in ${target}.`;
7126
7350
  if (find.length === 0) return `Edit error: old_string is empty. Use write_file to create or fully overwrite a file.`;
7127
7351
  let original;
@@ -7640,6 +7864,11 @@ const multiEdit = {
7640
7864
  async execute({ path, edits }, ctx) {
7641
7865
  const target = path;
7642
7866
  const steps = edits;
7867
+ const pathErr = pathArgError(target);
7868
+ if (pathErr) {
7869
+ ctx.reportOutcome?.("failed");
7870
+ return `multi_edit error: ${pathErr}`;
7871
+ }
7643
7872
  if (!Array.isArray(steps) || steps.length === 0) {
7644
7873
  ctx.reportOutcome?.("failed");
7645
7874
  return `multi_edit error: edits must be a non-empty array.`;
@@ -7866,6 +8095,8 @@ const readFile$1 = {
7866
8095
  }
7867
8096
  },
7868
8097
  async execute({ path, offset, limit, maxBytes, lineNumbers }, ctx) {
8098
+ const pathErr = pathArgError(path);
8099
+ if (pathErr) return `Read error: ${pathErr}`;
7869
8100
  const extMedia = imageMediaTypeFor(path);
7870
8101
  if (extMedia) {
7871
8102
  const sizeCap = maxBytes !== void 0 ? normalizeInteger(maxBytes, DEFAULT_ATTACHMENT_BYTE_CAP) : DEFAULT_ATTACHMENT_BYTE_CAP;
@@ -8560,6 +8791,11 @@ const writeFile$1 = {
8560
8791
  async execute({ path, content }, ctx) {
8561
8792
  const targetPath = path;
8562
8793
  const targetContent = content;
8794
+ const pathErr = pathArgError(targetPath);
8795
+ if (pathErr) {
8796
+ ctx.reportOutcome?.("failed");
8797
+ return `Write error: ${pathErr}`;
8798
+ }
8563
8799
  let existing;
8564
8800
  try {
8565
8801
  existing = await ctx.execution.readFile(ctx.handle, targetPath);
@@ -8585,4 +8821,4 @@ const writeFile$1 = {
8585
8821
  //#endregion
8586
8822
  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
8823
 
8588
- //# sourceMappingURL=tools-Bk9TqmCV.js.map
8824
+ //# sourceMappingURL=tools-0IqJRRj8.js.map