@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.
@@ -25,11 +25,8 @@ __export(community_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(community_exports);
27
27
 
28
- // src/protocols/dummy-protocol.ts
29
- var import_provider_utils = require("@ai-sdk/provider-utils");
30
-
31
28
  // src/protocols/json-mix-protocol.ts
32
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
29
+ var import_provider_utils = require("@ai-sdk/provider-utils");
33
30
 
34
31
  // src/utils/debug.ts
35
32
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -77,6 +74,7 @@ var cBgGreen = color(ANSI_BG_GREEN);
77
74
  var cInverse = color(ANSI_INVERSE);
78
75
  var cUnderline = color(ANSI_UNDERLINE);
79
76
  var cBold = color(ANSI_BOLD);
77
+ var MAX_SNIPPET_LENGTH = 800;
80
78
  function safeStringify(value) {
81
79
  try {
82
80
  return `
@@ -85,6 +83,41 @@ ${typeof value === "string" ? value : JSON.stringify(value, null, 2)}`;
85
83
  return String(value);
86
84
  }
87
85
  }
86
+ function formatError(error) {
87
+ if (error instanceof Error) {
88
+ const stack = error.stack ? `
89
+ ${error.stack}` : "";
90
+ return `
91
+ ${error.name}: ${error.message}${stack}`;
92
+ }
93
+ return safeStringify(error);
94
+ }
95
+ function truncateSnippet(snippet) {
96
+ if (snippet.length <= MAX_SNIPPET_LENGTH) {
97
+ return snippet;
98
+ }
99
+ return `${snippet.slice(0, MAX_SNIPPET_LENGTH)}
100
+ \u2026[truncated ${snippet.length - MAX_SNIPPET_LENGTH} chars]`;
101
+ }
102
+ function logParseFailure({
103
+ phase,
104
+ reason,
105
+ snippet,
106
+ error
107
+ }) {
108
+ if (getDebugLevel() !== "parse") {
109
+ return;
110
+ }
111
+ const label = cBgBlue(`[${phase}]`);
112
+ console.log(cGray("[debug:mw:fail]"), label, cYellow(reason));
113
+ if (snippet) {
114
+ const formatted = truncateSnippet(snippet);
115
+ console.log(cGray("[debug:mw:fail:snippet]"), formatted);
116
+ }
117
+ if (error) {
118
+ console.log(cGray("[debug:mw:fail:error]"), cCyan(formatError(error)));
119
+ }
120
+ }
88
121
  function logRawChunk(part) {
89
122
  console.log(cGray("[debug:mw:raw]"), cYellow(safeStringify(part)));
90
123
  }
@@ -150,63 +183,6 @@ ${rendered}`);
150
183
  }
151
184
  }
152
185
 
153
- // src/utils/dynamic-tool-schema.ts
154
- function createDynamicIfThenElseSchema(tools) {
155
- let currentSchema = {};
156
- const toolNames = [];
157
- for (let i = tools.length - 1; i >= 0; i -= 1) {
158
- const tool = tools[i];
159
- if (tool.type === "provider-defined") {
160
- throw new Error(
161
- "Provider-defined tools are not supported by this middleware. Please use custom tools."
162
- );
163
- }
164
- toolNames.unshift(tool.name);
165
- const toolCondition = {
166
- if: {
167
- properties: {
168
- name: {
169
- const: tool.name
170
- }
171
- },
172
- required: ["name"]
173
- },
174
- // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
175
- then: {
176
- properties: {
177
- name: {
178
- const: tool.name
179
- },
180
- arguments: tool.inputSchema
181
- },
182
- required: ["name", "arguments"]
183
- }
184
- };
185
- if (Object.keys(currentSchema).length > 0) {
186
- toolCondition.else = currentSchema;
187
- }
188
- currentSchema = toolCondition;
189
- }
190
- return {
191
- type: "object",
192
- // Explicitly specify type as "object"
193
- properties: {
194
- name: {
195
- type: "string",
196
- description: "Name of the tool to call",
197
- enum: toolNames
198
- },
199
- arguments: {
200
- type: "object",
201
- // By default, arguments is also specified as object type
202
- description: "Argument object to be passed to the tool"
203
- }
204
- },
205
- required: ["name", "arguments"],
206
- ...currentSchema
207
- };
208
- }
209
-
210
186
  // src/utils/get-potential-start-index.ts
211
187
  function getPotentialStartIndex(text, searchedText) {
212
188
  if (searchedText.length === 0) {
@@ -225,57 +201,12 @@ function getPotentialStartIndex(text, searchedText) {
225
201
  return null;
226
202
  }
227
203
 
228
- // src/utils/on-error.ts
229
- function extractOnErrorOption(providerOptions) {
230
- var _a;
231
- if (providerOptions && typeof providerOptions === "object") {
232
- const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
233
- return onError ? { onError } : void 0;
234
- }
235
- return;
236
- }
237
-
238
- // src/utils/provider-options.ts
239
- var originalToolsSchema = {
240
- encode: encodeOriginalTools,
241
- decode: decodeOriginalTools
242
- };
243
- function encodeOriginalTools(tools) {
244
- return (tools == null ? void 0 : tools.map((t) => ({
245
- name: t.name,
246
- inputSchema: JSON.stringify(t.inputSchema)
247
- }))) || [];
248
- }
249
- function decodeOriginalTools(originalTools) {
250
- if (!originalTools) {
251
- return [];
252
- }
253
- return originalTools.map(
254
- (t) => ({
255
- type: "function",
256
- name: t.name,
257
- inputSchema: JSON.parse(t.inputSchema)
258
- })
259
- );
260
- }
261
- function isToolChoiceActive(params) {
262
- var _a, _b, _c;
263
- const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
264
- 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"));
265
- }
266
-
267
204
  // src/utils/regex.ts
268
205
  function escapeRegExp(literal) {
269
206
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
270
207
  }
271
208
 
272
209
  // src/utils/robust-json.ts
273
- var robust_json_exports = {};
274
- __export(robust_json_exports, {
275
- parse: () => parse,
276
- stringify: () => stringify,
277
- transform: () => transform
278
- });
279
210
  var WHITESPACE_TEST_REGEX = /\s/;
280
211
  var WHITESPACE_REGEX = /^\s+/;
281
212
  var OBJECT_START_REGEX = /^\{/;
@@ -516,11 +447,6 @@ function stripTrailingComma(tokens) {
516
447
  });
517
448
  return res;
518
449
  }
519
- function transform(text) {
520
- let tokens = lexer(text);
521
- tokens = stripTrailingComma(tokens);
522
- return tokens.reduce((str, token) => str + token.match, "");
523
- }
524
450
  function popToken(tokens, state) {
525
451
  var _a, _b;
526
452
  const token = tokens[state.pos];
@@ -916,38 +842,6 @@ function parse(text, optsOrReviver) {
916
842
  }
917
843
  return parseWithTransform(text, options);
918
844
  }
919
- function stringifyPair(obj, key) {
920
- return `${JSON.stringify(key)}:${stringify(obj[key])}`;
921
- }
922
- function stringify(obj) {
923
- const type = typeof obj;
924
- if (type === "string" || type === "number" || type === "boolean" || obj === null) {
925
- return JSON.stringify(obj);
926
- }
927
- if (type === "undefined") {
928
- return "null";
929
- }
930
- if (Array.isArray(obj)) {
931
- const elements = obj.map(stringify).join(",");
932
- return `[${elements}]`;
933
- }
934
- if (type === "object") {
935
- const keys = Object.keys(obj);
936
- keys.sort();
937
- const pairs = keys.map((key) => stringifyPair(obj, key)).join(",");
938
- return `{${pairs}}`;
939
- }
940
- return "null";
941
- }
942
-
943
- // src/utils/type-guards.ts
944
- function isToolCallContent(content) {
945
- return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
946
- (typeof content.input === "string" || typeof content.input === "object");
947
- }
948
- function hasInputProperty(obj) {
949
- return typeof obj === "object" && obj !== null && "input" in obj;
950
- }
951
845
 
952
846
  // src/protocols/json-mix-protocol.ts
953
847
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
@@ -956,11 +850,17 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
956
850
  const parsedToolCall = parse(toolCallJson);
957
851
  processedElements.push({
958
852
  type: "tool-call",
959
- toolCallId: (0, import_provider_utils2.generateId)(),
853
+ toolCallId: (0, import_provider_utils.generateId)(),
960
854
  toolName: parsedToolCall.name,
961
855
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
962
856
  });
963
857
  } catch (error) {
858
+ logParseFailure({
859
+ phase: "generated-text",
860
+ reason: "Failed to parse tool call JSON segment",
861
+ snippet: fullMatch,
862
+ error
863
+ });
964
864
  if (options == null ? void 0 : options.onError) {
965
865
  options.onError(
966
866
  "Could not process JSON tool call, keeping original text.",
@@ -993,7 +893,7 @@ function flushBuffer(state, controller, toolCallStart) {
993
893
  return;
994
894
  }
995
895
  if (!state.currentTextId) {
996
- state.currentTextId = (0, import_provider_utils2.generateId)();
896
+ state.currentTextId = (0, import_provider_utils.generateId)();
997
897
  controller.enqueue({ type: "text-start", id: state.currentTextId });
998
898
  state.hasEmittedTextStart = true;
999
899
  }
@@ -1016,7 +916,12 @@ function emitIncompleteToolCall(state, controller, toolCallStart) {
1016
916
  if (!state.currentToolCallJson) {
1017
917
  return;
1018
918
  }
1019
- const errorId = (0, import_provider_utils2.generateId)();
919
+ logParseFailure({
920
+ phase: "stream",
921
+ reason: "Incomplete streaming tool call segment emitted as text",
922
+ snippet: `${toolCallStart}${state.currentToolCallJson}`
923
+ });
924
+ const errorId = (0, import_provider_utils.generateId)();
1020
925
  controller.enqueue({ type: "text-start", id: errorId });
1021
926
  controller.enqueue({
1022
927
  type: "text-delta",
@@ -1040,7 +945,7 @@ function publishText(text, state, controller) {
1040
945
  state.currentToolCallJson += text;
1041
946
  } else if (text.length > 0) {
1042
947
  if (!state.currentTextId) {
1043
- state.currentTextId = (0, import_provider_utils2.generateId)();
948
+ state.currentTextId = (0, import_provider_utils.generateId)();
1044
949
  controller.enqueue({ type: "text-start", id: state.currentTextId });
1045
950
  state.hasEmittedTextStart = true;
1046
951
  }
@@ -1055,16 +960,22 @@ function emitToolCall(context) {
1055
960
  var _a;
1056
961
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
1057
962
  try {
1058
- const parsedToolCall = robust_json_exports.parse(state.currentToolCallJson);
963
+ const parsedToolCall = parse(state.currentToolCallJson);
1059
964
  closeTextBlock(state, controller);
1060
965
  controller.enqueue({
1061
966
  type: "tool-call",
1062
- toolCallId: (0, import_provider_utils2.generateId)(),
967
+ toolCallId: (0, import_provider_utils.generateId)(),
1063
968
  toolName: parsedToolCall.name,
1064
969
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1065
970
  });
1066
- } catch (e) {
1067
- const errorId = (0, import_provider_utils2.generateId)();
971
+ } catch (error) {
972
+ logParseFailure({
973
+ phase: "stream",
974
+ reason: "Failed to parse streaming tool call JSON segment",
975
+ snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
976
+ error
977
+ });
978
+ const errorId = (0, import_provider_utils.generateId)();
1068
979
  controller.enqueue({ type: "text-start", id: errorId });
1069
980
  controller.enqueue({
1070
981
  type: "text-delta",
@@ -1229,9 +1140,336 @@ var jsonMixProtocol = ({
1229
1140
  });
1230
1141
 
1231
1142
  // src/protocols/morph-xml-protocol.ts
1232
- var import_provider_utils3 = require("@ai-sdk/provider-utils");
1143
+ var import_provider_utils2 = require("@ai-sdk/provider-utils");
1233
1144
  var import_rxml = require("@ai-sdk-tool/rxml");
1145
+
1146
+ // src/utils/type-guards.ts
1147
+ function isToolCallContent(content) {
1148
+ return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
1149
+ (typeof content.input === "string" || typeof content.input === "object");
1150
+ }
1151
+ function hasInputProperty(obj) {
1152
+ return typeof obj === "object" && obj !== null && "input" in obj;
1153
+ }
1154
+
1155
+ // src/protocols/morph-xml-protocol.ts
1234
1156
  var WHITESPACE_REGEX2 = /\s/;
1157
+ var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1158
+ var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1159
+ var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
1160
+ var STATUS_TO_STEP_BOUNDARY_RE = /<\/status>\s*<step>/g;
1161
+ var STEP_TAG_RE = /<step>([\s\S]*?)<\/step>/i;
1162
+ var STATUS_TAG_RE = /<status>([\s\S]*?)<\/status>/i;
1163
+ function normalizeCloseTags(xml) {
1164
+ return xml.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1165
+ }
1166
+ function escapeInvalidLt(xml) {
1167
+ const len = xml.length;
1168
+ let out = "";
1169
+ for (let i = 0; i < len; i += 1) {
1170
+ const ch = xml[i];
1171
+ if (ch === "<") {
1172
+ const next = i + 1 < len ? xml[i + 1] : "";
1173
+ if (!(NAME_CHAR_RE.test(next) || next === "/" || next === "!" || next === "?")) {
1174
+ out += "&lt;";
1175
+ continue;
1176
+ }
1177
+ }
1178
+ out += ch;
1179
+ }
1180
+ return out;
1181
+ }
1182
+ function shouldDeduplicateStringTags(schema) {
1183
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1184
+ if (!unwrapped || typeof unwrapped !== "object") {
1185
+ return false;
1186
+ }
1187
+ const props = unwrapped.properties;
1188
+ if (!props) {
1189
+ return false;
1190
+ }
1191
+ const commandRaw = props.command;
1192
+ if (!commandRaw) {
1193
+ return false;
1194
+ }
1195
+ const command = (0, import_rxml.unwrapJsonSchema)(commandRaw);
1196
+ return (command == null ? void 0 : command.type) === "array";
1197
+ }
1198
+ function tryParseSecondaryXml(content, toolSchema, options) {
1199
+ const normalized = normalizeCloseTags(content);
1200
+ const balanced = balanceTags(content);
1201
+ const hasMalformedClose = MALFORMED_CLOSE_RE.test(content);
1202
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1203
+ return null;
1204
+ }
1205
+ try {
1206
+ let parsed = (0, import_rxml.parse)(balanced, toolSchema, {
1207
+ onError: options == null ? void 0 : options.onError,
1208
+ noChildNodes: []
1209
+ });
1210
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1211
+ return parsed;
1212
+ } catch (_e) {
1213
+ if (shouldDeduplicateStringTags(toolSchema)) {
1214
+ const deduped = dedupeStringTagsAgainstSchema(balanced, toolSchema);
1215
+ if (deduped !== balanced) {
1216
+ try {
1217
+ let reparsed = (0, import_rxml.parse)(deduped, toolSchema, {
1218
+ onError: options == null ? void 0 : options.onError,
1219
+ noChildNodes: []
1220
+ });
1221
+ reparsed = repairParsedAgainstSchema(reparsed, toolSchema, options);
1222
+ return reparsed;
1223
+ } catch (_) {
1224
+ return null;
1225
+ }
1226
+ }
1227
+ }
1228
+ return null;
1229
+ }
1230
+ }
1231
+ function balanceTags(xml) {
1232
+ const src = normalizeCloseTags(xml).replace(
1233
+ STATUS_TO_STEP_BOUNDARY_RE,
1234
+ "</status></step><step>"
1235
+ );
1236
+ let i = 0;
1237
+ const len = src.length;
1238
+ const out = [];
1239
+ const stack = [];
1240
+ while (i < len) {
1241
+ const lt = src.indexOf("<", i);
1242
+ if (lt === -1) {
1243
+ out.push(src.slice(i));
1244
+ break;
1245
+ }
1246
+ out.push(src.slice(i, lt));
1247
+ if (lt + 1 >= len) {
1248
+ break;
1249
+ }
1250
+ const next = src[lt + 1];
1251
+ if (next === "!" || next === "?") {
1252
+ i = handleSpecialTagSegment(src, lt, out);
1253
+ continue;
1254
+ }
1255
+ if (next === "/") {
1256
+ i = handleClosingTagSegment(src, lt, out, stack);
1257
+ continue;
1258
+ }
1259
+ i = handleOpeningTagSegment(src, lt, out, stack);
1260
+ }
1261
+ for (let k = stack.length - 1; k >= 0; k -= 1) {
1262
+ out.push(`</${stack[k]}>`);
1263
+ }
1264
+ return out.join("");
1265
+ }
1266
+ function skipWs(s, p, len) {
1267
+ let idx = p;
1268
+ while (idx < len && WHITESPACE_REGEX2.test(s[idx])) {
1269
+ idx += 1;
1270
+ }
1271
+ return idx;
1272
+ }
1273
+ function parseTagNameAt(s, p, len) {
1274
+ let idx = p;
1275
+ const start = idx;
1276
+ while (idx < len && NAME_CHAR_RE.test(s[idx])) {
1277
+ idx += 1;
1278
+ }
1279
+ return { name: s.slice(start, idx), pos: idx };
1280
+ }
1281
+ function handleSpecialTagSegment(src, lt, out) {
1282
+ const gt = src.indexOf(">", lt + 1);
1283
+ if (gt === -1) {
1284
+ out.push(src.slice(lt));
1285
+ return src.length;
1286
+ }
1287
+ out.push(src.slice(lt, gt + 1));
1288
+ return gt + 1;
1289
+ }
1290
+ function handleClosingTagSegment(src, lt, out, stack) {
1291
+ const len = src.length;
1292
+ let p = skipWs(src, lt + 2, len);
1293
+ const { name, pos } = parseTagNameAt(src, p, len);
1294
+ p = pos;
1295
+ const gt = src.indexOf(">", p);
1296
+ const closingText = gt === -1 ? src.slice(lt) : src.slice(lt, gt + 1);
1297
+ const idx = stack.lastIndexOf(name);
1298
+ if (idx !== -1) {
1299
+ for (let k = stack.length - 1; k > idx; k -= 1) {
1300
+ out.push(`</${stack[k]}>`);
1301
+ stack.pop();
1302
+ }
1303
+ out.push(closingText);
1304
+ stack.pop();
1305
+ }
1306
+ return gt === -1 ? len : gt + 1;
1307
+ }
1308
+ function handleOpeningTagSegment(src, lt, out, stack) {
1309
+ const len = src.length;
1310
+ let p = skipWs(src, lt + 1, len);
1311
+ const nameStart = p;
1312
+ const parsed = parseTagNameAt(src, p, len);
1313
+ p = parsed.pos;
1314
+ const name = src.slice(nameStart, p);
1315
+ const q = src.indexOf(">", p);
1316
+ if (q === -1) {
1317
+ out.push(src.slice(lt));
1318
+ return len;
1319
+ }
1320
+ let r = q - 1;
1321
+ while (r >= nameStart && WHITESPACE_REGEX2.test(src[r])) {
1322
+ r -= 1;
1323
+ }
1324
+ const selfClosing = src[r] === "/";
1325
+ out.push(src.slice(lt, q + 1));
1326
+ if (!selfClosing && name) {
1327
+ stack.push(name);
1328
+ }
1329
+ return q + 1;
1330
+ }
1331
+ function repairParsedAgainstSchema(input, schema, options) {
1332
+ if (!input || typeof input !== "object") {
1333
+ return input;
1334
+ }
1335
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1336
+ if (!unwrapped || typeof unwrapped !== "object") {
1337
+ return input;
1338
+ }
1339
+ const properties = unwrapped.properties;
1340
+ if (!properties) {
1341
+ return input;
1342
+ }
1343
+ applySchemaProps(input, properties, options);
1344
+ return input;
1345
+ }
1346
+ function applySchemaProps(obj, properties, options) {
1347
+ for (const key of Object.keys(obj)) {
1348
+ const propSchema = properties[key];
1349
+ if (!propSchema) {
1350
+ continue;
1351
+ }
1352
+ const prop = (0, import_rxml.unwrapJsonSchema)(propSchema);
1353
+ const propType = prop.type;
1354
+ if (propType === "array" && prop.items) {
1355
+ const itemSchemaRaw = prop.items;
1356
+ const itemSchema = (0, import_rxml.unwrapJsonSchema)(itemSchemaRaw);
1357
+ obj[key] = coerceArrayItems(obj[key], itemSchema, options);
1358
+ continue;
1359
+ }
1360
+ if (propType === "object") {
1361
+ const val = obj[key];
1362
+ if (val && typeof val === "object") {
1363
+ obj[key] = repairParsedAgainstSchema(
1364
+ val,
1365
+ prop,
1366
+ options
1367
+ );
1368
+ }
1369
+ }
1370
+ }
1371
+ }
1372
+ function coerceArrayItems(val, itemSchema, options) {
1373
+ if (!Array.isArray(val)) {
1374
+ return val;
1375
+ }
1376
+ return val.map((v) => coerceArrayItem(v, itemSchema, options));
1377
+ }
1378
+ function coerceArrayItem(v, itemSchema, options) {
1379
+ const itemType = itemSchema == null ? void 0 : itemSchema.type;
1380
+ if (typeof v === "string" && itemType === "object") {
1381
+ const parsed = tryParseStringToSchemaObject(v, itemSchema, options);
1382
+ if (parsed !== null) {
1383
+ return parsed;
1384
+ }
1385
+ const fallback = extractStepStatusFromString(normalizeCloseTags(v));
1386
+ if (fallback) {
1387
+ return fallback;
1388
+ }
1389
+ return v;
1390
+ }
1391
+ if (v && typeof v === "object" && itemType === "object") {
1392
+ return repairParsedAgainstSchema(
1393
+ v,
1394
+ itemSchema,
1395
+ options
1396
+ );
1397
+ }
1398
+ return v;
1399
+ }
1400
+ function getStringPropertyNames(schema) {
1401
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1402
+ if (!unwrapped || typeof unwrapped !== "object") {
1403
+ return [];
1404
+ }
1405
+ const props = unwrapped.properties;
1406
+ if (!props) {
1407
+ return [];
1408
+ }
1409
+ const names = [];
1410
+ for (const key of Object.keys(props)) {
1411
+ const prop = (0, import_rxml.unwrapJsonSchema)(
1412
+ props[key]
1413
+ );
1414
+ const type = prop.type;
1415
+ if (type === "string") {
1416
+ names.push(key);
1417
+ }
1418
+ }
1419
+ return names;
1420
+ }
1421
+ function escapeRegExp2(s) {
1422
+ return s.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
1423
+ }
1424
+ function dedupeStringTagsAgainstSchema(xml, schema) {
1425
+ const names = getStringPropertyNames(schema);
1426
+ let out = xml;
1427
+ for (const key of names) {
1428
+ out = dedupeSingleTag(out, key);
1429
+ }
1430
+ return out;
1431
+ }
1432
+ function dedupeSingleTag(xml, key) {
1433
+ var _a, _b;
1434
+ const escaped = escapeRegExp2(key);
1435
+ const re = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
1436
+ const matches = Array.from(xml.matchAll(re));
1437
+ if (matches.length <= 1) {
1438
+ return xml;
1439
+ }
1440
+ const last = matches.at(-1);
1441
+ let result = "";
1442
+ let cursor = 0;
1443
+ for (const m of matches) {
1444
+ const idx = (_a = m.index) != null ? _a : 0;
1445
+ result += xml.slice(cursor, idx);
1446
+ if (last && idx === ((_b = last.index) != null ? _b : -1)) {
1447
+ result += m[0];
1448
+ }
1449
+ cursor = idx + m[0].length;
1450
+ }
1451
+ result += xml.slice(cursor);
1452
+ return result;
1453
+ }
1454
+ function tryParseStringToSchemaObject(xml, itemSchema, options) {
1455
+ try {
1456
+ const fixed = (0, import_rxml.parse)(normalizeCloseTags(xml), itemSchema, {
1457
+ onError: options == null ? void 0 : options.onError,
1458
+ noChildNodes: []
1459
+ });
1460
+ return typeof fixed === "string" ? null : fixed;
1461
+ } catch (e) {
1462
+ return null;
1463
+ }
1464
+ }
1465
+ function extractStepStatusFromString(normXml) {
1466
+ const stepMatch = normXml.match(STEP_TAG_RE);
1467
+ const statusMatch = normXml.match(STATUS_TAG_RE);
1468
+ if (stepMatch && statusMatch) {
1469
+ return { step: stepMatch[1], status: statusMatch[1] };
1470
+ }
1471
+ return null;
1472
+ }
1235
1473
  function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1236
1474
  if (toolCallStartIndex > currentIndex) {
1237
1475
  const textSegment = text.substring(currentIndex, toolCallStartIndex);
@@ -1244,20 +1482,36 @@ function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, proce
1244
1482
  function processToolCall(params) {
1245
1483
  var _a;
1246
1484
  const { toolCall, tools, options, text, processedElements } = params;
1485
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1247
1486
  try {
1248
- const toolSchema = getToolSchema(tools, toolCall.toolName);
1249
- const parsed = (0, import_rxml.parse)(toolCall.content, toolSchema, {
1487
+ const primary = escapeInvalidLt(normalizeCloseTags(toolCall.content));
1488
+ let parsed = (0, import_rxml.parse)(primary, toolSchema, {
1250
1489
  onError: options == null ? void 0 : options.onError,
1251
1490
  // Disable HTML self-closing tag behavior to allow base, meta, link etc. as regular tags
1252
1491
  noChildNodes: []
1253
1492
  });
1493
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1254
1494
  processedElements.push({
1255
1495
  type: "tool-call",
1256
- toolCallId: (0, import_provider_utils3.generateId)(),
1496
+ toolCallId: (0, import_provider_utils2.generateId)(),
1257
1497
  toolName: toolCall.toolName,
1258
1498
  input: JSON.stringify(parsed)
1259
1499
  });
1260
1500
  } catch (error) {
1501
+ const reparsed = tryParseSecondaryXml(
1502
+ toolCall.content,
1503
+ toolSchema,
1504
+ options
1505
+ );
1506
+ if (reparsed !== null) {
1507
+ processedElements.push({
1508
+ type: "tool-call",
1509
+ toolCallId: (0, import_provider_utils2.generateId)(),
1510
+ toolName: toolCall.toolName,
1511
+ input: JSON.stringify(reparsed)
1512
+ });
1513
+ return;
1514
+ }
1261
1515
  const originalCallText = text.substring(
1262
1516
  toolCall.startIndex,
1263
1517
  toolCall.endIndex
@@ -1281,20 +1535,33 @@ function addRemainingText(text, currentIndex, processedElements) {
1281
1535
  }
1282
1536
  function handleStreamingToolCallEnd(params) {
1283
1537
  const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1538
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1284
1539
  try {
1285
- const toolSchema = getToolSchema(tools, currentToolCall.name);
1286
- const parsed = (0, import_rxml.parse)(toolContent, toolSchema, {
1540
+ const primary = escapeInvalidLt(normalizeCloseTags(toolContent));
1541
+ let parsed = (0, import_rxml.parse)(primary, toolSchema, {
1287
1542
  onError: options == null ? void 0 : options.onError,
1288
1543
  noChildNodes: []
1289
1544
  });
1545
+ parsed = repairParsedAgainstSchema(parsed, toolSchema, options);
1290
1546
  flushText(ctrl);
1291
1547
  ctrl.enqueue({
1292
1548
  type: "tool-call",
1293
- toolCallId: (0, import_provider_utils3.generateId)(),
1549
+ toolCallId: (0, import_provider_utils2.generateId)(),
1294
1550
  toolName: currentToolCall.name,
1295
1551
  input: JSON.stringify(parsed)
1296
1552
  });
1297
1553
  } catch (error) {
1554
+ const parsed = tryParseSecondaryXml(toolContent, toolSchema, options);
1555
+ if (parsed !== null) {
1556
+ flushText(ctrl);
1557
+ ctrl.enqueue({
1558
+ type: "tool-call",
1559
+ toolCallId: (0, import_provider_utils2.generateId)(),
1560
+ toolName: currentToolCall.name,
1561
+ input: JSON.stringify(parsed)
1562
+ });
1563
+ return;
1564
+ }
1298
1565
  handleStreamingToolCallError({
1299
1566
  error,
1300
1567
  currentToolCall,
@@ -1326,19 +1593,28 @@ function handleStreamingToolCallError(params) {
1326
1593
  flushText(ctrl, originalCallText);
1327
1594
  }
1328
1595
  function findEarliestToolTag(buffer, toolNames) {
1329
- let earliestStartTagIndex = -1;
1330
- let earliestToolName = "";
1596
+ let bestIndex = -1;
1597
+ let bestName = "";
1598
+ let bestSelfClosing = false;
1331
1599
  if (toolNames.length > 0) {
1332
1600
  for (const name of toolNames) {
1333
- const startTag = `<${name}>`;
1334
- const index = buffer.indexOf(startTag);
1335
- if (index !== -1 && (earliestStartTagIndex === -1 || index < earliestStartTagIndex)) {
1336
- earliestStartTagIndex = index;
1337
- earliestToolName = name;
1601
+ const openTag = `<${name}>`;
1602
+ const selfTag = `<${name}/>`;
1603
+ const idxOpen = buffer.indexOf(openTag);
1604
+ const idxSelf = buffer.indexOf(selfTag);
1605
+ if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
1606
+ bestIndex = idxOpen;
1607
+ bestName = name;
1608
+ bestSelfClosing = false;
1609
+ }
1610
+ if (idxSelf !== -1 && (bestIndex === -1 || idxSelf < bestIndex)) {
1611
+ bestIndex = idxSelf;
1612
+ bestName = name;
1613
+ bestSelfClosing = true;
1338
1614
  }
1339
1615
  }
1340
1616
  }
1341
- return { index: earliestStartTagIndex, name: earliestToolName };
1617
+ return { index: bestIndex, name: bestName, selfClosing: bestSelfClosing };
1342
1618
  }
1343
1619
  function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1344
1620
  const tail = Math.max(0, maxStartTagLen - 1);
@@ -1361,10 +1637,12 @@ function processToolCallInBuffer(params) {
1361
1637
  setBuffer
1362
1638
  } = params;
1363
1639
  const endTag = `</${currentToolCall.name}>`;
1364
- const endTagIndex = buffer.indexOf(endTag);
1640
+ const normalized = normalizeCloseTags(buffer);
1641
+ const effectiveBuffer = normalized;
1642
+ const endTagIndex = effectiveBuffer.indexOf(endTag);
1365
1643
  if (endTagIndex !== -1) {
1366
- const toolContent = buffer.substring(0, endTagIndex);
1367
- const newBuffer = buffer.substring(endTagIndex + endTag.length);
1644
+ const toolContent = effectiveBuffer.substring(0, endTagIndex);
1645
+ const newBuffer = effectiveBuffer.substring(endTagIndex + endTag.length);
1368
1646
  setBuffer("");
1369
1647
  handleStreamingToolCallEnd({
1370
1648
  toolContent,
@@ -1377,14 +1655,46 @@ function processToolCallInBuffer(params) {
1377
1655
  setBuffer(newBuffer);
1378
1656
  return { buffer: newBuffer, currentToolCall: null, shouldBreak: false };
1379
1657
  }
1380
- return { buffer, currentToolCall, shouldBreak: true };
1658
+ return { buffer: effectiveBuffer, currentToolCall, shouldBreak: true };
1381
1659
  }
1382
1660
  function processNoToolCallInBuffer(params) {
1383
- const { buffer, toolNames, maxStartTagLen, controller, flushText } = params;
1384
- const { index: earliestStartTagIndex, name: earliestToolName } = findEarliestToolTag(buffer, toolNames);
1661
+ const {
1662
+ buffer,
1663
+ toolNames,
1664
+ maxStartTagLen,
1665
+ controller,
1666
+ flushText,
1667
+ tools,
1668
+ options
1669
+ } = params;
1670
+ const {
1671
+ index: earliestStartTagIndex,
1672
+ name: earliestToolName,
1673
+ selfClosing
1674
+ } = findEarliestToolTag(buffer, toolNames);
1385
1675
  if (earliestStartTagIndex !== -1) {
1386
1676
  const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
1387
1677
  flushText(controller, textBeforeTag);
1678
+ if (selfClosing) {
1679
+ const selfTag = `<${earliestToolName}/>`;
1680
+ const newBuffer2 = buffer.substring(
1681
+ earliestStartTagIndex + selfTag.length
1682
+ );
1683
+ handleStreamingToolCallEnd({
1684
+ toolContent: "",
1685
+ currentToolCall: { name: earliestToolName, content: "" },
1686
+ tools,
1687
+ options,
1688
+ ctrl: controller,
1689
+ flushText
1690
+ });
1691
+ return {
1692
+ buffer: newBuffer2,
1693
+ currentToolCall: null,
1694
+ shouldBreak: false,
1695
+ shouldContinue: false
1696
+ };
1697
+ }
1388
1698
  const startTag = `<${earliestToolName}>`;
1389
1699
  const newBuffer = buffer.substring(earliestStartTagIndex + startTag.length);
1390
1700
  return {
@@ -1413,7 +1723,7 @@ function createFlushTextHandler(getBuffer, setBuffer, getCurrentTextId, setCurre
1413
1723
  if (content) {
1414
1724
  const currentTextId2 = getCurrentTextId();
1415
1725
  if (!currentTextId2) {
1416
- const newId = (0, import_provider_utils3.generateId)();
1726
+ const newId = (0, import_provider_utils2.generateId)();
1417
1727
  setCurrentTextId(newId);
1418
1728
  controller.enqueue({ type: "text-start", id: newId });
1419
1729
  }
@@ -1465,6 +1775,8 @@ function processBufferWithoutToolCall(params, controller) {
1465
1775
  getBuffer,
1466
1776
  setBuffer,
1467
1777
  setCurrentToolCall,
1778
+ tools,
1779
+ options,
1468
1780
  toolNames,
1469
1781
  maxStartTagLen,
1470
1782
  flushText
@@ -1474,7 +1786,9 @@ function processBufferWithoutToolCall(params, controller) {
1474
1786
  toolNames,
1475
1787
  maxStartTagLen,
1476
1788
  controller,
1477
- flushText
1789
+ flushText,
1790
+ tools,
1791
+ options
1478
1792
  });
1479
1793
  setBuffer(result.buffer);
1480
1794
  setCurrentToolCall(result.currentToolCall);
@@ -1549,7 +1863,17 @@ var morphXmlProtocol = () => ({
1549
1863
  }
1550
1864
  const processedElements = [];
1551
1865
  let currentIndex = 0;
1552
- const toolCalls = findToolCalls(text, toolNames);
1866
+ const toolCallsRaw = findToolCalls(text, toolNames);
1867
+ const toolCallsNorm = collectToolCallsFromNormalizedText(text, toolNames);
1868
+ const seen = /* @__PURE__ */ new Set();
1869
+ const toolCalls = [...toolCallsRaw, ...toolCallsNorm].filter((tc) => {
1870
+ const key = `${tc.toolName}:${tc.startIndex}:${tc.endIndex}`;
1871
+ if (seen.has(key)) {
1872
+ return false;
1873
+ }
1874
+ seen.add(key);
1875
+ return true;
1876
+ }).sort((a, b) => a.startIndex - b.startIndex);
1553
1877
  for (const toolCall of toolCalls) {
1554
1878
  currentIndex = processTextBeforeToolCall(
1555
1879
  text,
@@ -1637,53 +1961,225 @@ function getToolSchema(tools, toolName) {
1637
1961
  var _a;
1638
1962
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
1639
1963
  }
1640
- function computeFullTagEnd(text, contentEnd, toolName) {
1641
- let fullTagEnd = contentEnd + `</${toolName}>`.length;
1642
- const closeHead = text.indexOf(`</${toolName}`, contentEnd);
1643
- if (closeHead === contentEnd) {
1644
- let p = closeHead + 2 + toolName.length;
1964
+ function findClosingTagEndFlexible(text, contentStart, toolName) {
1965
+ let pos = contentStart;
1966
+ let depth = 1;
1967
+ while (pos < text.length) {
1968
+ const tok = nextTagToken(text, pos);
1969
+ if (tok.kind === "eof") {
1970
+ break;
1971
+ }
1972
+ const result = updateDepthWithToken(tok, toolName, depth);
1973
+ depth = result.depth;
1974
+ if (result.closedAt !== void 0) {
1975
+ return result.closedAt;
1976
+ }
1977
+ pos = tok.nextPos;
1978
+ }
1979
+ return -1;
1980
+ }
1981
+ function skipSpecialSegment(text, lt) {
1982
+ const next = text[lt + 1];
1983
+ if (next !== "!" && next !== "?") {
1984
+ return null;
1985
+ }
1986
+ const gt = text.indexOf(">", lt + 1);
1987
+ if (gt === -1) {
1988
+ return null;
1989
+ }
1990
+ return gt + 1;
1991
+ }
1992
+ function consumeClosingTag(text, lt, toolName) {
1993
+ let p = lt + 2;
1994
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
1995
+ p += 1;
1996
+ }
1997
+ if (text.slice(p, p + toolName.length) === toolName) {
1998
+ p += toolName.length;
1645
1999
  while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
1646
2000
  p += 1;
1647
2001
  }
1648
2002
  if (text[p] === ">") {
1649
- fullTagEnd = p + 1;
2003
+ const endPos2 = p + 1;
2004
+ return { matched: true, endPos: endPos2 };
1650
2005
  }
1651
2006
  }
1652
- return fullTagEnd;
2007
+ const gt = text.indexOf(">", lt + 1);
2008
+ const endPos = gt === -1 ? text.length : gt + 1;
2009
+ return { matched: false, endPos };
1653
2010
  }
1654
- function extractToolCallInfo(text, tagStart, toolName, range) {
1655
- var _a;
1656
- const startTag = `<${toolName}>`;
1657
- const contentStart = tagStart + startTag.length;
1658
- const contentEnd = contentStart + (range.end - range.start);
1659
- const fullTagEnd = computeFullTagEnd(text, contentEnd, toolName);
1660
- const segment = text.substring(tagStart, fullTagEnd);
1661
- const content = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : text.substring(contentStart, contentEnd);
2011
+ function consumeOpenTag(text, lt) {
2012
+ let p = lt + 1;
2013
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2014
+ p += 1;
2015
+ }
2016
+ const nameStart = p;
2017
+ while (p < text.length && NAME_CHAR_RE.test(text.charAt(p))) {
2018
+ p += 1;
2019
+ }
2020
+ const name = text.slice(nameStart, p);
2021
+ const q = text.indexOf(">", p);
2022
+ if (q === -1) {
2023
+ return null;
2024
+ }
2025
+ let r = q - 1;
2026
+ while (r >= nameStart && WHITESPACE_REGEX2.test(text[r])) {
2027
+ r -= 1;
2028
+ }
2029
+ const selfClosing = text[r] === "/";
2030
+ return { name, selfClosing, nextPos: q + 1 };
2031
+ }
2032
+ function updateDepthWithToken(tok, toolName, depth) {
2033
+ if (tok.kind === "close" && tok.name === toolName) {
2034
+ const newDepth = depth - 1;
2035
+ return newDepth === 0 ? { depth: newDepth, closedAt: tok.nextPos } : { depth: newDepth };
2036
+ }
2037
+ if (tok.kind === "open" && tok.name === toolName && !tok.selfClosing) {
2038
+ return { depth: depth + 1 };
2039
+ }
2040
+ return { depth };
2041
+ }
2042
+ function nextTagToken(text, fromPos) {
2043
+ const lt = text.indexOf("<", fromPos);
2044
+ if (lt === -1 || lt + 1 >= text.length) {
2045
+ return { kind: "eof", nextPos: text.length };
2046
+ }
2047
+ const next = text[lt + 1];
2048
+ const specialEnd = skipSpecialSegment(text, lt);
2049
+ if (specialEnd !== null) {
2050
+ return { kind: "special", nextPos: specialEnd };
2051
+ }
2052
+ if (next === "/") {
2053
+ const closing = consumeClosingTag(text, lt, "");
2054
+ let p = lt + 2;
2055
+ while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2056
+ p += 1;
2057
+ }
2058
+ const nameStart = p;
2059
+ while (p < text.length && NAME_CHAR_RE.test(text.charAt(p))) {
2060
+ p += 1;
2061
+ }
2062
+ const name = text.slice(nameStart, p);
2063
+ return { kind: "close", name, nextPos: closing.endPos };
2064
+ }
2065
+ const open = consumeOpenTag(text, lt);
2066
+ if (open === null) {
2067
+ return { kind: "eof", nextPos: text.length };
2068
+ }
1662
2069
  return {
1663
- toolName,
1664
- startIndex: tagStart,
1665
- endIndex: fullTagEnd,
1666
- content,
1667
- segment
2070
+ kind: "open",
2071
+ name: open.name,
2072
+ selfClosing: open.selfClosing,
2073
+ nextPos: open.nextPos
1668
2074
  };
1669
2075
  }
2076
+ function collectToolCallsFromNormalizedText(text, toolNames) {
2077
+ var _a;
2078
+ const normalizedText = normalizeCloseTags(text);
2079
+ const collected = [];
2080
+ for (const toolName of toolNames) {
2081
+ const startTag = `<${toolName}>`;
2082
+ let idx = 0;
2083
+ let lastOrigIdx = 0;
2084
+ while (idx < normalizedText.length) {
2085
+ const tagStartNorm = normalizedText.indexOf(startTag, idx);
2086
+ if (tagStartNorm === -1) {
2087
+ break;
2088
+ }
2089
+ const contentStartNorm = tagStartNorm + startTag.length;
2090
+ const endNorm = findClosingTagEndFlexible(
2091
+ normalizedText,
2092
+ contentStartNorm,
2093
+ toolName
2094
+ );
2095
+ if (endNorm > contentStartNorm) {
2096
+ const tagStartOrig = text.indexOf(startTag, lastOrigIdx);
2097
+ const contentStartOrig = tagStartOrig + startTag.length;
2098
+ let endOrig = findClosingTagEndFlexible(
2099
+ text,
2100
+ contentStartOrig,
2101
+ toolName
2102
+ );
2103
+ if (endOrig === -1) {
2104
+ const approxLen = endNorm - tagStartNorm;
2105
+ endOrig = Math.min(text.length, tagStartOrig + approxLen);
2106
+ }
2107
+ const segment = text.substring(tagStartOrig, endOrig);
2108
+ const inner = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2109
+ collected.push({
2110
+ toolName,
2111
+ startIndex: tagStartOrig,
2112
+ endIndex: endOrig,
2113
+ content: inner,
2114
+ segment
2115
+ });
2116
+ lastOrigIdx = endOrig;
2117
+ idx = endNorm;
2118
+ } else {
2119
+ idx = contentStartNorm;
2120
+ }
2121
+ }
2122
+ }
2123
+ return collected.sort((a, b) => a.startIndex - b.startIndex);
2124
+ }
2125
+ function getNextTagInfo(text, toolName, fromIndex) {
2126
+ const startTag = `<${toolName}>`;
2127
+ const selfTag = `<${toolName}/>`;
2128
+ const openIdx = text.indexOf(startTag, fromIndex);
2129
+ const selfIdx = text.indexOf(selfTag, fromIndex);
2130
+ const hasOpen = openIdx !== -1;
2131
+ const hasSelf = selfIdx !== -1;
2132
+ if (!(hasOpen || hasSelf)) {
2133
+ return {
2134
+ found: false,
2135
+ tagStart: -1,
2136
+ selfClosing: false,
2137
+ startTag,
2138
+ selfTag
2139
+ };
2140
+ }
2141
+ const pickSelf = hasSelf && (!hasOpen || selfIdx < openIdx);
2142
+ const tagStart = pickSelf ? selfIdx : openIdx;
2143
+ return { found: true, tagStart, selfClosing: pickSelf, startTag, selfTag };
2144
+ }
1670
2145
  function findToolCallsForName(text, toolName) {
2146
+ var _a;
1671
2147
  const toolCalls = [];
1672
- const startTag = `<${toolName}>`;
1673
2148
  let searchIndex = 0;
1674
2149
  while (searchIndex < text.length) {
1675
- const tagStart = text.indexOf(startTag, searchIndex);
1676
- if (tagStart === -1) {
2150
+ const info = getNextTagInfo(text, toolName, searchIndex);
2151
+ if (!info.found) {
1677
2152
  break;
1678
2153
  }
1679
- const remainingText = text.substring(tagStart);
1680
- const range = (0, import_rxml.findFirstTopLevelRange)(remainingText, toolName);
1681
- if (range) {
1682
- const toolCallInfo = extractToolCallInfo(text, tagStart, toolName, range);
1683
- toolCalls.push(toolCallInfo);
1684
- searchIndex = toolCallInfo.endIndex;
2154
+ const { tagStart, selfClosing, startTag, selfTag } = info;
2155
+ if (selfClosing) {
2156
+ const endIndex = tagStart + selfTag.length;
2157
+ const segment = text.substring(tagStart, endIndex);
2158
+ toolCalls.push({
2159
+ toolName,
2160
+ startIndex: tagStart,
2161
+ endIndex,
2162
+ content: "",
2163
+ segment
2164
+ });
2165
+ searchIndex = endIndex;
2166
+ continue;
2167
+ }
2168
+ const contentStart = tagStart + startTag.length;
2169
+ const fullTagEnd = findClosingTagEndFlexible(text, contentStart, toolName);
2170
+ if (fullTagEnd !== -1 && fullTagEnd > contentStart) {
2171
+ const segment = text.substring(tagStart, fullTagEnd);
2172
+ const inner = (_a = (0, import_rxml.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2173
+ toolCalls.push({
2174
+ toolName,
2175
+ startIndex: tagStart,
2176
+ endIndex: fullTagEnd,
2177
+ content: inner,
2178
+ segment
2179
+ });
2180
+ searchIndex = fullTagEnd;
1685
2181
  } else {
1686
- searchIndex = tagStart + startTag.length;
2182
+ searchIndex = contentStart;
1687
2183
  }
1688
2184
  }
1689
2185
  return toolCalls;
@@ -1697,14 +2193,50 @@ function findToolCalls(text, toolNames) {
1697
2193
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1698
2194
  }
1699
2195
 
1700
- // src/protocols/tool-call-protocol.ts
1701
- function isProtocolFactory(protocol) {
1702
- return typeof protocol === "function";
2196
+ // src/generate-handler.ts
2197
+ var import_provider_utils3 = require("@ai-sdk/provider-utils");
2198
+ var import_rxml2 = require("@ai-sdk-tool/rxml");
2199
+
2200
+ // src/utils/on-error.ts
2201
+ function extractOnErrorOption(providerOptions) {
2202
+ var _a;
2203
+ if (providerOptions && typeof providerOptions === "object") {
2204
+ const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
2205
+ return onError ? { onError } : void 0;
2206
+ }
2207
+ return;
2208
+ }
2209
+
2210
+ // src/utils/provider-options.ts
2211
+ var originalToolsSchema = {
2212
+ encode: encodeOriginalTools,
2213
+ decode: decodeOriginalTools
2214
+ };
2215
+ function encodeOriginalTools(tools) {
2216
+ return (tools == null ? void 0 : tools.map((t) => ({
2217
+ name: t.name,
2218
+ inputSchema: JSON.stringify(t.inputSchema)
2219
+ }))) || [];
2220
+ }
2221
+ function decodeOriginalTools(originalTools) {
2222
+ if (!originalTools) {
2223
+ return [];
2224
+ }
2225
+ return originalTools.map(
2226
+ (t) => ({
2227
+ type: "function",
2228
+ name: t.name,
2229
+ inputSchema: JSON.parse(t.inputSchema)
2230
+ })
2231
+ );
2232
+ }
2233
+ function isToolChoiceActive(params) {
2234
+ var _a, _b, _c;
2235
+ const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
2236
+ 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"));
1703
2237
  }
1704
2238
 
1705
2239
  // src/generate-handler.ts
1706
- var import_provider_utils4 = require("@ai-sdk/provider-utils");
1707
- var import_rxml2 = require("@ai-sdk-tool/rxml");
1708
2240
  function parseToolChoiceJson(text, providerOptions) {
1709
2241
  var _a;
1710
2242
  try {
@@ -1748,7 +2280,7 @@ async function handleToolChoice(doGenerate, params) {
1748
2280
  }
1749
2281
  const toolCall = {
1750
2282
  type: "tool-call",
1751
- toolCallId: (0, import_provider_utils4.generateId)(),
2283
+ toolCallId: (0, import_provider_utils3.generateId)(),
1752
2284
  toolName: parsed.name || "unknown",
1753
2285
  input: JSON.stringify(parsed.arguments || {})
1754
2286
  };
@@ -1874,8 +2406,13 @@ function fixToolCallWithSchema(part, tools) {
1874
2406
  };
1875
2407
  }
1876
2408
 
2409
+ // src/protocols/tool-call-protocol.ts
2410
+ function isProtocolFactory(protocol) {
2411
+ return typeof protocol === "function";
2412
+ }
2413
+
1877
2414
  // src/stream-handler.ts
1878
- var import_provider_utils5 = require("@ai-sdk/provider-utils");
2415
+ var import_provider_utils4 = require("@ai-sdk/provider-utils");
1879
2416
  function extractToolCallSegments(protocol, fullRawText, tools) {
1880
2417
  const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({
1881
2418
  text: fullRawText,
@@ -1912,7 +2449,7 @@ function handleDebugSummary(parsedToolCalls, origin, params) {
1912
2449
  }
1913
2450
  function createDebugSummaryTransform({
1914
2451
  protocol,
1915
- fullRawText,
2452
+ getFullRawText,
1916
2453
  tools,
1917
2454
  params
1918
2455
  }) {
@@ -1925,11 +2462,9 @@ function createDebugSummaryTransform({
1925
2462
  }
1926
2463
  if (part.type === "finish") {
1927
2464
  try {
1928
- const origin = extractToolCallSegments(
1929
- protocol,
1930
- fullRawText,
1931
- tools
1932
- );
2465
+ const raw = getFullRawText();
2466
+ logRawChunk(raw);
2467
+ const origin = extractToolCallSegments(protocol, raw, tools);
1933
2468
  handleDebugSummary(parsedToolCalls, origin, params);
1934
2469
  } catch (e) {
1935
2470
  }
@@ -2027,7 +2562,7 @@ async function wrapStream({
2027
2562
  const withSummary = parsed.pipeThrough(
2028
2563
  createDebugSummaryTransform({
2029
2564
  protocol,
2030
- fullRawText,
2565
+ getFullRawText: () => fullRawText,
2031
2566
  tools,
2032
2567
  params
2033
2568
  })
@@ -2061,19 +2596,26 @@ async function toolChoiceStream({
2061
2596
  }
2062
2597
  const toolCallChunk = {
2063
2598
  type: "tool-call",
2064
- toolCallId: (0, import_provider_utils5.generateId)(),
2599
+ toolCallId: (0, import_provider_utils4.generateId)(),
2065
2600
  toolName: toolJson.name || "unknown",
2066
2601
  input: JSON.stringify(toolJson.arguments || {})
2067
2602
  };
2068
2603
  const finishChunk = {
2069
2604
  type: "finish",
2070
- usage: (result == null ? void 0 : result.usage) || // TODO: If possible, try to return a certain amount of LLM usage.
2071
- {
2072
- inputTokens: 0,
2073
- outputTokens: 0,
2074
- totalTokens: 0
2605
+ usage: (result == null ? void 0 : result.usage) || {
2606
+ inputTokens: {
2607
+ total: 0,
2608
+ noCache: void 0,
2609
+ cacheRead: void 0,
2610
+ cacheWrite: void 0
2611
+ },
2612
+ outputTokens: {
2613
+ total: 0,
2614
+ text: void 0,
2615
+ reasoning: void 0
2616
+ }
2075
2617
  },
2076
- finishReason: "tool-calls"
2618
+ finishReason: { unified: "tool-calls", raw: void 0 }
2077
2619
  };
2078
2620
  const stream = new ReadableStream({
2079
2621
  start(controller) {
@@ -2107,26 +2649,106 @@ async function toolChoiceStream({
2107
2649
  };
2108
2650
  }
2109
2651
 
2652
+ // src/utils/dynamic-tool-schema.ts
2653
+ function createDynamicIfThenElseSchema(tools) {
2654
+ let currentSchema = {};
2655
+ const toolNames = [];
2656
+ for (let i = tools.length - 1; i >= 0; i -= 1) {
2657
+ const tool = tools[i];
2658
+ if (tool.type === "provider") {
2659
+ throw new Error(
2660
+ "Provider tools are not supported by this middleware. Please use function tools."
2661
+ );
2662
+ }
2663
+ toolNames.unshift(tool.name);
2664
+ const toolCondition = {
2665
+ if: {
2666
+ properties: {
2667
+ name: {
2668
+ const: tool.name
2669
+ }
2670
+ },
2671
+ required: ["name"]
2672
+ },
2673
+ // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
2674
+ then: {
2675
+ properties: {
2676
+ name: {
2677
+ const: tool.name
2678
+ },
2679
+ arguments: tool.inputSchema
2680
+ },
2681
+ required: ["name", "arguments"]
2682
+ }
2683
+ };
2684
+ if (Object.keys(currentSchema).length > 0) {
2685
+ toolCondition.else = currentSchema;
2686
+ }
2687
+ currentSchema = toolCondition;
2688
+ }
2689
+ return {
2690
+ type: "object",
2691
+ // Explicitly specify type as "object"
2692
+ properties: {
2693
+ name: {
2694
+ type: "string",
2695
+ description: "Name of the tool to call",
2696
+ enum: toolNames
2697
+ },
2698
+ arguments: {
2699
+ type: "object",
2700
+ // By default, arguments is also specified as object type
2701
+ description: "Argument object to be passed to the tool"
2702
+ }
2703
+ },
2704
+ required: ["name", "arguments"],
2705
+ ...currentSchema
2706
+ };
2707
+ }
2708
+
2110
2709
  // src/transform-handler.ts
2111
- function buildFinalPrompt(systemPrompt, processedPrompt) {
2112
- var _a;
2113
- if (((_a = processedPrompt[0]) == null ? void 0 : _a.role) === "system") {
2710
+ function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
2711
+ const systemIndex = processedPrompt.findIndex((m) => m.role === "system");
2712
+ if (systemIndex !== -1) {
2713
+ const existing = processedPrompt[systemIndex].content;
2714
+ let existingText = "";
2715
+ if (typeof existing === "string") {
2716
+ existingText = existing;
2717
+ } else if (Array.isArray(existing)) {
2718
+ existingText = existing.map((p) => {
2719
+ var _a;
2720
+ return (p == null ? void 0 : p.type) === "text" ? (_a = p.text) != null ? _a : "" : "";
2721
+ }).filter(Boolean).join("\n");
2722
+ } else {
2723
+ existingText = String(existing != null ? existing : "");
2724
+ }
2725
+ const mergedContent = placement === "first" ? `${systemPrompt}
2726
+
2727
+ ${existingText}` : `${existingText}
2728
+
2729
+ ${systemPrompt}`;
2730
+ return processedPrompt.map(
2731
+ (m, idx) => idx === systemIndex ? {
2732
+ ...m,
2733
+ content: mergedContent
2734
+ } : m
2735
+ );
2736
+ }
2737
+ if (placement === "first") {
2114
2738
  return [
2115
2739
  {
2116
2740
  role: "system",
2117
- content: `${systemPrompt}
2118
-
2119
- ${processedPrompt[0].content}`
2741
+ content: systemPrompt
2120
2742
  },
2121
- ...processedPrompt.slice(1)
2743
+ ...processedPrompt
2122
2744
  ];
2123
2745
  }
2124
2746
  return [
2747
+ ...processedPrompt,
2125
2748
  {
2126
2749
  role: "system",
2127
2750
  content: systemPrompt
2128
- },
2129
- ...processedPrompt
2751
+ }
2130
2752
  ];
2131
2753
  }
2132
2754
  function buildBaseReturnParams(params, finalPrompt, functionTools) {
@@ -2226,7 +2848,8 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2226
2848
  function transformParams({
2227
2849
  params,
2228
2850
  protocol,
2229
- toolSystemPromptTemplate
2851
+ toolSystemPromptTemplate,
2852
+ placement = "first"
2230
2853
  }) {
2231
2854
  var _a, _b, _c, _d, _e;
2232
2855
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
@@ -2242,7 +2865,11 @@ function transformParams({
2242
2865
  resolvedProtocol,
2243
2866
  extractOnErrorOption(params.providerOptions)
2244
2867
  );
2245
- const finalPrompt = buildFinalPrompt(systemPrompt, processedPrompt);
2868
+ const finalPrompt = buildFinalPrompt(
2869
+ systemPrompt,
2870
+ processedPrompt,
2871
+ placement
2872
+ );
2246
2873
  const baseReturnParams = buildBaseReturnParams(
2247
2874
  params,
2248
2875
  finalPrompt,
@@ -2319,7 +2946,10 @@ function processMessage(message, resolvedProtocol, providerOptions) {
2319
2946
  };
2320
2947
  }
2321
2948
  if (message.role === "tool") {
2322
- return processToolMessage(message.content, resolvedProtocol);
2949
+ const toolResultParts = message.content.filter(
2950
+ (part) => part.type === "tool-result"
2951
+ );
2952
+ return processToolMessage(toolResultParts, resolvedProtocol);
2323
2953
  }
2324
2954
  return message;
2325
2955
  }
@@ -2394,7 +3024,8 @@ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
2394
3024
  // src/tool-call-middleware.ts
2395
3025
  function createToolMiddleware({
2396
3026
  protocol,
2397
- toolSystemPromptTemplate
3027
+ toolSystemPromptTemplate,
3028
+ placement = "last"
2398
3029
  }) {
2399
3030
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2400
3031
  return {
@@ -2421,6 +3052,7 @@ function createToolMiddleware({
2421
3052
  transformParams: async ({ params }) => transformParams({
2422
3053
  protocol: resolvedProtocol,
2423
3054
  toolSystemPromptTemplate,
3055
+ placement,
2424
3056
  params
2425
3057
  })
2426
3058
  };
@@ -2466,6 +3098,7 @@ For each function call return a json object with function name and arguments wit
2466
3098
  });
2467
3099
  var morphXmlToolMiddleware = createToolMiddleware({
2468
3100
  protocol: morphXmlProtocol,
3101
+ placement: "last",
2469
3102
  toolSystemPromptTemplate(tools) {
2470
3103
  return `You are a function calling AI model.
2471
3104
 
@@ -2488,7 +3121,7 @@ Available functions are listed inside <tools></tools>.
2488
3121
  }
2489
3122
  });
2490
3123
 
2491
- // src/community/index.ts
3124
+ // src/community/sijawara.ts
2492
3125
  var sijawaraDetailedXmlToolMiddleware = createToolMiddleware({
2493
3126
  protocol: morphXmlProtocol,
2494
3127
  toolSystemPromptTemplate(tools) {