@ykurilov/agent-chat-core 0.65.1 → 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";
@@ -6227,9 +6341,9 @@ function trackToolErrors(tools, options2 = {}) {
6227
6341
  const thrownByTools = /* @__PURE__ */ new WeakSet();
6228
6342
  const scrub = (text) => scrubSecrets(text, secrets);
6229
6343
  function remember(error) {
6230
- const marked = withoutSecrets(typeof error === "object" && error !== null ? error : new Error(String(error)), scrub);
6231
- thrownByTools.add(marked);
6232
- throw marked;
6344
+ const marked2 = withoutSecrets(typeof error === "object" && error !== null ? error : new Error(String(error)), scrub);
6345
+ thrownByTools.add(marked2);
6346
+ throw marked2;
6233
6347
  }
6234
6348
  const tracked = {};
6235
6349
  for (const [name, original] of Object.entries(tools ?? {})) {
@@ -7355,6 +7469,7 @@ async function openAttempt(input, attempt, attemptSignal) {
7355
7469
  memory: _memory,
7356
7470
  resume: _resume,
7357
7471
  toolTimeout: _toolTimeout,
7472
+ repairToolInput: _repairToolInput,
7358
7473
  sequentialTools: _sequentialTools,
7359
7474
  now: _now,
7360
7475
  usageSource: _usageSource,
@@ -7482,6 +7597,7 @@ async function openAttempt(input, attempt, attemptSignal) {
7482
7597
  abortSignal: attemptSignal,
7483
7598
  // Кривые аргументы чинятся по схеме без обращения к модели; каждая починка — строкой в журнал.
7484
7599
  repairToolCall: createRepairToolCall({
7600
+ ...input.repairToolInput && { repairToolInput: input.repairToolInput },
7485
7601
  onRepaired: (repair) => logError(describeToolInputRepair(repair, { chatId: input.chatId, modelId: found.profile.id, providerId }))
7486
7602
  }),
7487
7603
  // Размер контекста после ответа — по последнему шагу: у прежних шагов тот же префикс, сумма считала бы его много раз.
@@ -7637,6 +7753,7 @@ function helperRunContext(input, toolQueue, logError) {
7637
7753
  logError,
7638
7754
  ...input.toolResultLimit && { toolResultLimit: input.toolResultLimit },
7639
7755
  ...input.toolTimeout && { toolTimeout: input.toolTimeout },
7756
+ ...input.repairToolInput && { repairToolInput: input.repairToolInput },
7640
7757
  ...input.sequentialTools && { sequentialTools: input.sequentialTools },
7641
7758
  ...input.providerFallback !== void 0 && { providerFallback: input.providerFallback },
7642
7759
  ...input.providerOutages && { providerOutages: input.providerOutages },
@@ -8002,6 +8119,7 @@ async function helperStep(input) {
8002
8119
  maxRetries: 0,
8003
8120
  abortSignal: input.abortSignal,
8004
8121
  repairToolCall: createRepairToolCall({
8122
+ ...context.repairToolInput && { repairToolInput: context.repairToolInput },
8005
8123
  onRepaired: (repair) => context.logError(describeToolInputRepair(repair, { chatId: context.chatId, modelId: helper.model.modelId, providerId }))
8006
8124
  })
8007
8125
  });
@@ -12136,6 +12254,7 @@ function createChatService(input) {
12136
12254
  providerFallback,
12137
12255
  cacheMissLog,
12138
12256
  toolTimeoutMs: toolTimeoutSetting,
12257
+ repairToolInput,
12139
12258
  sequentialTools: sequentialToolsSetting,
12140
12259
  timeZone: timeZoneSetting,
12141
12260
  modelSilenceTimeout,
@@ -12523,8 +12642,8 @@ function createChatService(input) {
12523
12642
  const ids = pendingUndelivered.get(chatId);
12524
12643
  pendingUndelivered.delete(chatId);
12525
12644
  if (!ids || !await storage.getChat(chatId)) return;
12526
- const marked = undeliveredIdsOf(await currentBranch(chatId));
12527
- const fresh = [...ids].filter((id) => !marked.has(id));
12645
+ const marked2 = undeliveredIdsOf(await currentBranch(chatId));
12646
+ const fresh = [...ids].filter((id) => !marked2.has(id));
12528
12647
  if (fresh.length === 0) return;
12529
12648
  const state = await readBranchState(storage, chatId);
12530
12649
  await storage.insertMessage(stateMessage({ chatId, parentId: state.atMessageId, record: undeliveredRecord(...fresh) }));
@@ -12850,6 +12969,7 @@ function createChatService(input) {
12850
12969
  ...staff && { staff },
12851
12970
  toolResultLimit,
12852
12971
  toolTimeout,
12972
+ ...repairToolInput && { repairToolInput },
12853
12973
  sequentialTools,
12854
12974
  toolQueue,
12855
12975
  stepContextTimeoutMs,
@@ -14181,6 +14301,7 @@ function createChatService(input) {
14181
14301
  usageSource: "background",
14182
14302
  toolResultLimit,
14183
14303
  toolTimeout,
14304
+ ...repairToolInput && { repairToolInput },
14184
14305
  sequentialTools,
14185
14306
  modelSilence,
14186
14307
  logError,
@@ -14364,6 +14485,7 @@ export {
14364
14485
  cutForgedBlocks,
14365
14486
  toolImageFallbackText,
14366
14487
  modelMaySeeToolResultImages,
14488
+ repairToolArguments,
14367
14489
  RELEASED_ANSWER_ALIVE_AT,
14368
14490
  aliveAtByLocalClock,
14369
14491
  silenceBoundaryByLocalClock,
@@ -14463,4 +14585,4 @@ export {
14463
14585
  InvalidWidgetAnswerError,
14464
14586
  createChatService
14465
14587
  };
14466
- //# sourceMappingURL=chunk-4NQEQWGW.js.map
14588
+ //# sourceMappingURL=chunk-WMP5AKDU.js.map