@ai-sdk-tool/parser 3.0.0-canary.0 → 3.0.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- RJSON: () => robust_json_exports,
24
23
  createDynamicIfThenElseSchema: () => createDynamicIfThenElseSchema,
25
24
  createToolMiddleware: () => createToolMiddleware,
26
25
  decodeOriginalTools: () => decodeOriginalTools,
@@ -37,6 +36,7 @@ __export(src_exports, {
37
36
  isToolChoiceActive: () => isToolChoiceActive,
38
37
  isToolResultPart: () => isToolResultPart,
39
38
  jsonMixProtocol: () => jsonMixProtocol,
39
+ logParseFailure: () => logParseFailure,
40
40
  logParsedChunk: () => logParsedChunk,
41
41
  logParsedSummary: () => logParsedSummary,
42
42
  logRawChunk: () => logRawChunk,
@@ -49,11 +49,8 @@ __export(src_exports, {
49
49
  });
50
50
  module.exports = __toCommonJS(src_exports);
51
51
 
52
- // src/protocols/dummy-protocol.ts
53
- var import_provider_utils = require("@ai-sdk/provider-utils");
54
-
55
52
  // src/protocols/json-mix-protocol.ts
56
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
53
+ var import_provider_utils = require("@ai-sdk/provider-utils");
57
54
 
58
55
  // src/utils/debug.ts
59
56
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -101,6 +98,7 @@ var cBgGreen = color(ANSI_BG_GREEN);
101
98
  var cInverse = color(ANSI_INVERSE);
102
99
  var cUnderline = color(ANSI_UNDERLINE);
103
100
  var cBold = color(ANSI_BOLD);
101
+ var MAX_SNIPPET_LENGTH = 800;
104
102
  function safeStringify(value) {
105
103
  try {
106
104
  return `
@@ -109,6 +107,41 @@ ${typeof value === "string" ? value : JSON.stringify(value, null, 2)}`;
109
107
  return String(value);
110
108
  }
111
109
  }
110
+ function formatError(error) {
111
+ if (error instanceof Error) {
112
+ const stack = error.stack ? `
113
+ ${error.stack}` : "";
114
+ return `
115
+ ${error.name}: ${error.message}${stack}`;
116
+ }
117
+ return safeStringify(error);
118
+ }
119
+ function truncateSnippet(snippet) {
120
+ if (snippet.length <= MAX_SNIPPET_LENGTH) {
121
+ return snippet;
122
+ }
123
+ return `${snippet.slice(0, MAX_SNIPPET_LENGTH)}
124
+ \u2026[truncated ${snippet.length - MAX_SNIPPET_LENGTH} chars]`;
125
+ }
126
+ function logParseFailure({
127
+ phase,
128
+ reason,
129
+ snippet,
130
+ error
131
+ }) {
132
+ if (getDebugLevel() !== "parse") {
133
+ return;
134
+ }
135
+ const label = cBgBlue(`[${phase}]`);
136
+ console.log(cGray("[debug:mw:fail]"), label, cYellow(reason));
137
+ if (snippet) {
138
+ const formatted = truncateSnippet(snippet);
139
+ console.log(cGray("[debug:mw:fail:snippet]"), formatted);
140
+ }
141
+ if (error) {
142
+ console.log(cGray("[debug:mw:fail:error]"), cCyan(formatError(error)));
143
+ }
144
+ }
112
145
  function logRawChunk(part) {
113
146
  console.log(cGray("[debug:mw:raw]"), cYellow(safeStringify(part)));
114
147
  }
@@ -174,63 +207,6 @@ ${rendered}`);
174
207
  }
175
208
  }
176
209
 
177
- // src/utils/dynamic-tool-schema.ts
178
- function createDynamicIfThenElseSchema(tools) {
179
- let currentSchema = {};
180
- const toolNames = [];
181
- for (let i = tools.length - 1; i >= 0; i -= 1) {
182
- const tool = tools[i];
183
- if (tool.type === "provider-defined") {
184
- throw new Error(
185
- "Provider-defined tools are not supported by this middleware. Please use custom tools."
186
- );
187
- }
188
- toolNames.unshift(tool.name);
189
- const toolCondition = {
190
- if: {
191
- properties: {
192
- name: {
193
- const: tool.name
194
- }
195
- },
196
- required: ["name"]
197
- },
198
- // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
199
- then: {
200
- properties: {
201
- name: {
202
- const: tool.name
203
- },
204
- arguments: tool.inputSchema
205
- },
206
- required: ["name", "arguments"]
207
- }
208
- };
209
- if (Object.keys(currentSchema).length > 0) {
210
- toolCondition.else = currentSchema;
211
- }
212
- currentSchema = toolCondition;
213
- }
214
- return {
215
- type: "object",
216
- // Explicitly specify type as "object"
217
- properties: {
218
- name: {
219
- type: "string",
220
- description: "Name of the tool to call",
221
- enum: toolNames
222
- },
223
- arguments: {
224
- type: "object",
225
- // By default, arguments is also specified as object type
226
- description: "Argument object to be passed to the tool"
227
- }
228
- },
229
- required: ["name", "arguments"],
230
- ...currentSchema
231
- };
232
- }
233
-
234
210
  // src/utils/get-potential-start-index.ts
235
211
  function getPotentialStartIndex(text, searchedText) {
236
212
  if (searchedText.length === 0) {
@@ -249,60 +225,12 @@ function getPotentialStartIndex(text, searchedText) {
249
225
  return null;
250
226
  }
251
227
 
252
- // src/utils/on-error.ts
253
- function extractOnErrorOption(providerOptions) {
254
- var _a;
255
- if (providerOptions && typeof providerOptions === "object") {
256
- const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
257
- return onError ? { onError } : void 0;
258
- }
259
- return;
260
- }
261
-
262
- // src/utils/provider-options.ts
263
- var originalToolsSchema = {
264
- encode: encodeOriginalTools,
265
- decode: decodeOriginalTools
266
- };
267
- function encodeOriginalTools(tools) {
268
- return (tools == null ? void 0 : tools.map((t) => ({
269
- name: t.name,
270
- inputSchema: JSON.stringify(t.inputSchema)
271
- }))) || [];
272
- }
273
- function decodeOriginalTools(originalTools) {
274
- if (!originalTools) {
275
- return [];
276
- }
277
- return originalTools.map(
278
- (t) => ({
279
- type: "function",
280
- name: t.name,
281
- inputSchema: JSON.parse(t.inputSchema)
282
- })
283
- );
284
- }
285
- function extractToolNamesFromOriginalTools(originalTools) {
286
- return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
287
- }
288
- function isToolChoiceActive(params) {
289
- var _a, _b, _c;
290
- const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
291
- return !!(typeof params.providerOptions === "object" && params.providerOptions !== null && typeof ((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required"));
292
- }
293
-
294
228
  // src/utils/regex.ts
295
229
  function escapeRegExp(literal) {
296
230
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
297
231
  }
298
232
 
299
233
  // src/utils/robust-json.ts
300
- var robust_json_exports = {};
301
- __export(robust_json_exports, {
302
- parse: () => parse,
303
- stringify: () => stringify,
304
- transform: () => transform
305
- });
306
234
  var WHITESPACE_TEST_REGEX = /\s/;
307
235
  var WHITESPACE_REGEX = /^\s+/;
308
236
  var OBJECT_START_REGEX = /^\{/;
@@ -967,19 +895,6 @@ function stringify(obj) {
967
895
  return "null";
968
896
  }
969
897
 
970
- // src/utils/type-guards.ts
971
- function isToolCallContent(content) {
972
- return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
973
- (typeof content.input === "string" || typeof content.input === "object");
974
- }
975
- function isToolResultPart(content) {
976
- const c = content;
977
- return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
978
- }
979
- function hasInputProperty(obj) {
980
- return typeof obj === "object" && obj !== null && "input" in obj;
981
- }
982
-
983
898
  // src/protocols/json-mix-protocol.ts
984
899
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
985
900
  var _a;
@@ -987,11 +902,17 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
987
902
  const parsedToolCall = parse(toolCallJson);
988
903
  processedElements.push({
989
904
  type: "tool-call",
990
- toolCallId: (0, import_provider_utils2.generateId)(),
905
+ toolCallId: (0, import_provider_utils.generateId)(),
991
906
  toolName: parsedToolCall.name,
992
907
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
993
908
  });
994
909
  } catch (error) {
910
+ logParseFailure({
911
+ phase: "generated-text",
912
+ reason: "Failed to parse tool call JSON segment",
913
+ snippet: fullMatch,
914
+ error
915
+ });
995
916
  if (options == null ? void 0 : options.onError) {
996
917
  options.onError(
997
918
  "Could not process JSON tool call, keeping original text.",
@@ -1024,7 +945,7 @@ function flushBuffer(state, controller, toolCallStart) {
1024
945
  return;
1025
946
  }
1026
947
  if (!state.currentTextId) {
1027
- state.currentTextId = (0, import_provider_utils2.generateId)();
948
+ state.currentTextId = (0, import_provider_utils.generateId)();
1028
949
  controller.enqueue({ type: "text-start", id: state.currentTextId });
1029
950
  state.hasEmittedTextStart = true;
1030
951
  }
@@ -1047,7 +968,12 @@ function emitIncompleteToolCall(state, controller, toolCallStart) {
1047
968
  if (!state.currentToolCallJson) {
1048
969
  return;
1049
970
  }
1050
- const errorId = (0, import_provider_utils2.generateId)();
971
+ logParseFailure({
972
+ phase: "stream",
973
+ reason: "Incomplete streaming tool call segment emitted as text",
974
+ snippet: `${toolCallStart}${state.currentToolCallJson}`
975
+ });
976
+ const errorId = (0, import_provider_utils.generateId)();
1051
977
  controller.enqueue({ type: "text-start", id: errorId });
1052
978
  controller.enqueue({
1053
979
  type: "text-delta",
@@ -1071,7 +997,7 @@ function publishText(text, state, controller) {
1071
997
  state.currentToolCallJson += text;
1072
998
  } else if (text.length > 0) {
1073
999
  if (!state.currentTextId) {
1074
- state.currentTextId = (0, import_provider_utils2.generateId)();
1000
+ state.currentTextId = (0, import_provider_utils.generateId)();
1075
1001
  controller.enqueue({ type: "text-start", id: state.currentTextId });
1076
1002
  state.hasEmittedTextStart = true;
1077
1003
  }
@@ -1086,16 +1012,22 @@ function emitToolCall(context) {
1086
1012
  var _a;
1087
1013
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
1088
1014
  try {
1089
- const parsedToolCall = robust_json_exports.parse(state.currentToolCallJson);
1015
+ const parsedToolCall = parse(state.currentToolCallJson);
1090
1016
  closeTextBlock(state, controller);
1091
1017
  controller.enqueue({
1092
1018
  type: "tool-call",
1093
- toolCallId: (0, import_provider_utils2.generateId)(),
1019
+ toolCallId: (0, import_provider_utils.generateId)(),
1094
1020
  toolName: parsedToolCall.name,
1095
1021
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1096
1022
  });
1097
- } catch (e) {
1098
- const errorId = (0, import_provider_utils2.generateId)();
1023
+ } catch (error) {
1024
+ logParseFailure({
1025
+ phase: "stream",
1026
+ reason: "Failed to parse streaming tool call JSON segment",
1027
+ snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
1028
+ error
1029
+ });
1030
+ const errorId = (0, import_provider_utils.generateId)();
1099
1031
  controller.enqueue({ type: "text-start", id: errorId });
1100
1032
  controller.enqueue({
1101
1033
  type: "text-delta",
@@ -1260,9 +1192,340 @@ var jsonMixProtocol = ({
1260
1192
  });
1261
1193
 
1262
1194
  // src/protocols/morph-xml-protocol.ts
1263
- var import_provider_utils3 = require("@ai-sdk/provider-utils");
1195
+ var import_provider_utils2 = require("@ai-sdk/provider-utils");
1264
1196
  var import_rxml = require("@ai-sdk-tool/rxml");
1197
+
1198
+ // src/utils/type-guards.ts
1199
+ function isToolCallContent(content) {
1200
+ return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
1201
+ (typeof content.input === "string" || typeof content.input === "object");
1202
+ }
1203
+ function isToolResultPart(content) {
1204
+ const c = content;
1205
+ return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
1206
+ }
1207
+ function hasInputProperty(obj) {
1208
+ return typeof obj === "object" && obj !== null && "input" in obj;
1209
+ }
1210
+
1211
+ // src/protocols/morph-xml-protocol.ts
1265
1212
  var WHITESPACE_REGEX2 = /\s/;
1213
+ var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1214
+ var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1215
+ var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
1216
+ var STATUS_TO_STEP_BOUNDARY_RE = /<\/status>\s*<step>/g;
1217
+ var STEP_TAG_RE = /<step>([\s\S]*?)<\/step>/i;
1218
+ var STATUS_TAG_RE = /<status>([\s\S]*?)<\/status>/i;
1219
+ function normalizeCloseTags(xml) {
1220
+ return xml.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1221
+ }
1222
+ function escapeInvalidLt(xml) {
1223
+ const len = xml.length;
1224
+ let out = "";
1225
+ for (let i = 0; i < len; i += 1) {
1226
+ const ch = xml[i];
1227
+ if (ch === "<") {
1228
+ const next = i + 1 < len ? xml[i + 1] : "";
1229
+ if (!(NAME_CHAR_RE.test(next) || next === "/" || next === "!" || next === "?")) {
1230
+ out += "&lt;";
1231
+ continue;
1232
+ }
1233
+ }
1234
+ out += ch;
1235
+ }
1236
+ return out;
1237
+ }
1238
+ function shouldDeduplicateStringTags(schema) {
1239
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1240
+ if (!unwrapped || typeof unwrapped !== "object") {
1241
+ return false;
1242
+ }
1243
+ const props = unwrapped.properties;
1244
+ if (!props) {
1245
+ return false;
1246
+ }
1247
+ const commandRaw = props.command;
1248
+ if (!commandRaw) {
1249
+ return false;
1250
+ }
1251
+ const command = (0, import_rxml.unwrapJsonSchema)(commandRaw);
1252
+ return (command == null ? void 0 : command.type) === "array";
1253
+ }
1254
+ function tryParseSecondaryXml(content, toolSchema, options) {
1255
+ const normalized = normalizeCloseTags(content);
1256
+ const balanced = balanceTags(content);
1257
+ const hasMalformedClose = MALFORMED_CLOSE_RE.test(content);
1258
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1259
+ return null;
1260
+ }
1261
+ try {
1262
+ let parsed = (0, import_rxml.parse)(balanced, toolSchema, {
1263
+ onError: options == null ? void 0 : options.onError,
1264
+ noChildNodes: []
1265
+ });
1266
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1267
+ return parsed;
1268
+ } catch (_e) {
1269
+ if (shouldDeduplicateStringTags(toolSchema)) {
1270
+ const deduped = dedupeStringTagsAgainstSchema(balanced, toolSchema);
1271
+ if (deduped !== balanced) {
1272
+ try {
1273
+ let reparsed = (0, import_rxml.parse)(deduped, toolSchema, {
1274
+ onError: options == null ? void 0 : options.onError,
1275
+ noChildNodes: []
1276
+ });
1277
+ reparsed = repairParsedAgainstSchema(reparsed, toolSchema, options);
1278
+ return reparsed;
1279
+ } catch (_) {
1280
+ return null;
1281
+ }
1282
+ }
1283
+ }
1284
+ return null;
1285
+ }
1286
+ }
1287
+ function balanceTags(xml) {
1288
+ const src = normalizeCloseTags(xml).replace(
1289
+ STATUS_TO_STEP_BOUNDARY_RE,
1290
+ "</status></step><step>"
1291
+ );
1292
+ let i = 0;
1293
+ const len = src.length;
1294
+ const out = [];
1295
+ const stack = [];
1296
+ while (i < len) {
1297
+ const lt = src.indexOf("<", i);
1298
+ if (lt === -1) {
1299
+ out.push(src.slice(i));
1300
+ break;
1301
+ }
1302
+ out.push(src.slice(i, lt));
1303
+ if (lt + 1 >= len) {
1304
+ break;
1305
+ }
1306
+ const next = src[lt + 1];
1307
+ if (next === "!" || next === "?") {
1308
+ i = handleSpecialTagSegment(src, lt, out);
1309
+ continue;
1310
+ }
1311
+ if (next === "/") {
1312
+ i = handleClosingTagSegment(src, lt, out, stack);
1313
+ continue;
1314
+ }
1315
+ i = handleOpeningTagSegment(src, lt, out, stack);
1316
+ }
1317
+ for (let k = stack.length - 1; k >= 0; k -= 1) {
1318
+ out.push(`</${stack[k]}>`);
1319
+ }
1320
+ return out.join("");
1321
+ }
1322
+ function skipWs(s, p, len) {
1323
+ let idx = p;
1324
+ while (idx < len && WHITESPACE_REGEX2.test(s[idx])) {
1325
+ idx += 1;
1326
+ }
1327
+ return idx;
1328
+ }
1329
+ function parseTagNameAt(s, p, len) {
1330
+ let idx = p;
1331
+ const start = idx;
1332
+ while (idx < len && NAME_CHAR_RE.test(s[idx])) {
1333
+ idx += 1;
1334
+ }
1335
+ return { name: s.slice(start, idx), pos: idx };
1336
+ }
1337
+ function handleSpecialTagSegment(src, lt, out) {
1338
+ const gt = src.indexOf(">", lt + 1);
1339
+ if (gt === -1) {
1340
+ out.push(src.slice(lt));
1341
+ return src.length;
1342
+ }
1343
+ out.push(src.slice(lt, gt + 1));
1344
+ return gt + 1;
1345
+ }
1346
+ function handleClosingTagSegment(src, lt, out, stack) {
1347
+ const len = src.length;
1348
+ let p = skipWs(src, lt + 2, len);
1349
+ const { name, pos } = parseTagNameAt(src, p, len);
1350
+ p = pos;
1351
+ const gt = src.indexOf(">", p);
1352
+ const closingText = gt === -1 ? src.slice(lt) : src.slice(lt, gt + 1);
1353
+ const idx = stack.lastIndexOf(name);
1354
+ if (idx !== -1) {
1355
+ for (let k = stack.length - 1; k > idx; k -= 1) {
1356
+ out.push(`</${stack[k]}>`);
1357
+ stack.pop();
1358
+ }
1359
+ out.push(closingText);
1360
+ stack.pop();
1361
+ }
1362
+ return gt === -1 ? len : gt + 1;
1363
+ }
1364
+ function handleOpeningTagSegment(src, lt, out, stack) {
1365
+ const len = src.length;
1366
+ let p = skipWs(src, lt + 1, len);
1367
+ const nameStart = p;
1368
+ const parsed = parseTagNameAt(src, p, len);
1369
+ p = parsed.pos;
1370
+ const name = src.slice(nameStart, p);
1371
+ const q = src.indexOf(">", p);
1372
+ if (q === -1) {
1373
+ out.push(src.slice(lt));
1374
+ return len;
1375
+ }
1376
+ let r = q - 1;
1377
+ while (r >= nameStart && WHITESPACE_REGEX2.test(src[r])) {
1378
+ r -= 1;
1379
+ }
1380
+ const selfClosing = src[r] === "/";
1381
+ out.push(src.slice(lt, q + 1));
1382
+ if (!selfClosing && name) {
1383
+ stack.push(name);
1384
+ }
1385
+ return q + 1;
1386
+ }
1387
+ function repairParsedAgainstSchema(input, schema, options) {
1388
+ if (!input || typeof input !== "object") {
1389
+ return input;
1390
+ }
1391
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1392
+ if (!unwrapped || typeof unwrapped !== "object") {
1393
+ return input;
1394
+ }
1395
+ const properties = unwrapped.properties;
1396
+ if (!properties) {
1397
+ return input;
1398
+ }
1399
+ applySchemaProps(input, properties, options);
1400
+ return input;
1401
+ }
1402
+ function applySchemaProps(obj, properties, options) {
1403
+ for (const key of Object.keys(obj)) {
1404
+ const propSchema = properties[key];
1405
+ if (!propSchema) {
1406
+ continue;
1407
+ }
1408
+ const prop = (0, import_rxml.unwrapJsonSchema)(propSchema);
1409
+ const propType = prop.type;
1410
+ if (propType === "array" && prop.items) {
1411
+ const itemSchemaRaw = prop.items;
1412
+ const itemSchema = (0, import_rxml.unwrapJsonSchema)(itemSchemaRaw);
1413
+ obj[key] = coerceArrayItems(obj[key], itemSchema, options);
1414
+ continue;
1415
+ }
1416
+ if (propType === "object") {
1417
+ const val = obj[key];
1418
+ if (val && typeof val === "object") {
1419
+ obj[key] = repairParsedAgainstSchema(
1420
+ val,
1421
+ prop,
1422
+ options
1423
+ );
1424
+ }
1425
+ }
1426
+ }
1427
+ }
1428
+ function coerceArrayItems(val, itemSchema, options) {
1429
+ if (!Array.isArray(val)) {
1430
+ return val;
1431
+ }
1432
+ return val.map((v) => coerceArrayItem(v, itemSchema, options));
1433
+ }
1434
+ function coerceArrayItem(v, itemSchema, options) {
1435
+ const itemType = itemSchema == null ? void 0 : itemSchema.type;
1436
+ if (typeof v === "string" && itemType === "object") {
1437
+ const parsed = tryParseStringToSchemaObject(v, itemSchema, options);
1438
+ if (parsed !== null) {
1439
+ return parsed;
1440
+ }
1441
+ const fallback = extractStepStatusFromString(normalizeCloseTags(v));
1442
+ if (fallback) {
1443
+ return fallback;
1444
+ }
1445
+ return v;
1446
+ }
1447
+ if (v && typeof v === "object" && itemType === "object") {
1448
+ return repairParsedAgainstSchema(
1449
+ v,
1450
+ itemSchema,
1451
+ options
1452
+ );
1453
+ }
1454
+ return v;
1455
+ }
1456
+ function getStringPropertyNames(schema) {
1457
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1458
+ if (!unwrapped || typeof unwrapped !== "object") {
1459
+ return [];
1460
+ }
1461
+ const props = unwrapped.properties;
1462
+ if (!props) {
1463
+ return [];
1464
+ }
1465
+ const names = [];
1466
+ for (const key of Object.keys(props)) {
1467
+ const prop = (0, import_rxml.unwrapJsonSchema)(
1468
+ props[key]
1469
+ );
1470
+ const type = prop.type;
1471
+ if (type === "string") {
1472
+ names.push(key);
1473
+ }
1474
+ }
1475
+ return names;
1476
+ }
1477
+ function escapeRegExp2(s) {
1478
+ return s.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
1479
+ }
1480
+ function dedupeStringTagsAgainstSchema(xml, schema) {
1481
+ const names = getStringPropertyNames(schema);
1482
+ let out = xml;
1483
+ for (const key of names) {
1484
+ out = dedupeSingleTag(out, key);
1485
+ }
1486
+ return out;
1487
+ }
1488
+ function dedupeSingleTag(xml, key) {
1489
+ var _a, _b;
1490
+ const escaped = escapeRegExp2(key);
1491
+ const re = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
1492
+ const matches = Array.from(xml.matchAll(re));
1493
+ if (matches.length <= 1) {
1494
+ return xml;
1495
+ }
1496
+ const last = matches.at(-1);
1497
+ let result = "";
1498
+ let cursor = 0;
1499
+ for (const m of matches) {
1500
+ const idx = (_a = m.index) != null ? _a : 0;
1501
+ result += xml.slice(cursor, idx);
1502
+ if (last && idx === ((_b = last.index) != null ? _b : -1)) {
1503
+ result += m[0];
1504
+ }
1505
+ cursor = idx + m[0].length;
1506
+ }
1507
+ result += xml.slice(cursor);
1508
+ return result;
1509
+ }
1510
+ function tryParseStringToSchemaObject(xml, itemSchema, options) {
1511
+ try {
1512
+ const fixed = (0, import_rxml.parse)(normalizeCloseTags(xml), itemSchema, {
1513
+ onError: options == null ? void 0 : options.onError,
1514
+ noChildNodes: []
1515
+ });
1516
+ return typeof fixed === "string" ? null : fixed;
1517
+ } catch (e) {
1518
+ return null;
1519
+ }
1520
+ }
1521
+ function extractStepStatusFromString(normXml) {
1522
+ const stepMatch = normXml.match(STEP_TAG_RE);
1523
+ const statusMatch = normXml.match(STATUS_TAG_RE);
1524
+ if (stepMatch && statusMatch) {
1525
+ return { step: stepMatch[1], status: statusMatch[1] };
1526
+ }
1527
+ return null;
1528
+ }
1266
1529
  function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1267
1530
  if (toolCallStartIndex > currentIndex) {
1268
1531
  const textSegment = text.substring(currentIndex, toolCallStartIndex);
@@ -1275,20 +1538,36 @@ function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, proce
1275
1538
  function processToolCall(params) {
1276
1539
  var _a;
1277
1540
  const { toolCall, tools, options, text, processedElements } = params;
1541
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1278
1542
  try {
1279
- const toolSchema = getToolSchema(tools, toolCall.toolName);
1280
- const parsed = (0, import_rxml.parse)(toolCall.content, toolSchema, {
1543
+ const primary = escapeInvalidLt(normalizeCloseTags(toolCall.content));
1544
+ let parsed = (0, import_rxml.parse)(primary, toolSchema, {
1281
1545
  onError: options == null ? void 0 : options.onError,
1282
1546
  // Disable HTML self-closing tag behavior to allow base, meta, link etc. as regular tags
1283
1547
  noChildNodes: []
1284
1548
  });
1549
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1285
1550
  processedElements.push({
1286
1551
  type: "tool-call",
1287
- toolCallId: (0, import_provider_utils3.generateId)(),
1552
+ toolCallId: (0, import_provider_utils2.generateId)(),
1288
1553
  toolName: toolCall.toolName,
1289
1554
  input: JSON.stringify(parsed)
1290
1555
  });
1291
1556
  } catch (error) {
1557
+ const reparsed = tryParseSecondaryXml(
1558
+ toolCall.content,
1559
+ toolSchema,
1560
+ options
1561
+ );
1562
+ if (reparsed !== null) {
1563
+ processedElements.push({
1564
+ type: "tool-call",
1565
+ toolCallId: (0, import_provider_utils2.generateId)(),
1566
+ toolName: toolCall.toolName,
1567
+ input: JSON.stringify(reparsed)
1568
+ });
1569
+ return;
1570
+ }
1292
1571
  const originalCallText = text.substring(
1293
1572
  toolCall.startIndex,
1294
1573
  toolCall.endIndex
@@ -1312,20 +1591,33 @@ function addRemainingText(text, currentIndex, processedElements) {
1312
1591
  }
1313
1592
  function handleStreamingToolCallEnd(params) {
1314
1593
  const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1594
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1315
1595
  try {
1316
- const toolSchema = getToolSchema(tools, currentToolCall.name);
1317
- const parsed = (0, import_rxml.parse)(toolContent, toolSchema, {
1596
+ const primary = escapeInvalidLt(normalizeCloseTags(toolContent));
1597
+ let parsed = (0, import_rxml.parse)(primary, toolSchema, {
1318
1598
  onError: options == null ? void 0 : options.onError,
1319
1599
  noChildNodes: []
1320
1600
  });
1601
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1321
1602
  flushText(ctrl);
1322
1603
  ctrl.enqueue({
1323
1604
  type: "tool-call",
1324
- toolCallId: (0, import_provider_utils3.generateId)(),
1605
+ toolCallId: (0, import_provider_utils2.generateId)(),
1325
1606
  toolName: currentToolCall.name,
1326
1607
  input: JSON.stringify(parsed)
1327
1608
  });
1328
1609
  } catch (error) {
1610
+ const parsed = tryParseSecondaryXml(toolContent, toolSchema, options);
1611
+ if (parsed !== null) {
1612
+ flushText(ctrl);
1613
+ ctrl.enqueue({
1614
+ type: "tool-call",
1615
+ toolCallId: (0, import_provider_utils2.generateId)(),
1616
+ toolName: currentToolCall.name,
1617
+ input: JSON.stringify(parsed)
1618
+ });
1619
+ return;
1620
+ }
1329
1621
  handleStreamingToolCallError({
1330
1622
  error,
1331
1623
  currentToolCall,
@@ -1357,19 +1649,28 @@ function handleStreamingToolCallError(params) {
1357
1649
  flushText(ctrl, originalCallText);
1358
1650
  }
1359
1651
  function findEarliestToolTag(buffer, toolNames) {
1360
- let earliestStartTagIndex = -1;
1361
- let earliestToolName = "";
1652
+ let bestIndex = -1;
1653
+ let bestName = "";
1654
+ let bestSelfClosing = false;
1362
1655
  if (toolNames.length > 0) {
1363
1656
  for (const name of toolNames) {
1364
- const startTag = `<${name}>`;
1365
- const index = buffer.indexOf(startTag);
1366
- if (index !== -1 && (earliestStartTagIndex === -1 || index < earliestStartTagIndex)) {
1367
- earliestStartTagIndex = index;
1368
- earliestToolName = name;
1657
+ const openTag = `<${name}>`;
1658
+ const selfTag = `<${name}/>`;
1659
+ const idxOpen = buffer.indexOf(openTag);
1660
+ const idxSelf = buffer.indexOf(selfTag);
1661
+ if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
1662
+ bestIndex = idxOpen;
1663
+ bestName = name;
1664
+ bestSelfClosing = false;
1665
+ }
1666
+ if (idxSelf !== -1 && (bestIndex === -1 || idxSelf < bestIndex)) {
1667
+ bestIndex = idxSelf;
1668
+ bestName = name;
1669
+ bestSelfClosing = true;
1369
1670
  }
1370
1671
  }
1371
1672
  }
1372
- return { index: earliestStartTagIndex, name: earliestToolName };
1673
+ return { index: bestIndex, name: bestName, selfClosing: bestSelfClosing };
1373
1674
  }
1374
1675
  function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1375
1676
  const tail = Math.max(0, maxStartTagLen - 1);
@@ -1392,10 +1693,12 @@ function processToolCallInBuffer(params) {
1392
1693
  setBuffer
1393
1694
  } = params;
1394
1695
  const endTag = `</${currentToolCall.name}>`;
1395
- const endTagIndex = buffer.indexOf(endTag);
1696
+ const normalized = normalizeCloseTags(buffer);
1697
+ const effectiveBuffer = normalized;
1698
+ const endTagIndex = effectiveBuffer.indexOf(endTag);
1396
1699
  if (endTagIndex !== -1) {
1397
- const toolContent = buffer.substring(0, endTagIndex);
1398
- const newBuffer = buffer.substring(endTagIndex + endTag.length);
1700
+ const toolContent = effectiveBuffer.substring(0, endTagIndex);
1701
+ const newBuffer = effectiveBuffer.substring(endTagIndex + endTag.length);
1399
1702
  setBuffer("");
1400
1703
  handleStreamingToolCallEnd({
1401
1704
  toolContent,
@@ -1408,14 +1711,46 @@ function processToolCallInBuffer(params) {
1408
1711
  setBuffer(newBuffer);
1409
1712
  return { buffer: newBuffer, currentToolCall: null, shouldBreak: false };
1410
1713
  }
1411
- return { buffer, currentToolCall, shouldBreak: true };
1714
+ return { buffer: effectiveBuffer, currentToolCall, shouldBreak: true };
1412
1715
  }
1413
1716
  function processNoToolCallInBuffer(params) {
1414
- const { buffer, toolNames, maxStartTagLen, controller, flushText } = params;
1415
- const { index: earliestStartTagIndex, name: earliestToolName } = findEarliestToolTag(buffer, toolNames);
1717
+ const {
1718
+ buffer,
1719
+ toolNames,
1720
+ maxStartTagLen,
1721
+ controller,
1722
+ flushText,
1723
+ tools,
1724
+ options
1725
+ } = params;
1726
+ const {
1727
+ index: earliestStartTagIndex,
1728
+ name: earliestToolName,
1729
+ selfClosing
1730
+ } = findEarliestToolTag(buffer, toolNames);
1416
1731
  if (earliestStartTagIndex !== -1) {
1417
1732
  const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
1418
1733
  flushText(controller, textBeforeTag);
1734
+ if (selfClosing) {
1735
+ const selfTag = `<${earliestToolName}/>`;
1736
+ const newBuffer2 = buffer.substring(
1737
+ earliestStartTagIndex + selfTag.length
1738
+ );
1739
+ handleStreamingToolCallEnd({
1740
+ toolContent: "",
1741
+ currentToolCall: { name: earliestToolName, content: "" },
1742
+ tools,
1743
+ options,
1744
+ ctrl: controller,
1745
+ flushText
1746
+ });
1747
+ return {
1748
+ buffer: newBuffer2,
1749
+ currentToolCall: null,
1750
+ shouldBreak: false,
1751
+ shouldContinue: false
1752
+ };
1753
+ }
1419
1754
  const startTag = `<${earliestToolName}>`;
1420
1755
  const newBuffer = buffer.substring(earliestStartTagIndex + startTag.length);
1421
1756
  return {
@@ -1444,7 +1779,7 @@ function createFlushTextHandler(getBuffer, setBuffer, getCurrentTextId, setCurre
1444
1779
  if (content) {
1445
1780
  const currentTextId2 = getCurrentTextId();
1446
1781
  if (!currentTextId2) {
1447
- const newId = (0, import_provider_utils3.generateId)();
1782
+ const newId = (0, import_provider_utils2.generateId)();
1448
1783
  setCurrentTextId(newId);
1449
1784
  controller.enqueue({ type: "text-start", id: newId });
1450
1785
  }
@@ -1496,6 +1831,8 @@ function processBufferWithoutToolCall(params, controller) {
1496
1831
  getBuffer,
1497
1832
  setBuffer,
1498
1833
  setCurrentToolCall,
1834
+ tools,
1835
+ options,
1499
1836
  toolNames,
1500
1837
  maxStartTagLen,
1501
1838
  flushText
@@ -1505,7 +1842,9 @@ function processBufferWithoutToolCall(params, controller) {
1505
1842
  toolNames,
1506
1843
  maxStartTagLen,
1507
1844
  controller,
1508
- flushText
1845
+ flushText,
1846
+ tools,
1847
+ options
1509
1848
  });
1510
1849
  setBuffer(result.buffer);
1511
1850
  setCurrentToolCall(result.currentToolCall);
@@ -1580,7 +1919,17 @@ var morphXmlProtocol = () => ({
1580
1919
  }
1581
1920
  const processedElements = [];
1582
1921
  let currentIndex = 0;
1583
- const toolCalls = findToolCalls(text, toolNames);
1922
+ const toolCallsRaw = findToolCalls(text, toolNames);
1923
+ const toolCallsNorm = collectToolCallsFromNormalizedText(text, toolNames);
1924
+ const seen = /* @__PURE__ */ new Set();
1925
+ const toolCalls = [...toolCallsRaw, ...toolCallsNorm].filter((tc) => {
1926
+ const key = `${tc.toolName}:${tc.startIndex}:${tc.endIndex}`;
1927
+ if (seen.has(key)) {
1928
+ return false;
1929
+ }
1930
+ seen.add(key);
1931
+ return true;
1932
+ }).sort((a, b) => a.startIndex - b.startIndex);
1584
1933
  for (const toolCall of toolCalls) {
1585
1934
  currentIndex = processTextBeforeToolCall(
1586
1935
  text,
@@ -1668,53 +2017,225 @@ function getToolSchema(tools, toolName) {
1668
2017
  var _a;
1669
2018
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
1670
2019
  }
1671
- function computeFullTagEnd(text, contentEnd, toolName) {
1672
- let fullTagEnd = contentEnd + `</${toolName}>`.length;
1673
- const closeHead = text.indexOf(`</${toolName}`, contentEnd);
1674
- if (closeHead === contentEnd) {
1675
- let p = closeHead + 2 + toolName.length;
2020
+ function findClosingTagEndFlexible(text, contentStart, toolName) {
2021
+ let pos = contentStart;
2022
+ let depth = 1;
2023
+ while (pos < text.length) {
2024
+ const tok = nextTagToken(text, pos);
2025
+ if (tok.kind === "eof") {
2026
+ break;
2027
+ }
2028
+ const result = updateDepthWithToken(tok, toolName, depth);
2029
+ depth = result.depth;
2030
+ if (result.closedAt !== void 0) {
2031
+ return result.closedAt;
2032
+ }
2033
+ pos = tok.nextPos;
2034
+ }
2035
+ return -1;
2036
+ }
2037
+ function skipSpecialSegment(text, lt) {
2038
+ const next = text[lt + 1];
2039
+ if (next !== "!" && next !== "?") {
2040
+ return null;
2041
+ }
2042
+ const gt = text.indexOf(">", lt + 1);
2043
+ if (gt === -1) {
2044
+ return null;
2045
+ }
2046
+ return gt + 1;
2047
+ }
2048
+ function consumeClosingTag(text, lt, toolName) {
2049
+ let p = lt + 2;
2050
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2051
+ p += 1;
2052
+ }
2053
+ if (text.slice(p, p + toolName.length) === toolName) {
2054
+ p += toolName.length;
1676
2055
  while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
1677
2056
  p += 1;
1678
2057
  }
1679
2058
  if (text[p] === ">") {
1680
- fullTagEnd = p + 1;
2059
+ const endPos2 = p + 1;
2060
+ return { matched: true, endPos: endPos2 };
1681
2061
  }
1682
2062
  }
1683
- return fullTagEnd;
2063
+ const gt = text.indexOf(">", lt + 1);
2064
+ const endPos = gt === -1 ? text.length : gt + 1;
2065
+ return { matched: false, endPos };
1684
2066
  }
1685
- function extractToolCallInfo(text, tagStart, toolName, range) {
1686
- var _a;
1687
- const startTag = `<${toolName}>`;
1688
- const contentStart = tagStart + startTag.length;
1689
- const contentEnd = contentStart + (range.end - range.start);
1690
- const fullTagEnd = computeFullTagEnd(text, contentEnd, toolName);
1691
- const segment = text.substring(tagStart, fullTagEnd);
1692
- const content = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : text.substring(contentStart, contentEnd);
2067
+ function consumeOpenTag(text, lt) {
2068
+ let p = lt + 1;
2069
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2070
+ p += 1;
2071
+ }
2072
+ const nameStart = p;
2073
+ while (p < text.length && NAME_CHAR_RE.test(text.charAt(p))) {
2074
+ p += 1;
2075
+ }
2076
+ const name = text.slice(nameStart, p);
2077
+ const q = text.indexOf(">", p);
2078
+ if (q === -1) {
2079
+ return null;
2080
+ }
2081
+ let r = q - 1;
2082
+ while (r >= nameStart && WHITESPACE_REGEX2.test(text[r])) {
2083
+ r -= 1;
2084
+ }
2085
+ const selfClosing = text[r] === "/";
2086
+ return { name, selfClosing, nextPos: q + 1 };
2087
+ }
2088
+ function updateDepthWithToken(tok, toolName, depth) {
2089
+ if (tok.kind === "close" && tok.name === toolName) {
2090
+ const newDepth = depth - 1;
2091
+ return newDepth === 0 ? { depth: newDepth, closedAt: tok.nextPos } : { depth: newDepth };
2092
+ }
2093
+ if (tok.kind === "open" && tok.name === toolName && !tok.selfClosing) {
2094
+ return { depth: depth + 1 };
2095
+ }
2096
+ return { depth };
2097
+ }
2098
+ function nextTagToken(text, fromPos) {
2099
+ const lt = text.indexOf("<", fromPos);
2100
+ if (lt === -1 || lt + 1 >= text.length) {
2101
+ return { kind: "eof", nextPos: text.length };
2102
+ }
2103
+ const next = text[lt + 1];
2104
+ const specialEnd = skipSpecialSegment(text, lt);
2105
+ if (specialEnd !== null) {
2106
+ return { kind: "special", nextPos: specialEnd };
2107
+ }
2108
+ if (next === "/") {
2109
+ const closing = consumeClosingTag(text, lt, "");
2110
+ let p = lt + 2;
2111
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2112
+ p += 1;
2113
+ }
2114
+ const nameStart = p;
2115
+ while (p < text.length && NAME_CHAR_RE.test(text.charAt(p))) {
2116
+ p += 1;
2117
+ }
2118
+ const name = text.slice(nameStart, p);
2119
+ return { kind: "close", name, nextPos: closing.endPos };
2120
+ }
2121
+ const open = consumeOpenTag(text, lt);
2122
+ if (open === null) {
2123
+ return { kind: "eof", nextPos: text.length };
2124
+ }
1693
2125
  return {
1694
- toolName,
1695
- startIndex: tagStart,
1696
- endIndex: fullTagEnd,
1697
- content,
1698
- segment
2126
+ kind: "open",
2127
+ name: open.name,
2128
+ selfClosing: open.selfClosing,
2129
+ nextPos: open.nextPos
1699
2130
  };
1700
2131
  }
2132
+ function collectToolCallsFromNormalizedText(text, toolNames) {
2133
+ var _a;
2134
+ const normalizedText = normalizeCloseTags(text);
2135
+ const collected = [];
2136
+ for (const toolName of toolNames) {
2137
+ const startTag = `<${toolName}>`;
2138
+ let idx = 0;
2139
+ let lastOrigIdx = 0;
2140
+ while (idx < normalizedText.length) {
2141
+ const tagStartNorm = normalizedText.indexOf(startTag, idx);
2142
+ if (tagStartNorm === -1) {
2143
+ break;
2144
+ }
2145
+ const contentStartNorm = tagStartNorm + startTag.length;
2146
+ const endNorm = findClosingTagEndFlexible(
2147
+ normalizedText,
2148
+ contentStartNorm,
2149
+ toolName
2150
+ );
2151
+ if (endNorm > contentStartNorm) {
2152
+ const tagStartOrig = text.indexOf(startTag, lastOrigIdx);
2153
+ const contentStartOrig = tagStartOrig + startTag.length;
2154
+ let endOrig = findClosingTagEndFlexible(
2155
+ text,
2156
+ contentStartOrig,
2157
+ toolName
2158
+ );
2159
+ if (endOrig === -1) {
2160
+ const approxLen = endNorm - tagStartNorm;
2161
+ endOrig = Math.min(text.length, tagStartOrig + approxLen);
2162
+ }
2163
+ const segment = text.substring(tagStartOrig, endOrig);
2164
+ const inner = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2165
+ collected.push({
2166
+ toolName,
2167
+ startIndex: tagStartOrig,
2168
+ endIndex: endOrig,
2169
+ content: inner,
2170
+ segment
2171
+ });
2172
+ lastOrigIdx = endOrig;
2173
+ idx = endNorm;
2174
+ } else {
2175
+ idx = contentStartNorm;
2176
+ }
2177
+ }
2178
+ }
2179
+ return collected.sort((a, b) => a.startIndex - b.startIndex);
2180
+ }
2181
+ function getNextTagInfo(text, toolName, fromIndex) {
2182
+ const startTag = `<${toolName}>`;
2183
+ const selfTag = `<${toolName}/>`;
2184
+ const openIdx = text.indexOf(startTag, fromIndex);
2185
+ const selfIdx = text.indexOf(selfTag, fromIndex);
2186
+ const hasOpen = openIdx !== -1;
2187
+ const hasSelf = selfIdx !== -1;
2188
+ if (!(hasOpen || hasSelf)) {
2189
+ return {
2190
+ found: false,
2191
+ tagStart: -1,
2192
+ selfClosing: false,
2193
+ startTag,
2194
+ selfTag
2195
+ };
2196
+ }
2197
+ const pickSelf = hasSelf && (!hasOpen || selfIdx < openIdx);
2198
+ const tagStart = pickSelf ? selfIdx : openIdx;
2199
+ return { found: true, tagStart, selfClosing: pickSelf, startTag, selfTag };
2200
+ }
1701
2201
  function findToolCallsForName(text, toolName) {
2202
+ var _a;
1702
2203
  const toolCalls = [];
1703
- const startTag = `<${toolName}>`;
1704
2204
  let searchIndex = 0;
1705
2205
  while (searchIndex < text.length) {
1706
- const tagStart = text.indexOf(startTag, searchIndex);
1707
- if (tagStart === -1) {
2206
+ const info = getNextTagInfo(text, toolName, searchIndex);
2207
+ if (!info.found) {
1708
2208
  break;
1709
2209
  }
1710
- const remainingText = text.substring(tagStart);
1711
- const range = (0, import_rxml.findFirstTopLevelRange)(remainingText, toolName);
1712
- if (range) {
1713
- const toolCallInfo = extractToolCallInfo(text, tagStart, toolName, range);
1714
- toolCalls.push(toolCallInfo);
1715
- searchIndex = toolCallInfo.endIndex;
2210
+ const { tagStart, selfClosing, startTag, selfTag } = info;
2211
+ if (selfClosing) {
2212
+ const endIndex = tagStart + selfTag.length;
2213
+ const segment = text.substring(tagStart, endIndex);
2214
+ toolCalls.push({
2215
+ toolName,
2216
+ startIndex: tagStart,
2217
+ endIndex,
2218
+ content: "",
2219
+ segment
2220
+ });
2221
+ searchIndex = endIndex;
2222
+ continue;
2223
+ }
2224
+ const contentStart = tagStart + startTag.length;
2225
+ const fullTagEnd = findClosingTagEndFlexible(text, contentStart, toolName);
2226
+ if (fullTagEnd !== -1 && fullTagEnd > contentStart) {
2227
+ const segment = text.substring(tagStart, fullTagEnd);
2228
+ const inner = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2229
+ toolCalls.push({
2230
+ toolName,
2231
+ startIndex: tagStart,
2232
+ endIndex: fullTagEnd,
2233
+ content: inner,
2234
+ segment
2235
+ });
2236
+ searchIndex = fullTagEnd;
1716
2237
  } else {
1717
- searchIndex = tagStart + startTag.length;
2238
+ searchIndex = contentStart;
1718
2239
  }
1719
2240
  }
1720
2241
  return toolCalls;
@@ -1728,14 +2249,53 @@ function findToolCalls(text, toolNames) {
1728
2249
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1729
2250
  }
1730
2251
 
1731
- // src/protocols/tool-call-protocol.ts
1732
- function isProtocolFactory(protocol) {
1733
- return typeof protocol === "function";
2252
+ // src/generate-handler.ts
2253
+ var import_provider_utils3 = require("@ai-sdk/provider-utils");
2254
+ var import_rxml2 = require("@ai-sdk-tool/rxml");
2255
+
2256
+ // src/utils/on-error.ts
2257
+ function extractOnErrorOption(providerOptions) {
2258
+ var _a;
2259
+ if (providerOptions && typeof providerOptions === "object") {
2260
+ const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
2261
+ return onError ? { onError } : void 0;
2262
+ }
2263
+ return;
2264
+ }
2265
+
2266
+ // src/utils/provider-options.ts
2267
+ var originalToolsSchema = {
2268
+ encode: encodeOriginalTools,
2269
+ decode: decodeOriginalTools
2270
+ };
2271
+ function encodeOriginalTools(tools) {
2272
+ return (tools == null ? void 0 : tools.map((t) => ({
2273
+ name: t.name,
2274
+ inputSchema: JSON.stringify(t.inputSchema)
2275
+ }))) || [];
2276
+ }
2277
+ function decodeOriginalTools(originalTools) {
2278
+ if (!originalTools) {
2279
+ return [];
2280
+ }
2281
+ return originalTools.map(
2282
+ (t) => ({
2283
+ type: "function",
2284
+ name: t.name,
2285
+ inputSchema: JSON.parse(t.inputSchema)
2286
+ })
2287
+ );
2288
+ }
2289
+ function extractToolNamesFromOriginalTools(originalTools) {
2290
+ return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
2291
+ }
2292
+ function isToolChoiceActive(params) {
2293
+ var _a, _b, _c;
2294
+ const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
2295
+ return !!(typeof params.providerOptions === "object" && params.providerOptions !== null && typeof ((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required"));
1734
2296
  }
1735
2297
 
1736
2298
  // src/generate-handler.ts
1737
- var import_provider_utils4 = require("@ai-sdk/provider-utils");
1738
- var import_rxml2 = require("@ai-sdk-tool/rxml");
1739
2299
  function parseToolChoiceJson(text, providerOptions) {
1740
2300
  var _a;
1741
2301
  try {
@@ -1779,7 +2339,7 @@ async function handleToolChoice(doGenerate, params) {
1779
2339
  }
1780
2340
  const toolCall = {
1781
2341
  type: "tool-call",
1782
- toolCallId: (0, import_provider_utils4.generateId)(),
2342
+ toolCallId: (0, import_provider_utils3.generateId)(),
1783
2343
  toolName: parsed.name || "unknown",
1784
2344
  input: JSON.stringify(parsed.arguments || {})
1785
2345
  };
@@ -1905,8 +2465,13 @@ function fixToolCallWithSchema(part, tools) {
1905
2465
  };
1906
2466
  }
1907
2467
 
2468
+ // src/protocols/tool-call-protocol.ts
2469
+ function isProtocolFactory(protocol) {
2470
+ return typeof protocol === "function";
2471
+ }
2472
+
1908
2473
  // src/stream-handler.ts
1909
- var import_provider_utils5 = require("@ai-sdk/provider-utils");
2474
+ var import_provider_utils4 = require("@ai-sdk/provider-utils");
1910
2475
  function extractToolCallSegments(protocol, fullRawText, tools) {
1911
2476
  const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({
1912
2477
  text: fullRawText,
@@ -1943,7 +2508,7 @@ function handleDebugSummary(parsedToolCalls, origin, params) {
1943
2508
  }
1944
2509
  function createDebugSummaryTransform({
1945
2510
  protocol,
1946
- fullRawText,
2511
+ getFullRawText,
1947
2512
  tools,
1948
2513
  params
1949
2514
  }) {
@@ -1956,11 +2521,9 @@ function createDebugSummaryTransform({
1956
2521
  }
1957
2522
  if (part.type === "finish") {
1958
2523
  try {
1959
- const origin = extractToolCallSegments(
1960
- protocol,
1961
- fullRawText,
1962
- tools
1963
- );
2524
+ const raw = getFullRawText();
2525
+ logRawChunk(raw);
2526
+ const origin = extractToolCallSegments(protocol, raw, tools);
1964
2527
  handleDebugSummary(parsedToolCalls, origin, params);
1965
2528
  } catch (e) {
1966
2529
  }
@@ -2058,7 +2621,7 @@ async function wrapStream({
2058
2621
  const withSummary = parsed.pipeThrough(
2059
2622
  createDebugSummaryTransform({
2060
2623
  protocol,
2061
- fullRawText,
2624
+ getFullRawText: () => fullRawText,
2062
2625
  tools,
2063
2626
  params
2064
2627
  })
@@ -2092,19 +2655,26 @@ async function toolChoiceStream({
2092
2655
  }
2093
2656
  const toolCallChunk = {
2094
2657
  type: "tool-call",
2095
- toolCallId: (0, import_provider_utils5.generateId)(),
2658
+ toolCallId: (0, import_provider_utils4.generateId)(),
2096
2659
  toolName: toolJson.name || "unknown",
2097
2660
  input: JSON.stringify(toolJson.arguments || {})
2098
2661
  };
2099
2662
  const finishChunk = {
2100
2663
  type: "finish",
2101
- usage: (result == null ? void 0 : result.usage) || // TODO: If possible, try to return a certain amount of LLM usage.
2102
- {
2103
- inputTokens: 0,
2104
- outputTokens: 0,
2105
- totalTokens: 0
2664
+ usage: (result == null ? void 0 : result.usage) || {
2665
+ inputTokens: {
2666
+ total: 0,
2667
+ noCache: void 0,
2668
+ cacheRead: void 0,
2669
+ cacheWrite: void 0
2670
+ },
2671
+ outputTokens: {
2672
+ total: 0,
2673
+ text: void 0,
2674
+ reasoning: void 0
2675
+ }
2106
2676
  },
2107
- finishReason: "tool-calls"
2677
+ finishReason: { unified: "tool-calls", raw: void 0 }
2108
2678
  };
2109
2679
  const stream = new ReadableStream({
2110
2680
  start(controller) {
@@ -2138,26 +2708,106 @@ async function toolChoiceStream({
2138
2708
  };
2139
2709
  }
2140
2710
 
2711
+ // src/utils/dynamic-tool-schema.ts
2712
+ function createDynamicIfThenElseSchema(tools) {
2713
+ let currentSchema = {};
2714
+ const toolNames = [];
2715
+ for (let i = tools.length - 1; i >= 0; i -= 1) {
2716
+ const tool = tools[i];
2717
+ if (tool.type === "provider") {
2718
+ throw new Error(
2719
+ "Provider tools are not supported by this middleware. Please use function tools."
2720
+ );
2721
+ }
2722
+ toolNames.unshift(tool.name);
2723
+ const toolCondition = {
2724
+ if: {
2725
+ properties: {
2726
+ name: {
2727
+ const: tool.name
2728
+ }
2729
+ },
2730
+ required: ["name"]
2731
+ },
2732
+ // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
2733
+ then: {
2734
+ properties: {
2735
+ name: {
2736
+ const: tool.name
2737
+ },
2738
+ arguments: tool.inputSchema
2739
+ },
2740
+ required: ["name", "arguments"]
2741
+ }
2742
+ };
2743
+ if (Object.keys(currentSchema).length > 0) {
2744
+ toolCondition.else = currentSchema;
2745
+ }
2746
+ currentSchema = toolCondition;
2747
+ }
2748
+ return {
2749
+ type: "object",
2750
+ // Explicitly specify type as "object"
2751
+ properties: {
2752
+ name: {
2753
+ type: "string",
2754
+ description: "Name of the tool to call",
2755
+ enum: toolNames
2756
+ },
2757
+ arguments: {
2758
+ type: "object",
2759
+ // By default, arguments is also specified as object type
2760
+ description: "Argument object to be passed to the tool"
2761
+ }
2762
+ },
2763
+ required: ["name", "arguments"],
2764
+ ...currentSchema
2765
+ };
2766
+ }
2767
+
2141
2768
  // src/transform-handler.ts
2142
- function buildFinalPrompt(systemPrompt, processedPrompt) {
2143
- var _a;
2144
- if (((_a = processedPrompt[0]) == null ? void 0 : _a.role) === "system") {
2769
+ function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
2770
+ const systemIndex = processedPrompt.findIndex((m) => m.role === "system");
2771
+ if (systemIndex !== -1) {
2772
+ const existing = processedPrompt[systemIndex].content;
2773
+ let existingText = "";
2774
+ if (typeof existing === "string") {
2775
+ existingText = existing;
2776
+ } else if (Array.isArray(existing)) {
2777
+ existingText = existing.map((p) => {
2778
+ var _a;
2779
+ return (p == null ? void 0 : p.type) === "text" ? (_a = p.text) != null ? _a : "" : "";
2780
+ }).filter(Boolean).join("\n");
2781
+ } else {
2782
+ existingText = String(existing != null ? existing : "");
2783
+ }
2784
+ const mergedContent = placement === "first" ? `${systemPrompt}
2785
+
2786
+ ${existingText}` : `${existingText}
2787
+
2788
+ ${systemPrompt}`;
2789
+ return processedPrompt.map(
2790
+ (m, idx) => idx === systemIndex ? {
2791
+ ...m,
2792
+ content: mergedContent
2793
+ } : m
2794
+ );
2795
+ }
2796
+ if (placement === "first") {
2145
2797
  return [
2146
2798
  {
2147
2799
  role: "system",
2148
- content: `${systemPrompt}
2149
-
2150
- ${processedPrompt[0].content}`
2800
+ content: systemPrompt
2151
2801
  },
2152
- ...processedPrompt.slice(1)
2802
+ ...processedPrompt
2153
2803
  ];
2154
2804
  }
2155
2805
  return [
2806
+ ...processedPrompt,
2156
2807
  {
2157
2808
  role: "system",
2158
2809
  content: systemPrompt
2159
- },
2160
- ...processedPrompt
2810
+ }
2161
2811
  ];
2162
2812
  }
2163
2813
  function buildBaseReturnParams(params, finalPrompt, functionTools) {
@@ -2257,7 +2907,8 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2257
2907
  function transformParams({
2258
2908
  params,
2259
2909
  protocol,
2260
- toolSystemPromptTemplate
2910
+ toolSystemPromptTemplate,
2911
+ placement = "first"
2261
2912
  }) {
2262
2913
  var _a, _b, _c, _d, _e;
2263
2914
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
@@ -2273,7 +2924,11 @@ function transformParams({
2273
2924
  resolvedProtocol,
2274
2925
  extractOnErrorOption(params.providerOptions)
2275
2926
  );
2276
- const finalPrompt = buildFinalPrompt(systemPrompt, processedPrompt);
2927
+ const finalPrompt = buildFinalPrompt(
2928
+ systemPrompt,
2929
+ processedPrompt,
2930
+ placement
2931
+ );
2277
2932
  const baseReturnParams = buildBaseReturnParams(
2278
2933
  params,
2279
2934
  finalPrompt,
@@ -2350,7 +3005,10 @@ function processMessage(message, resolvedProtocol, providerOptions) {
2350
3005
  };
2351
3006
  }
2352
3007
  if (message.role === "tool") {
2353
- return processToolMessage(message.content, resolvedProtocol);
3008
+ const toolResultParts = message.content.filter(
3009
+ (part) => part.type === "tool-result"
3010
+ );
3011
+ return processToolMessage(toolResultParts, resolvedProtocol);
2354
3012
  }
2355
3013
  return message;
2356
3014
  }
@@ -2425,7 +3083,8 @@ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
2425
3083
  // src/tool-call-middleware.ts
2426
3084
  function createToolMiddleware({
2427
3085
  protocol,
2428
- toolSystemPromptTemplate
3086
+ toolSystemPromptTemplate,
3087
+ placement = "last"
2429
3088
  }) {
2430
3089
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2431
3090
  return {
@@ -2452,6 +3111,7 @@ function createToolMiddleware({
2452
3111
  transformParams: async ({ params }) => transformParams({
2453
3112
  protocol: resolvedProtocol,
2454
3113
  toolSystemPromptTemplate,
3114
+ placement,
2455
3115
  params
2456
3116
  })
2457
3117
  };
@@ -2497,6 +3157,7 @@ For each function call return a json object with function name and arguments wit
2497
3157
  });
2498
3158
  var morphXmlToolMiddleware = createToolMiddleware({
2499
3159
  protocol: morphXmlProtocol,
3160
+ placement: "last",
2500
3161
  toolSystemPromptTemplate(tools) {
2501
3162
  return `You are a function calling AI model.
2502
3163
 
@@ -2520,7 +3181,6 @@ Available functions are listed inside <tools></tools>.
2520
3181
  });
2521
3182
  // Annotate the CommonJS export names for ESM import in node:
2522
3183
  0 && (module.exports = {
2523
- RJSON,
2524
3184
  createDynamicIfThenElseSchema,
2525
3185
  createToolMiddleware,
2526
3186
  decodeOriginalTools,
@@ -2537,6 +3197,7 @@ Available functions are listed inside <tools></tools>.
2537
3197
  isToolChoiceActive,
2538
3198
  isToolResultPart,
2539
3199
  jsonMixProtocol,
3200
+ logParseFailure,
2540
3201
  logParsedChunk,
2541
3202
  logParsedSummary,
2542
3203
  logRawChunk,