@ykurilov/agent-chat-core 0.65.0 → 0.66.0

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.
@@ -2215,8 +2215,8 @@ function cutForgedBlocks(text, formats, options2 = {}) {
2215
2215
  const scanner = createForgedBlockScanner(formats, options2);
2216
2216
  const pieces = [...scanner.push(text), ...scanner.end()];
2217
2217
  return {
2218
- text: pieces.map((piece) => piece.kind === "text" ? piece.text : "").join(""),
2219
- blocks: pieces.flatMap((piece) => piece.kind === "block" ? [piece.name] : [])
2218
+ text: pieces.map((piece2) => piece2.kind === "text" ? piece2.text : "").join(""),
2219
+ blocks: pieces.flatMap((piece2) => piece2.kind === "block" ? [piece2.name] : [])
2220
2220
  };
2221
2221
  }
2222
2222
  var hasWords = (text) => /[\p{L}\p{N}]/u.test(text);
@@ -2286,16 +2286,16 @@ function createForgedBlockGuard(formats) {
2286
2286
  };
2287
2287
  const deltaOf = (id, pieces, controller) => {
2288
2288
  let delta = "";
2289
- for (const piece of pieces) {
2290
- if (piece.kind === "text" && hasWords(piece.text)) {
2289
+ for (const piece2 of pieces) {
2290
+ if (piece2.kind === "text" && hasWords(piece2.text)) {
2291
2291
  worded = true;
2292
2292
  if (held) settle(false, controller);
2293
2293
  }
2294
- if (piece.kind === "text" && !held) delta += piece.text;
2294
+ if (piece2.kind === "text" && !held) delta += piece2.text;
2295
2295
  else if (worded) {
2296
- if (piece.kind === "block") cut.push(piece.name);
2296
+ if (piece2.kind === "block") cut.push(piece2.name);
2297
2297
  } else {
2298
- (held ??= []).push({ id, name: piece.kind === "block" ? piece.name : "", text: piece.text });
2298
+ (held ??= []).push({ id, name: piece2.kind === "block" ? piece2.name : "", text: piece2.text });
2299
2299
  }
2300
2300
  }
2301
2301
  return delta;
@@ -2512,6 +2512,230 @@ function contentFor(value, images) {
2512
2512
  return value.some(isImage) ? [...kept, { type: "text", text: NO_IMAGE_NOTE }] : kept;
2513
2513
  }
2514
2514
 
2515
+ // src/agent-run/json-repair.ts
2516
+ var VALID_JSON_ESCAPES = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]);
2517
+ function isControlCharacter(char) {
2518
+ const codePoint = char.codePointAt(0);
2519
+ return codePoint !== void 0 && codePoint >= 0 && codePoint <= 31;
2520
+ }
2521
+ function escapeControlCharacter(char) {
2522
+ switch (char) {
2523
+ case "\b":
2524
+ return "\\b";
2525
+ case "\f":
2526
+ return "\\f";
2527
+ case "\n":
2528
+ return "\\n";
2529
+ case "\r":
2530
+ return "\\r";
2531
+ case " ":
2532
+ return "\\t";
2533
+ default:
2534
+ return `\\u${char.codePointAt(0)?.toString(16).padStart(4, "0") ?? "0000"}`;
2535
+ }
2536
+ }
2537
+ function repairJson(json) {
2538
+ let repaired = "";
2539
+ let inString = false;
2540
+ for (let index = 0; index < json.length; index++) {
2541
+ const char = json[index];
2542
+ if (!inString) {
2543
+ repaired += char;
2544
+ if (char === '"') inString = true;
2545
+ continue;
2546
+ }
2547
+ if (char === '"') {
2548
+ repaired += char;
2549
+ inString = false;
2550
+ continue;
2551
+ }
2552
+ if (char === "\\") {
2553
+ const nextChar = json[index + 1];
2554
+ if (nextChar === void 0) {
2555
+ repaired += "\\\\";
2556
+ continue;
2557
+ }
2558
+ if (nextChar === "u") {
2559
+ const unicodeDigits = json.slice(index + 2, index + 6);
2560
+ if (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {
2561
+ repaired += `\\u${unicodeDigits}`;
2562
+ index += 5;
2563
+ continue;
2564
+ }
2565
+ }
2566
+ if (VALID_JSON_ESCAPES.has(nextChar)) {
2567
+ repaired += `\\${nextChar}`;
2568
+ index += 1;
2569
+ continue;
2570
+ }
2571
+ repaired += "\\\\";
2572
+ continue;
2573
+ }
2574
+ repaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;
2575
+ }
2576
+ return repaired;
2577
+ }
2578
+
2579
+ // src/agent-run/missing-closers.ts
2580
+ var MAX_MISSING_CLOSERS = 3;
2581
+ var CLOSER = { "{": "}", "[": "]" };
2582
+ function addMissingClosers(json) {
2583
+ const open = [];
2584
+ let text = "";
2585
+ let added = 0;
2586
+ let last = "none";
2587
+ let inString = false;
2588
+ let stringIsKey = false;
2589
+ for (let index = 0; index < json.length; index++) {
2590
+ const char = json[index];
2591
+ if (inString) {
2592
+ text += char;
2593
+ if (char === "\\") {
2594
+ if (index + 1 >= json.length) return void 0;
2595
+ text += json[index + 1];
2596
+ index += 1;
2597
+ } else if (char === '"') {
2598
+ inString = false;
2599
+ last = stringIsKey ? "key" : "string";
2600
+ }
2601
+ continue;
2602
+ }
2603
+ if (/\s/.test(char)) {
2604
+ text += char;
2605
+ continue;
2606
+ }
2607
+ if (char === "}" || char === "]") {
2608
+ const depth = open.lastIndexOf(char === "}" ? "{" : "[");
2609
+ if (depth === -1) return void 0;
2610
+ const missing = open.length - 1 - depth;
2611
+ if (missing > 0) {
2612
+ if (last !== "string" && last !== "bare" && last !== "close") return void 0;
2613
+ for (let skipped = 0; skipped < missing; skipped++) text += CLOSER[open.pop()];
2614
+ added += missing;
2615
+ }
2616
+ open.pop();
2617
+ last = "close";
2618
+ } else if (char === "{" || char === "[") {
2619
+ open.push(char);
2620
+ last = "open";
2621
+ } else if (char === '"') {
2622
+ inString = true;
2623
+ stringIsKey = open.at(-1) === "{" && (last === "open" || last === "comma");
2624
+ } else if (char === ",") {
2625
+ last = "comma";
2626
+ } else if (char === ":") {
2627
+ last = "colon";
2628
+ } else {
2629
+ last = "bare";
2630
+ }
2631
+ text += char;
2632
+ }
2633
+ if (inString || last !== "string" && last !== "close") return void 0;
2634
+ added += open.length;
2635
+ while (open.length > 0) text += CLOSER[open.pop()];
2636
+ return added > 0 && added <= MAX_MISSING_CLOSERS ? { text, added } : void 0;
2637
+ }
2638
+
2639
+ // src/agent-run/premature-closer.ts
2640
+ function dropPrematureCloser(json) {
2641
+ const open = [];
2642
+ let inString = false;
2643
+ for (let index = 0; index < json.length; index++) {
2644
+ const char = json[index];
2645
+ if (inString) {
2646
+ if (char === "\\") index += 1;
2647
+ else if (char === '"') inString = false;
2648
+ continue;
2649
+ }
2650
+ if (char === '"') inString = true;
2651
+ else if (char === "{" || char === "[") open.push(char);
2652
+ else if (char === "}" || char === "]") {
2653
+ if (open.at(-1) !== (char === "}" ? "{" : "[")) return void 0;
2654
+ open.pop();
2655
+ if (open.length === 0) {
2656
+ const rest = json.slice(index + 1);
2657
+ if (!/^\s*,/.test(rest) || index === 0) return void 0;
2658
+ return { text: json.slice(0, index) + rest };
2659
+ }
2660
+ }
2661
+ }
2662
+ return void 0;
2663
+ }
2664
+
2665
+ // src/agent-run/stray-closers.ts
2666
+ var MAX_STRAY_CLOSERS = 2;
2667
+ function dropStrayClosers(json) {
2668
+ const open = [];
2669
+ let text = "";
2670
+ let inside = 0;
2671
+ let end = 0;
2672
+ let inString = false;
2673
+ let started = false;
2674
+ for (let index = 0; index < json.length; index++) {
2675
+ const char = json[index];
2676
+ if (inString) {
2677
+ text += char;
2678
+ if (char === "\\") {
2679
+ text += json[index + 1] ?? "";
2680
+ index += 1;
2681
+ } else if (char === '"') {
2682
+ inString = false;
2683
+ }
2684
+ continue;
2685
+ }
2686
+ const finished = started && open.length === 0;
2687
+ if (char === "}" || char === "]") {
2688
+ if (open.at(-1) === (char === "}" ? "{" : "[")) {
2689
+ open.pop();
2690
+ text += char;
2691
+ } else if (finished) {
2692
+ end += 1;
2693
+ } else if (open.length > 0) {
2694
+ inside += 1;
2695
+ } else {
2696
+ return void 0;
2697
+ }
2698
+ continue;
2699
+ }
2700
+ if (finished && !/\s/.test(char)) return void 0;
2701
+ if (char === "{" || char === "[") {
2702
+ open.push(char);
2703
+ started = true;
2704
+ } else if (char === '"') {
2705
+ inString = true;
2706
+ }
2707
+ text += char;
2708
+ }
2709
+ const dropped = inside + end;
2710
+ if (dropped === 0 || dropped > MAX_STRAY_CLOSERS || inString || open.length > 0) return void 0;
2711
+ return { text, inside, end };
2712
+ }
2713
+
2714
+ // src/agent-run/tool-arguments-repair.ts
2715
+ function parseArgumentsLoosely(json) {
2716
+ const none = { inside: 0, end: 0 };
2717
+ const attempts = [
2718
+ { text: json, strayClosers: none, missingClosers: 0 },
2719
+ ...optional(dropStrayClosers(json), (fix) => ({ text: fix.text, strayClosers: { inside: fix.inside, end: fix.end }, missingClosers: 0 })),
2720
+ ...optional(addMissingClosers(json), (fix) => ({ text: fix.text, strayClosers: none, missingClosers: fix.added })),
2721
+ ...optional(dropPrematureCloser(json), (fix) => ({ text: fix.text, strayClosers: { inside: 1, end: 0 }, missingClosers: 0 }))
2722
+ ];
2723
+ for (const { text, ...rest } of attempts) {
2724
+ try {
2725
+ return { value: JSON.parse(text), ...rest };
2726
+ } catch {
2727
+ }
2728
+ }
2729
+ return void 0;
2730
+ }
2731
+ function repairToolArguments(text) {
2732
+ const parsed = parseArgumentsLoosely(repairJson(text));
2733
+ return parsed === void 0 ? void 0 : JSON.stringify(parsed.value);
2734
+ }
2735
+ function optional(fix, toAttempt) {
2736
+ return fix === void 0 ? [] : [toAttempt(fix)];
2737
+ }
2738
+
2515
2739
  // src/storage/chat-storage.ts
2516
2740
  var RELEASED_ANSWER_ALIVE_AT = (/* @__PURE__ */ new Date(0)).toISOString();
2517
2741
  function aliveAtByLocalClock(mark) {
@@ -4169,9 +4393,9 @@ function createStepCacheMarks(mark) {
4169
4393
  const carried = previous;
4170
4394
  const unmarked = carried ? messages.map((message) => message === carried.marked ? carried.original : message) : messages;
4171
4395
  const original = unmarked[unmarked.length - 1];
4172
- const marked = withCacheMark(original, mark);
4173
- previous = { marked, original };
4174
- return [...unmarked.slice(0, -1), marked];
4396
+ const marked2 = withCacheMark(original, mark);
4397
+ previous = { marked: marked2, original };
4398
+ return [...unmarked.slice(0, -1), marked2];
4175
4399
  }
4176
4400
  };
4177
4401
  }
@@ -4327,7 +4551,7 @@ function undeliveredIdsOf(messages) {
4327
4551
  for (const [path, value] of Object.entries(set)) {
4328
4552
  if (path.startsWith(prefix) && value === true) ids.add(path.slice(prefix.length));
4329
4553
  else if (path === UNDELIVERED_STATE_KEY && typeof value === "object" && value !== null) {
4330
- for (const [id, marked] of Object.entries(value)) if (marked === true) ids.add(id);
4554
+ for (const [id, marked2] of Object.entries(value)) if (marked2 === true) ids.add(id);
4331
4555
  }
4332
4556
  }
4333
4557
  }
@@ -5061,252 +5285,127 @@ function checkProviderFallback(setting, catalog) {
5061
5285
  // src/agent-run/repair-tool-call.ts
5062
5286
  import { InvalidToolInputError, asSchema as asSchema2 } from "ai";
5063
5287
 
5064
- // src/agent-run/json-repair.ts
5065
- var VALID_JSON_ESCAPES = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]);
5066
- function isControlCharacter(char) {
5067
- const codePoint = char.codePointAt(0);
5068
- return codePoint !== void 0 && codePoint >= 0 && codePoint <= 31;
5069
- }
5070
- function escapeControlCharacter(char) {
5071
- switch (char) {
5072
- case "\b":
5073
- return "\\b";
5074
- case "\f":
5075
- return "\\f";
5076
- case "\n":
5077
- return "\\n";
5078
- case "\r":
5079
- return "\\r";
5080
- case " ":
5081
- return "\\t";
5082
- default:
5083
- return `\\u${char.codePointAt(0)?.toString(16).padStart(4, "0") ?? "0000"}`;
5084
- }
5085
- }
5086
- function repairJson(json) {
5087
- let repaired = "";
5088
- let inString = false;
5089
- for (let index = 0; index < json.length; index++) {
5090
- const char = json[index];
5091
- if (!inString) {
5092
- repaired += char;
5093
- if (char === '"') inString = true;
5094
- continue;
5095
- }
5096
- if (char === '"') {
5097
- repaired += char;
5098
- inString = false;
5099
- continue;
5100
- }
5101
- if (char === "\\") {
5102
- const nextChar = json[index + 1];
5103
- if (nextChar === void 0) {
5104
- repaired += "\\\\";
5105
- continue;
5106
- }
5107
- if (nextChar === "u") {
5108
- const unicodeDigits = json.slice(index + 2, index + 6);
5109
- if (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {
5110
- repaired += `\\u${unicodeDigits}`;
5111
- index += 5;
5112
- continue;
5113
- }
5114
- }
5115
- if (VALID_JSON_ESCAPES.has(nextChar)) {
5116
- repaired += `\\${nextChar}`;
5117
- index += 1;
5118
- continue;
5119
- }
5120
- repaired += "\\\\";
5121
- continue;
5122
- }
5123
- repaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;
5124
- }
5125
- return repaired;
5126
- }
5127
-
5128
- // src/agent-run/missing-closers.ts
5129
- var MAX_MISSING_CLOSERS = 3;
5130
- var CLOSER = { "{": "}", "[": "]" };
5131
- function addMissingClosers(json) {
5132
- const open = [];
5133
- let text = "";
5134
- let added = 0;
5135
- let last = "none";
5136
- let inString = false;
5137
- let stringIsKey = false;
5138
- for (let index = 0; index < json.length; index++) {
5139
- const char = json[index];
5140
- if (inString) {
5141
- text += char;
5142
- if (char === "\\") {
5143
- if (index + 1 >= json.length) return void 0;
5144
- text += json[index + 1];
5145
- index += 1;
5146
- } else if (char === '"') {
5147
- inString = false;
5148
- last = stringIsKey ? "key" : "string";
5149
- }
5150
- continue;
5151
- }
5152
- if (/\s/.test(char)) {
5153
- text += char;
5154
- continue;
5155
- }
5156
- if (char === "}" || char === "]") {
5157
- const depth = open.lastIndexOf(char === "}" ? "{" : "[");
5158
- if (depth === -1) return void 0;
5159
- const missing = open.length - 1 - depth;
5160
- if (missing > 0) {
5161
- if (last !== "string" && last !== "bare" && last !== "close") return void 0;
5162
- for (let skipped = 0; skipped < missing; skipped++) text += CLOSER[open.pop()];
5163
- added += missing;
5164
- }
5165
- open.pop();
5166
- last = "close";
5167
- } else if (char === "{" || char === "[") {
5168
- open.push(char);
5169
- last = "open";
5170
- } else if (char === '"') {
5171
- inString = true;
5172
- stringIsKey = open.at(-1) === "{" && (last === "open" || last === "comma");
5173
- } else if (char === ",") {
5174
- last = "comma";
5175
- } else if (char === ":") {
5176
- last = "colon";
5177
- } else {
5178
- last = "bare";
5179
- }
5180
- text += char;
5181
- }
5182
- if (inString || last !== "string" && last !== "close") return void 0;
5183
- added += open.length;
5184
- while (open.length > 0) text += CLOSER[open.pop()];
5185
- return added > 0 && added <= MAX_MISSING_CLOSERS ? { text, added } : void 0;
5186
- }
5187
-
5188
- // src/agent-run/stray-closers.ts
5189
- var MAX_STRAY_CLOSERS = 2;
5190
- function dropStrayClosers(json) {
5191
- const open = [];
5192
- let text = "";
5193
- let inside = 0;
5194
- let end = 0;
5195
- let inString = false;
5196
- let started = false;
5197
- for (let index = 0; index < json.length; index++) {
5198
- const char = json[index];
5199
- if (inString) {
5200
- text += char;
5201
- if (char === "\\") {
5202
- text += json[index + 1] ?? "";
5203
- index += 1;
5204
- } else if (char === '"') {
5205
- inString = false;
5206
- }
5207
- continue;
5208
- }
5209
- const finished = started && open.length === 0;
5210
- if (char === "}" || char === "]") {
5211
- if (open.at(-1) === (char === "}" ? "{" : "[")) {
5212
- open.pop();
5213
- text += char;
5214
- } else if (finished) {
5215
- end += 1;
5216
- } else if (open.length > 0) {
5217
- inside += 1;
5218
- } else {
5219
- return void 0;
5220
- }
5221
- continue;
5222
- }
5223
- if (finished && !/\s/.test(char)) return void 0;
5224
- if (char === "{" || char === "[") {
5225
- open.push(char);
5226
- started = true;
5227
- } else if (char === '"') {
5228
- inString = true;
5229
- }
5230
- text += char;
5231
- }
5232
- const dropped = inside + end;
5233
- if (dropped === 0 || dropped > MAX_STRAY_CLOSERS || inString || open.length > 0) return void 0;
5234
- return { text, inside, end };
5288
+ // src/agent-run/unreadable-tool-input.ts
5289
+ import { JSONParseError, TypeValidationError } from "ai";
5290
+ var MAX_PIECE = 30;
5291
+ var MAX_ISSUES = 3;
5292
+ var MAX_ISSUE_LENGTH = 120;
5293
+ var RETRY_NOTE = "\u043F\u0440\u0438\u0448\u043B\u0438 \u0432\u044B\u0437\u043E\u0432 \u0437\u0430\u043D\u043E\u0432\u043E";
5294
+ function describeUnreadableToolInput(error, input) {
5295
+ const cause = error instanceof Error ? error.cause : void 0;
5296
+ if (JSONParseError.isInstance(cause)) return describeBrokenJson(cause, input);
5297
+ if (TypeValidationError.isInstance(cause)) return describeSchemaMismatch(cause);
5298
+ return `\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0443; ${RETRY_NOTE}`;
5299
+ }
5300
+ function describeBrokenJson(error, input) {
5301
+ const reason = error.cause instanceof Error ? error.cause.message : "";
5302
+ const position = /position (\d+)/.exec(reason)?.[1];
5303
+ if (position === void 0) {
5304
+ return `\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043A\u0430\u043A JSON: \u0442\u0435\u043A\u0441\u0442 \u043E\u0431\u043E\u0440\u0432\u0430\u043D \u043D\u0430 \u043F\u043E\u0437\u0438\u0446\u0438\u0438 ${input.length} (\u2026${piece(input, input.length)}); ${RETRY_NOTE}`;
5305
+ }
5306
+ const at = Number(position);
5307
+ const char = input[at];
5308
+ const what = char === "}" || char === "]" ? `\u043B\u0438\u0448\u043D\u044F\u044F \`${char}\`` : "\u043E\u0448\u0438\u0431\u043A\u0430";
5309
+ return `\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043A\u0430\u043A JSON: ${what} \u043E\u043A\u043E\u043B\u043E \u043F\u043E\u0437\u0438\u0446\u0438\u0438 ${at} (\u2026${piece(input, at)}\u2026); ${RETRY_NOTE}`;
5310
+ }
5311
+ function piece(input, at) {
5312
+ const start = Math.max(0, at - MAX_PIECE / 2);
5313
+ return JSON.stringify(input.slice(start, start + MAX_PIECE)).slice(1, -1);
5314
+ }
5315
+ function describeSchemaMismatch(error) {
5316
+ const issues = error.cause?.issues;
5317
+ if (!Array.isArray(issues) || issues.length === 0) {
5318
+ const text = error.cause instanceof Error ? error.cause.message : "\u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442 \u043F\u043E \u0441\u0445\u0435\u043C\u0435";
5319
+ return `\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442 \u043F\u043E \u0441\u0445\u0435\u043C\u0435: ${text.slice(0, MAX_ISSUE_LENGTH)}; ${RETRY_NOTE}`;
5320
+ }
5321
+ const listed = issues.slice(0, MAX_ISSUES).map(describeIssue).join("; ");
5322
+ const rest = issues.length > MAX_ISSUES ? ` (\u0438 \u0435\u0449\u0451 ${issues.length - MAX_ISSUES})` : "";
5323
+ return `\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442 \u043F\u043E \u0441\u0445\u0435\u043C\u0435 \u2014 ${listed}${rest}; ${RETRY_NOTE}, \u043F\u043E\u043F\u0440\u0430\u0432\u0438\u0432 \u044D\u0442\u0438 \u043F\u043E\u043B\u044F`;
5324
+ }
5325
+ function describeIssue(issue) {
5326
+ const { path, message } = issue;
5327
+ const where = Array.isArray(path) && path.length > 0 ? path.join(".") : "\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B";
5328
+ return `${where}: ${String(message ?? "\u043D\u0435\u0432\u0435\u0440\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435").slice(0, MAX_ISSUE_LENGTH)}`;
5235
5329
  }
5236
5330
 
5237
5331
  // src/agent-run/repair-tool-call.ts
5238
5332
  function createRepairToolCall(options2 = {}) {
5239
5333
  return async ({ toolCall, tools, error, inputSchema }) => {
5240
5334
  if (!InvalidToolInputError.isInstance(error)) return null;
5241
- const escaped = repairJson(toolCall.input);
5242
- const parsed = parseWithClosers(escaped);
5243
- if (!parsed) return null;
5244
5335
  const schema = await inputSchema({ toolName: toolCall.toolName });
5245
- const coerced = coerceBySchema(parsed.value, schema);
5246
- const input = JSON.stringify(coerced);
5247
- if (options2.onRepaired && await passesSchema(tools[toolCall.toolName]?.inputSchema, coerced)) {
5248
- options2.onRepaired({
5336
+ const toolSchema = tools[toolCall.toolName]?.inputSchema;
5337
+ const attempt = async (text, byApp) => {
5338
+ const escaped = repairJson(text);
5339
+ const parsed = parseArgumentsLoosely(escaped);
5340
+ if (!parsed) return void 0;
5341
+ const tally = { inside: parsed.strayClosers.inside, end: parsed.strayClosers.end, missing: parsed.missingClosers, nested: false, types: false };
5342
+ const coerced = coerceBySchema(parsed.value, schema, tally);
5343
+ if (!await passesSchema(toolSchema, coerced)) return void 0;
5344
+ options2.onRepaired?.({
5249
5345
  toolName: toolCall.toolName,
5250
5346
  inputLength: toolCall.input.length,
5251
- strayClosers: parsed.strayClosers,
5252
- missingClosers: parsed.missingClosers,
5253
- strings: escaped !== toolCall.input,
5254
- types: input !== JSON.stringify(parsed.value)
5347
+ strayClosers: { inside: tally.inside, end: tally.end },
5348
+ missingClosers: tally.missing,
5349
+ nestedJson: tally.nested,
5350
+ byApp,
5351
+ strings: escaped !== text,
5352
+ types: tally.types
5255
5353
  });
5256
- }
5257
- return { ...toolCall, input };
5354
+ return JSON.stringify(coerced);
5355
+ };
5356
+ const builtIn = await attempt(toolCall.input, false);
5357
+ if (builtIn !== void 0) return { ...toolCall, input: builtIn };
5358
+ const fromApp = await askApp(options2.repairToolInput, { toolName: toolCall.toolName, text: toolCall.input, error });
5359
+ const repaired = fromApp === void 0 ? void 0 : await attempt(fromApp, true);
5360
+ if (repaired !== void 0) return { ...toolCall, input: repaired };
5361
+ error.message = `Invalid input for tool ${toolCall.toolName}: ${describeUnreadableToolInput(error, toolCall.input)}`;
5362
+ return null;
5258
5363
  };
5259
5364
  }
5365
+ async function askApp(repair, call) {
5366
+ if (!repair) return void 0;
5367
+ try {
5368
+ const text = await repair(call);
5369
+ return typeof text === "string" ? text : void 0;
5370
+ } catch {
5371
+ return void 0;
5372
+ }
5373
+ }
5260
5374
  function describeToolInputRepair(repair, where) {
5261
5375
  const fixes = [
5262
5376
  ...repair.strayClosers.inside > 0 ? [`\u043B\u0438\u0448\u043D\u0438\u0435 \u0441\u043A\u043E\u0431\u043A\u0438 \u0432\u043D\u0443\u0442\u0440\u0438 \xD7${repair.strayClosers.inside}`] : [],
5263
5377
  ...repair.strayClosers.end > 0 ? [`\u043B\u0438\u0448\u043D\u0438\u0435 \u0441\u043A\u043E\u0431\u043A\u0438 \u0432 \u043A\u043E\u043D\u0446\u0435 \xD7${repair.strayClosers.end}`] : [],
5264
5378
  ...repair.missingClosers > 0 ? [`\u0434\u043E\u043F\u0438\u0441\u0430\u043D\u044B \u0441\u043A\u043E\u0431\u043A\u0438 \xD7${repair.missingClosers}`] : [],
5379
+ ...repair.nestedJson ? ["JSON \u0438\u0437 \u0441\u0442\u0440\u043E\u043A\u0438"] : [],
5265
5380
  ...repair.strings ? ["\u0437\u0430\u043F\u0438\u0441\u0438 \u0432 \u0441\u0442\u0440\u043E\u043A\u0430\u0445"] : [],
5266
- ...repair.types ? ["\u0442\u0438\u043F\u044B \u043F\u043E \u0441\u0445\u0435\u043C\u0435"] : []
5381
+ ...repair.types ? ["\u0442\u0438\u043F\u044B \u043F\u043E \u0441\u0445\u0435\u043C\u0435"] : [],
5382
+ ...repair.byApp ? ["\u043F\u0440\u0430\u0432\u043A\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F"] : []
5267
5383
  ];
5268
5384
  return `[\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u044B \u043F\u043E\u0447\u0438\u043D\u0435\u043D\u044B] \u0447\u0430\u0442=${where.chatId} \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442=${repair.toolName} \u043C\u043E\u0434\u0435\u043B\u044C=${where.modelId} \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440=${where.providerId} \u043F\u0440\u0430\u0432\u043A\u0430=${fixes.join(", ")} \u0434\u043B\u0438\u043D\u0430=${repair.inputLength}`;
5269
5385
  }
5270
- function parseWithClosers(json) {
5271
- const none = { inside: 0, end: 0 };
5272
- const attempts = [
5273
- { text: json, strayClosers: none, missingClosers: 0 },
5274
- ...optional(dropStrayClosers(json), (fix) => ({ text: fix.text, strayClosers: { inside: fix.inside, end: fix.end }, missingClosers: 0 })),
5275
- ...optional(addMissingClosers(json), (fix) => ({ text: fix.text, strayClosers: none, missingClosers: fix.added }))
5276
- ];
5277
- for (const { text, ...rest } of attempts) {
5278
- try {
5279
- return { value: JSON.parse(text), ...rest };
5280
- } catch {
5281
- }
5282
- }
5283
- return void 0;
5284
- }
5285
- function optional(fix, toAttempt) {
5286
- return fix === void 0 ? [] : [toAttempt(fix)];
5287
- }
5288
5386
  async function passesSchema(schema, value) {
5289
5387
  if (schema === void 0) return false;
5290
5388
  const validate = asSchema2(schema).validate;
5291
5389
  return validate ? (await validate(value)).success : true;
5292
5390
  }
5293
- function coerceBySchema(value, schema) {
5391
+ function coerceBySchema(value, schema, tally) {
5294
5392
  if (typeof schema !== "object") return value;
5295
5393
  const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
5296
5394
  if (typeof value === "string") {
5297
5395
  if (types.includes("string")) return value;
5298
- if (types.includes("integer") || types.includes("number")) return numberFrom(value, types.includes("number")) ?? value;
5299
- if (types.includes("boolean")) return value === "true" ? true : value === "false" ? false : value;
5396
+ if (types.includes("integer") || types.includes("number")) return marked(numberFrom(value, types.includes("number")), value, tally);
5397
+ if (types.includes("boolean")) return marked(value === "true" ? true : value === "false" ? false : void 0, value, tally);
5398
+ if (types.includes("array") || types.includes("object")) return parseNestedJson(value, schema, tally);
5300
5399
  return value;
5301
5400
  }
5302
5401
  if (Array.isArray(value)) {
5303
5402
  const items = Array.isArray(schema.items) ? void 0 : schema.items;
5304
- return value.map((item) => coerceBySchema(item, items));
5403
+ return value.map((item) => coerceBySchema(item, items, tally));
5305
5404
  }
5306
5405
  if (typeof value === "object" && value !== null) {
5307
5406
  const properties = schema.properties ?? {};
5308
5407
  return Object.fromEntries(
5309
- Object.entries(value).map(([key, property]) => [key, coerceBySchema(property, properties[key])])
5408
+ Object.entries(value).map(([key, property]) => [key, coerceBySchema(property, properties[key], tally)])
5310
5409
  );
5311
5410
  }
5312
5411
  return value;
@@ -5317,6 +5416,21 @@ function numberFrom(text, allowFraction) {
5317
5416
  if (!Number.isFinite(number)) return void 0;
5318
5417
  return allowFraction || Number.isInteger(number) ? number : void 0;
5319
5418
  }
5419
+ function marked(coerced, original, tally) {
5420
+ if (coerced === void 0) return original;
5421
+ tally.types = true;
5422
+ return coerced;
5423
+ }
5424
+ function parseNestedJson(text, schema, tally) {
5425
+ if (!/^\s*[[{]/.test(text)) return text;
5426
+ const parsed = parseArgumentsLoosely(repairJson(text));
5427
+ if (!parsed) return text;
5428
+ tally.nested = true;
5429
+ tally.inside += parsed.strayClosers.inside;
5430
+ tally.end += parsed.strayClosers.end;
5431
+ tally.missing += parsed.missingClosers;
5432
+ return coerceBySchema(parsed.value, schema, tally);
5433
+ }
5320
5434
 
5321
5435
  // src/agent-run/run-agent-turn.ts
5322
5436
  import { stepCountIs, streamText } from "ai";
@@ -5465,6 +5579,8 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5465
5579
  let lastWriteAt = Date.now();
5466
5580
  let lastSignAt = lastWriteAt;
5467
5581
  let unsaved = false;
5582
+ let changes = 0;
5583
+ let savedChanges = 0;
5468
5584
  let closed = false;
5469
5585
  let writing = null;
5470
5586
  let lastFence = 0;
@@ -5484,6 +5600,7 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5484
5600
  }
5485
5601
  async function write() {
5486
5602
  unsaved = false;
5603
+ const writtenChanges = changes;
5487
5604
  lastWriteAt = Date.now();
5488
5605
  lastSignAt = lastWriteAt;
5489
5606
  const parts = latest;
@@ -5497,6 +5614,7 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5497
5614
  } catch (failure) {
5498
5615
  noteFailure(failure);
5499
5616
  }
5617
+ savedChanges = Math.max(savedChanges, writtenChanges);
5500
5618
  callsWritten(parts);
5501
5619
  }
5502
5620
  async function markAlive() {
@@ -5574,7 +5692,7 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5574
5692
  reachedFence = fence;
5575
5693
  reachFence(fence);
5576
5694
  } else {
5577
- unsaved = true;
5695
+ noteChange();
5578
5696
  scheduleWrite();
5579
5697
  }
5580
5698
  }
@@ -5588,13 +5706,18 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5588
5706
  if (waiting) waiting.push(chunk);
5589
5707
  else feed.enqueue(chunk);
5590
5708
  }
5709
+ function noteChange() {
5710
+ changes++;
5711
+ unsaved = true;
5712
+ }
5591
5713
  async function writeNow() {
5714
+ const target = changes;
5592
5715
  for (; ; ) {
5593
5716
  clearTimeout(timer);
5594
5717
  timer = void 0;
5595
5718
  if (writing) await writing;
5596
- else if (unsaved && !closed) startWrite();
5597
- else return;
5719
+ else if (savedChanges < target && !closed) startWrite();
5720
+ else return scheduleWrite();
5598
5721
  }
5599
5722
  }
5600
5723
  async function settleAssembly() {
@@ -5632,7 +5755,7 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5632
5755
  const held = waiting;
5633
5756
  waiting = null;
5634
5757
  for (const chunk of held) feed.enqueue(chunk);
5635
- unsaved = true;
5758
+ noteChange();
5636
5759
  await writeNow();
5637
5760
  };
5638
5761
  const current = erase().finally(() => {
@@ -5651,7 +5774,7 @@ function createAnswerSaver(storage, messageId, durations, usage, onTakenOver, ex
5651
5774
  clearTimeout(aliveTimer);
5652
5775
  await rewinding?.catch(() => {
5653
5776
  });
5654
- unsaved = true;
5777
+ noteChange();
5655
5778
  await settleAssembly();
5656
5779
  await writeNow();
5657
5780
  },
@@ -6218,9 +6341,9 @@ function trackToolErrors(tools, options2 = {}) {
6218
6341
  const thrownByTools = /* @__PURE__ */ new WeakSet();
6219
6342
  const scrub = (text) => scrubSecrets(text, secrets);
6220
6343
  function remember(error) {
6221
- const marked = withoutSecrets(typeof error === "object" && error !== null ? error : new Error(String(error)), scrub);
6222
- thrownByTools.add(marked);
6223
- throw marked;
6344
+ const marked2 = withoutSecrets(typeof error === "object" && error !== null ? error : new Error(String(error)), scrub);
6345
+ thrownByTools.add(marked2);
6346
+ throw marked2;
6224
6347
  }
6225
6348
  const tracked = {};
6226
6349
  for (const [name, original] of Object.entries(tools ?? {})) {
@@ -7346,6 +7469,7 @@ async function openAttempt(input, attempt, attemptSignal) {
7346
7469
  memory: _memory,
7347
7470
  resume: _resume,
7348
7471
  toolTimeout: _toolTimeout,
7472
+ repairToolInput: _repairToolInput,
7349
7473
  sequentialTools: _sequentialTools,
7350
7474
  now: _now,
7351
7475
  usageSource: _usageSource,
@@ -7473,6 +7597,7 @@ async function openAttempt(input, attempt, attemptSignal) {
7473
7597
  abortSignal: attemptSignal,
7474
7598
  // Кривые аргументы чинятся по схеме без обращения к модели; каждая починка — строкой в журнал.
7475
7599
  repairToolCall: createRepairToolCall({
7600
+ ...input.repairToolInput && { repairToolInput: input.repairToolInput },
7476
7601
  onRepaired: (repair) => logError(describeToolInputRepair(repair, { chatId: input.chatId, modelId: found.profile.id, providerId }))
7477
7602
  }),
7478
7603
  // Размер контекста после ответа — по последнему шагу: у прежних шагов тот же префикс, сумма считала бы его много раз.
@@ -7628,6 +7753,7 @@ function helperRunContext(input, toolQueue, logError) {
7628
7753
  logError,
7629
7754
  ...input.toolResultLimit && { toolResultLimit: input.toolResultLimit },
7630
7755
  ...input.toolTimeout && { toolTimeout: input.toolTimeout },
7756
+ ...input.repairToolInput && { repairToolInput: input.repairToolInput },
7631
7757
  ...input.sequentialTools && { sequentialTools: input.sequentialTools },
7632
7758
  ...input.providerFallback !== void 0 && { providerFallback: input.providerFallback },
7633
7759
  ...input.providerOutages && { providerOutages: input.providerOutages },
@@ -7993,6 +8119,7 @@ async function helperStep(input) {
7993
8119
  maxRetries: 0,
7994
8120
  abortSignal: input.abortSignal,
7995
8121
  repairToolCall: createRepairToolCall({
8122
+ ...context.repairToolInput && { repairToolInput: context.repairToolInput },
7996
8123
  onRepaired: (repair) => context.logError(describeToolInputRepair(repair, { chatId: context.chatId, modelId: helper.model.modelId, providerId }))
7997
8124
  })
7998
8125
  });
@@ -12127,6 +12254,7 @@ function createChatService(input) {
12127
12254
  providerFallback,
12128
12255
  cacheMissLog,
12129
12256
  toolTimeoutMs: toolTimeoutSetting,
12257
+ repairToolInput,
12130
12258
  sequentialTools: sequentialToolsSetting,
12131
12259
  timeZone: timeZoneSetting,
12132
12260
  modelSilenceTimeout,
@@ -12514,8 +12642,8 @@ function createChatService(input) {
12514
12642
  const ids = pendingUndelivered.get(chatId);
12515
12643
  pendingUndelivered.delete(chatId);
12516
12644
  if (!ids || !await storage.getChat(chatId)) return;
12517
- const marked = undeliveredIdsOf(await currentBranch(chatId));
12518
- const fresh = [...ids].filter((id) => !marked.has(id));
12645
+ const marked2 = undeliveredIdsOf(await currentBranch(chatId));
12646
+ const fresh = [...ids].filter((id) => !marked2.has(id));
12519
12647
  if (fresh.length === 0) return;
12520
12648
  const state = await readBranchState(storage, chatId);
12521
12649
  await storage.insertMessage(stateMessage({ chatId, parentId: state.atMessageId, record: undeliveredRecord(...fresh) }));
@@ -12841,6 +12969,7 @@ function createChatService(input) {
12841
12969
  ...staff && { staff },
12842
12970
  toolResultLimit,
12843
12971
  toolTimeout,
12972
+ ...repairToolInput && { repairToolInput },
12844
12973
  sequentialTools,
12845
12974
  toolQueue,
12846
12975
  stepContextTimeoutMs,
@@ -14172,6 +14301,7 @@ function createChatService(input) {
14172
14301
  usageSource: "background",
14173
14302
  toolResultLimit,
14174
14303
  toolTimeout,
14304
+ ...repairToolInput && { repairToolInput },
14175
14305
  sequentialTools,
14176
14306
  modelSilence,
14177
14307
  logError,
@@ -14355,6 +14485,7 @@ export {
14355
14485
  cutForgedBlocks,
14356
14486
  toolImageFallbackText,
14357
14487
  modelMaySeeToolResultImages,
14488
+ repairToolArguments,
14358
14489
  RELEASED_ANSWER_ALIVE_AT,
14359
14490
  aliveAtByLocalClock,
14360
14491
  silenceBoundaryByLocalClock,
@@ -14454,4 +14585,4 @@ export {
14454
14585
  InvalidWidgetAnswerError,
14455
14586
  createChatService
14456
14587
  };
14457
- //# sourceMappingURL=chunk-SA3DXJYB.js.map
14588
+ //# sourceMappingURL=chunk-WMP5AKDU.js.map