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

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,162 +1140,896 @@ var jsonMixProtocol = ({
1229
1140
  });
1230
1141
 
1231
1142
  // src/protocols/morph-xml-protocol.ts
1232
- var import_provider_utils3 = require("@ai-sdk/provider-utils");
1233
- var import_rxml = require("@ai-sdk-tool/rxml");
1234
- var WHITESPACE_REGEX2 = /\s/;
1235
- function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1236
- if (toolCallStartIndex > currentIndex) {
1237
- const textSegment = text.substring(currentIndex, toolCallStartIndex);
1238
- if (textSegment.trim()) {
1239
- processedElements.push({ type: "text", text: textSegment });
1240
- }
1143
+ var import_provider_utils2 = require("@ai-sdk/provider-utils");
1144
+ var import_rxml2 = require("@ai-sdk-tool/rxml");
1145
+
1146
+ // src/heuristics/engine.ts
1147
+ function applyRawSegmentUpdate(current, result) {
1148
+ if (result.rawSegment !== void 0) {
1149
+ return { ...current, rawSegment: result.rawSegment };
1241
1150
  }
1242
- return currentIndex;
1151
+ return current;
1243
1152
  }
1244
- function processToolCall(params) {
1245
- var _a;
1246
- const { toolCall, tools, options, text, processedElements } = params;
1247
- try {
1248
- const toolSchema = getToolSchema(tools, toolCall.toolName);
1249
- const parsed = (0, import_rxml.parse)(toolCall.content, toolSchema, {
1250
- onError: options == null ? void 0 : options.onError,
1251
- // Disable HTML self-closing tag behavior to allow base, meta, link etc. as regular tags
1252
- noChildNodes: []
1253
- });
1254
- processedElements.push({
1255
- type: "tool-call",
1256
- toolCallId: (0, import_provider_utils3.generateId)(),
1257
- toolName: toolCall.toolName,
1258
- input: JSON.stringify(parsed)
1259
- });
1260
- } catch (error) {
1261
- const originalCallText = text.substring(
1262
- toolCall.startIndex,
1263
- toolCall.endIndex
1264
- );
1265
- const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1266
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1267
- toolCall: originalCallText,
1268
- toolName: toolCall.toolName,
1269
- error
1270
- });
1271
- processedElements.push({ type: "text", text: originalCallText });
1153
+ function applyParsedUpdate(current, result) {
1154
+ if (result.parsed !== void 0) {
1155
+ return { ...current, parsed: result.parsed };
1272
1156
  }
1157
+ return current;
1273
1158
  }
1274
- function addRemainingText(text, currentIndex, processedElements) {
1275
- if (currentIndex < text.length) {
1276
- const remainingText = text.substring(currentIndex);
1277
- if (remainingText.trim()) {
1278
- processedElements.push({ type: "text", text: remainingText });
1279
- }
1159
+ function applyWarningsUpdate(current, result) {
1160
+ var _a, _b;
1161
+ if (result.warnings && result.warnings.length > 0) {
1162
+ const meta = (_a = current.meta) != null ? _a : {};
1163
+ const existingWarnings = (_b = meta.warnings) != null ? _b : [];
1164
+ return {
1165
+ ...current,
1166
+ meta: { ...meta, warnings: [...existingWarnings, ...result.warnings] }
1167
+ };
1280
1168
  }
1169
+ return current;
1281
1170
  }
1282
- function handleStreamingToolCallEnd(params) {
1283
- const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1171
+ function attemptReparse(current, result, reparseCount, maxReparses, parse4) {
1172
+ if (!result.reparse || result.rawSegment === void 0 || reparseCount >= maxReparses) {
1173
+ return { state: current, newCount: reparseCount };
1174
+ }
1284
1175
  try {
1285
- const toolSchema = getToolSchema(tools, currentToolCall.name);
1286
- const parsed = (0, import_rxml.parse)(toolContent, toolSchema, {
1287
- onError: options == null ? void 0 : options.onError,
1288
- noChildNodes: []
1289
- });
1290
- flushText(ctrl);
1291
- ctrl.enqueue({
1292
- type: "tool-call",
1293
- toolCallId: (0, import_provider_utils3.generateId)(),
1294
- toolName: currentToolCall.name,
1295
- input: JSON.stringify(parsed)
1296
- });
1176
+ const reparsed = parse4(result.rawSegment, current.schema);
1177
+ return {
1178
+ state: { ...current, parsed: reparsed, errors: [] },
1179
+ newCount: reparseCount + 1
1180
+ };
1297
1181
  } catch (error) {
1298
- handleStreamingToolCallError({
1299
- error,
1300
- currentToolCall,
1301
- toolContent,
1302
- options,
1303
- ctrl,
1304
- flushText
1305
- });
1182
+ return {
1183
+ state: { ...current, errors: [...current.errors, error] },
1184
+ newCount: reparseCount + 1
1185
+ };
1306
1186
  }
1307
1187
  }
1308
- function handleStreamingToolCallError(params) {
1188
+ function executePhase(ctx, heuristics, options) {
1309
1189
  var _a;
1310
- const { error, currentToolCall, toolContent, options, ctrl, flushText } = params;
1311
- const endTag = `</${currentToolCall.name}>`;
1312
- const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1313
- let message = "Could not process streaming XML tool call; emitting original text.";
1314
- if (error instanceof import_rxml.RXMLDuplicateStringTagError) {
1315
- message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1316
- } else if (error instanceof import_rxml.RXMLCoercionError) {
1317
- message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1318
- } else if (error instanceof import_rxml.RXMLParseError) {
1319
- message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1190
+ let current = ctx;
1191
+ let reparseCount = 0;
1192
+ const maxReparses = (_a = options.maxReparses) != null ? _a : 2;
1193
+ for (const heuristic of heuristics) {
1194
+ if (!heuristic.applies(current)) {
1195
+ continue;
1196
+ }
1197
+ const result = heuristic.run(current);
1198
+ current = applyRawSegmentUpdate(current, result);
1199
+ current = applyParsedUpdate(current, result);
1200
+ current = applyWarningsUpdate(current, result);
1201
+ const reparseResult = attemptReparse(
1202
+ current,
1203
+ result,
1204
+ reparseCount,
1205
+ maxReparses,
1206
+ options.parse
1207
+ );
1208
+ current = reparseResult.state;
1209
+ reparseCount = reparseResult.newCount;
1210
+ if (result.stop) {
1211
+ break;
1212
+ }
1320
1213
  }
1321
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1322
- toolCall: originalCallText,
1323
- toolName: currentToolCall.name,
1324
- error
1325
- });
1326
- flushText(ctrl, originalCallText);
1214
+ return current;
1327
1215
  }
1328
- function findEarliestToolTag(buffer, toolNames) {
1329
- let earliestStartTagIndex = -1;
1330
- let earliestToolName = "";
1331
- if (toolNames.length > 0) {
1332
- 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;
1338
- }
1216
+ function applyHeuristicPipeline(ctx, config, options) {
1217
+ let current = ctx;
1218
+ if (config.preParse && config.preParse.length > 0) {
1219
+ current = executePhase(current, config.preParse, options);
1220
+ }
1221
+ if (current.parsed === null && current.errors.length === 0) {
1222
+ try {
1223
+ const parsed = options.parse(current.rawSegment, current.schema);
1224
+ current = { ...current, parsed, errors: [] };
1225
+ } catch (error) {
1226
+ current = { ...current, errors: [error] };
1339
1227
  }
1340
1228
  }
1341
- return { index: earliestStartTagIndex, name: earliestToolName };
1229
+ if (current.errors.length > 0 && config.fallbackReparse && config.fallbackReparse.length > 0) {
1230
+ current = executePhase(current, config.fallbackReparse, options);
1231
+ }
1232
+ if (current.parsed !== null && config.postParse && config.postParse.length > 0) {
1233
+ current = executePhase(current, config.postParse, options);
1234
+ }
1235
+ return current;
1342
1236
  }
1343
- function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1344
- const tail = Math.max(0, maxStartTagLen - 1);
1345
- const safeLen = Math.max(0, buffer.length - tail);
1346
- if (safeLen > 0) {
1347
- const textToFlush = buffer.slice(0, safeLen);
1348
- flushText(controller, textToFlush);
1349
- return { buffer: buffer.slice(safeLen), shouldContinue: true };
1237
+ function createIntermediateCall(toolName, rawSegment, schema) {
1238
+ return {
1239
+ toolName,
1240
+ schema,
1241
+ rawSegment,
1242
+ parsed: null,
1243
+ errors: [],
1244
+ meta: { originalContent: rawSegment }
1245
+ };
1246
+ }
1247
+ function mergePipelineConfigs(...configs) {
1248
+ var _a, _b, _c;
1249
+ const result = {
1250
+ preParse: [],
1251
+ fallbackReparse: [],
1252
+ postParse: []
1253
+ };
1254
+ for (const config of configs) {
1255
+ if (config.preParse) {
1256
+ result.preParse = [...(_a = result.preParse) != null ? _a : [], ...config.preParse];
1257
+ }
1258
+ if (config.fallbackReparse) {
1259
+ result.fallbackReparse = [
1260
+ ...(_b = result.fallbackReparse) != null ? _b : [],
1261
+ ...config.fallbackReparse
1262
+ ];
1263
+ }
1264
+ if (config.postParse) {
1265
+ result.postParse = [...(_c = result.postParse) != null ? _c : [], ...config.postParse];
1266
+ }
1350
1267
  }
1351
- return { buffer, shouldContinue: false };
1268
+ return result;
1352
1269
  }
1353
- function processToolCallInBuffer(params) {
1354
- const {
1355
- buffer,
1270
+
1271
+ // src/heuristics/xml-defaults.ts
1272
+ var import_rxml = require("@ai-sdk-tool/rxml");
1273
+ var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1274
+ var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1275
+ var STATUS_TO_STEP_BOUNDARY_RE = /<\/status>\s*<step>/g;
1276
+ var WHITESPACE_REGEX2 = /\s/;
1277
+ var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
1278
+ var NAME_START_CHAR_RE = /[A-Za-z_:]/;
1279
+ var STEP_TAG_RE = /<step>([\s\S]*?)<\/step>/i;
1280
+ var STATUS_TAG_RE = /<status>([\s\S]*?)<\/status>/i;
1281
+ var normalizeCloseTagsHeuristic = {
1282
+ id: "normalize-close-tags",
1283
+ phase: "pre-parse",
1284
+ applies: () => true,
1285
+ run: (ctx) => {
1286
+ const normalized = ctx.rawSegment.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1287
+ if (normalized !== ctx.rawSegment) {
1288
+ return { rawSegment: normalized };
1289
+ }
1290
+ return {};
1291
+ }
1292
+ };
1293
+ var escapeInvalidLtHeuristic = {
1294
+ id: "escape-invalid-lt",
1295
+ phase: "pre-parse",
1296
+ applies: () => true,
1297
+ run: (ctx) => {
1298
+ const escaped = escapeInvalidLt(ctx.rawSegment);
1299
+ if (escaped !== ctx.rawSegment) {
1300
+ return { rawSegment: escaped };
1301
+ }
1302
+ return {};
1303
+ }
1304
+ };
1305
+ var balanceTagsHeuristic = {
1306
+ id: "balance-tags",
1307
+ phase: "fallback-reparse",
1308
+ applies: (ctx) => {
1309
+ var _a;
1310
+ const original = ((_a = ctx.meta) == null ? void 0 : _a.originalContent) || ctx.rawSegment;
1311
+ const normalized = original.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1312
+ const balanced = balanceTags(original);
1313
+ const hasMalformedClose = MALFORMED_CLOSE_RE.test(original);
1314
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1315
+ return false;
1316
+ }
1317
+ return balanced !== normalized;
1318
+ },
1319
+ run: (ctx) => {
1320
+ var _a;
1321
+ const original = ((_a = ctx.meta) == null ? void 0 : _a.originalContent) || ctx.rawSegment;
1322
+ const balanced = balanceTags(original);
1323
+ const escaped = escapeInvalidLt(balanced);
1324
+ return { rawSegment: escaped, reparse: true };
1325
+ }
1326
+ };
1327
+ var dedupeShellStringTagsHeuristic = {
1328
+ id: "dedupe-shell-string-tags",
1329
+ phase: "fallback-reparse",
1330
+ applies: (ctx) => shouldDeduplicateStringTags(ctx.schema),
1331
+ run: (ctx) => {
1332
+ const names = getStringPropertyNames(ctx.schema);
1333
+ let deduped = ctx.rawSegment;
1334
+ for (const key of names) {
1335
+ deduped = dedupeSingleTag(deduped, key);
1336
+ }
1337
+ if (deduped !== ctx.rawSegment) {
1338
+ return { rawSegment: deduped, reparse: true };
1339
+ }
1340
+ return {};
1341
+ }
1342
+ };
1343
+ var repairAgainstSchemaHeuristic = {
1344
+ id: "repair-against-schema",
1345
+ phase: "post-parse",
1346
+ applies: (ctx) => ctx.parsed !== null && typeof ctx.parsed === "object",
1347
+ run: (ctx) => {
1348
+ const repaired = repairParsedAgainstSchema(ctx.parsed, ctx.schema);
1349
+ if (repaired !== ctx.parsed) {
1350
+ return { parsed: repaired };
1351
+ }
1352
+ return {};
1353
+ }
1354
+ };
1355
+ var defaultPipelineConfig = {
1356
+ preParse: [normalizeCloseTagsHeuristic, escapeInvalidLtHeuristic],
1357
+ fallbackReparse: [balanceTagsHeuristic, dedupeShellStringTagsHeuristic],
1358
+ postParse: [repairAgainstSchemaHeuristic]
1359
+ };
1360
+ var INDEX_TAG_RE = /^<(\d+)(?:>|\/?>)/;
1361
+ function isIndexTagAt(xml, pos) {
1362
+ const remaining = xml.slice(pos);
1363
+ return INDEX_TAG_RE.test(remaining);
1364
+ }
1365
+ function escapeInvalidLt(xml) {
1366
+ const len = xml.length;
1367
+ let out = "";
1368
+ for (let i = 0; i < len; i += 1) {
1369
+ const ch = xml[i];
1370
+ if (ch === "<") {
1371
+ const next = i + 1 < len ? xml[i + 1] : "";
1372
+ const isValidStart = NAME_START_CHAR_RE.test(next) || next === "/" || next === "!" || next === "?";
1373
+ const isIndexTag = !isValidStart && isIndexTagAt(xml, i);
1374
+ if (!(isValidStart || isIndexTag)) {
1375
+ out += "&lt;";
1376
+ continue;
1377
+ }
1378
+ }
1379
+ out += ch;
1380
+ }
1381
+ return out;
1382
+ }
1383
+ function balanceTags(xml) {
1384
+ const src = xml.replace(MALFORMED_CLOSE_RE_G, "</$1>").replace(STATUS_TO_STEP_BOUNDARY_RE, "</status></step><step>");
1385
+ let i = 0;
1386
+ const len = src.length;
1387
+ const out = [];
1388
+ const stack = [];
1389
+ while (i < len) {
1390
+ const lt = src.indexOf("<", i);
1391
+ if (lt === -1) {
1392
+ out.push(src.slice(i));
1393
+ break;
1394
+ }
1395
+ out.push(src.slice(i, lt));
1396
+ if (lt + 1 >= len) {
1397
+ break;
1398
+ }
1399
+ const next = src[lt + 1];
1400
+ if (next === "!" || next === "?") {
1401
+ i = handleSpecialTagSegment(src, lt, out);
1402
+ continue;
1403
+ }
1404
+ if (next === "/") {
1405
+ i = handleClosingTagSegment(src, lt, out, stack);
1406
+ continue;
1407
+ }
1408
+ i = handleOpeningTagSegment(src, lt, out, stack);
1409
+ }
1410
+ for (let k = stack.length - 1; k >= 0; k -= 1) {
1411
+ out.push(`</${stack[k]}>`);
1412
+ }
1413
+ return out.join("");
1414
+ }
1415
+ function skipWs(s, p, len) {
1416
+ let idx = p;
1417
+ while (idx < len && WHITESPACE_REGEX2.test(s[idx])) {
1418
+ idx += 1;
1419
+ }
1420
+ return idx;
1421
+ }
1422
+ function parseTagNameAt(s, p, len) {
1423
+ let idx = p;
1424
+ const start = idx;
1425
+ while (idx < len && NAME_CHAR_RE.test(s[idx])) {
1426
+ idx += 1;
1427
+ }
1428
+ return { name: s.slice(start, idx), pos: idx };
1429
+ }
1430
+ function handleSpecialTagSegment(src, lt, out) {
1431
+ const gt = src.indexOf(">", lt + 1);
1432
+ if (gt === -1) {
1433
+ out.push(src.slice(lt));
1434
+ return src.length;
1435
+ }
1436
+ out.push(src.slice(lt, gt + 1));
1437
+ return gt + 1;
1438
+ }
1439
+ function handleClosingTagSegment(src, lt, out, stack) {
1440
+ const len = src.length;
1441
+ let p = skipWs(src, lt + 2, len);
1442
+ const { name, pos } = parseTagNameAt(src, p, len);
1443
+ p = pos;
1444
+ const gt = src.indexOf(">", p);
1445
+ const closingText = gt === -1 ? src.slice(lt) : src.slice(lt, gt + 1);
1446
+ const idx = stack.lastIndexOf(name);
1447
+ if (idx !== -1) {
1448
+ for (let k = stack.length - 1; k > idx; k -= 1) {
1449
+ out.push(`</${stack[k]}>`);
1450
+ stack.pop();
1451
+ }
1452
+ out.push(closingText);
1453
+ stack.pop();
1454
+ }
1455
+ return gt === -1 ? len : gt + 1;
1456
+ }
1457
+ function handleOpeningTagSegment(src, lt, out, stack) {
1458
+ const len = src.length;
1459
+ let p = skipWs(src, lt + 1, len);
1460
+ const nameStart = p;
1461
+ const parsed = parseTagNameAt(src, p, len);
1462
+ p = parsed.pos;
1463
+ const name = src.slice(nameStart, p);
1464
+ const q = src.indexOf(">", p);
1465
+ if (q === -1) {
1466
+ out.push(src.slice(lt));
1467
+ return len;
1468
+ }
1469
+ let r = q - 1;
1470
+ while (r >= nameStart && WHITESPACE_REGEX2.test(src[r])) {
1471
+ r -= 1;
1472
+ }
1473
+ const selfClosing = src[r] === "/";
1474
+ out.push(src.slice(lt, q + 1));
1475
+ if (!selfClosing && name) {
1476
+ stack.push(name);
1477
+ }
1478
+ return q + 1;
1479
+ }
1480
+ function shouldDeduplicateStringTags(schema) {
1481
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1482
+ if (!unwrapped || typeof unwrapped !== "object") {
1483
+ return false;
1484
+ }
1485
+ const props = unwrapped.properties;
1486
+ if (!props) {
1487
+ return false;
1488
+ }
1489
+ const commandRaw = props.command;
1490
+ if (!commandRaw) {
1491
+ return false;
1492
+ }
1493
+ const command = (0, import_rxml.unwrapJsonSchema)(commandRaw);
1494
+ return (command == null ? void 0 : command.type) === "array";
1495
+ }
1496
+ function getStringPropertyNames(schema) {
1497
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1498
+ if (!unwrapped || typeof unwrapped !== "object") {
1499
+ return [];
1500
+ }
1501
+ const props = unwrapped.properties;
1502
+ if (!props) {
1503
+ return [];
1504
+ }
1505
+ const names = [];
1506
+ for (const key of Object.keys(props)) {
1507
+ const prop = (0, import_rxml.unwrapJsonSchema)(
1508
+ props[key]
1509
+ );
1510
+ const type = prop.type;
1511
+ if (type === "string") {
1512
+ names.push(key);
1513
+ }
1514
+ }
1515
+ return names;
1516
+ }
1517
+ function escapeRegExp2(s) {
1518
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1519
+ }
1520
+ function dedupeSingleTag(xml, key) {
1521
+ var _a, _b;
1522
+ const escaped = escapeRegExp2(key);
1523
+ const re = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
1524
+ const matches = Array.from(xml.matchAll(re));
1525
+ if (matches.length <= 1) {
1526
+ return xml;
1527
+ }
1528
+ const last = matches.at(-1);
1529
+ let result = "";
1530
+ let cursor = 0;
1531
+ for (const m of matches) {
1532
+ const idx = (_a = m.index) != null ? _a : 0;
1533
+ result += xml.slice(cursor, idx);
1534
+ if (last && idx === ((_b = last.index) != null ? _b : -1)) {
1535
+ result += m[0];
1536
+ }
1537
+ cursor = idx + m[0].length;
1538
+ }
1539
+ result += xml.slice(cursor);
1540
+ return result;
1541
+ }
1542
+ function repairParsedAgainstSchema(input, schema) {
1543
+ if (!input || typeof input !== "object") {
1544
+ return input;
1545
+ }
1546
+ const unwrapped = (0, import_rxml.unwrapJsonSchema)(schema);
1547
+ if (!unwrapped || typeof unwrapped !== "object") {
1548
+ return input;
1549
+ }
1550
+ const properties = unwrapped.properties;
1551
+ if (!properties) {
1552
+ return input;
1553
+ }
1554
+ applySchemaProps(input, properties);
1555
+ return input;
1556
+ }
1557
+ function applySchemaProps(obj, properties) {
1558
+ for (const key of Object.keys(obj)) {
1559
+ const propSchema = properties[key];
1560
+ if (!propSchema) {
1561
+ continue;
1562
+ }
1563
+ const prop = (0, import_rxml.unwrapJsonSchema)(propSchema);
1564
+ const propType = prop.type;
1565
+ if (propType === "array" && prop.items) {
1566
+ const itemSchemaRaw = prop.items;
1567
+ const itemSchema = (0, import_rxml.unwrapJsonSchema)(itemSchemaRaw);
1568
+ obj[key] = coerceArrayItems(obj[key], itemSchema);
1569
+ continue;
1570
+ }
1571
+ if (propType === "object") {
1572
+ const val = obj[key];
1573
+ if (val && typeof val === "object") {
1574
+ obj[key] = repairParsedAgainstSchema(val, prop);
1575
+ }
1576
+ }
1577
+ }
1578
+ }
1579
+ function coerceArrayItems(val, itemSchema) {
1580
+ if (!Array.isArray(val)) {
1581
+ return val;
1582
+ }
1583
+ return val.map((v) => coerceArrayItem(v, itemSchema));
1584
+ }
1585
+ function coerceArrayItem(v, itemSchema) {
1586
+ const itemType = itemSchema == null ? void 0 : itemSchema.type;
1587
+ if (typeof v === "string" && itemType === "object") {
1588
+ const parsed = tryParseStringToSchemaObject(v, itemSchema);
1589
+ if (parsed !== null) {
1590
+ return parsed;
1591
+ }
1592
+ const fallback = extractStepStatusFromString(
1593
+ v.replace(MALFORMED_CLOSE_RE_G, "</$1>")
1594
+ );
1595
+ if (fallback) {
1596
+ return fallback;
1597
+ }
1598
+ return v;
1599
+ }
1600
+ if (v && typeof v === "object" && itemType === "object") {
1601
+ return repairParsedAgainstSchema(v, itemSchema);
1602
+ }
1603
+ return v;
1604
+ }
1605
+ function tryParseStringToSchemaObject(xml, itemSchema) {
1606
+ try {
1607
+ const normalized = xml.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1608
+ const fixed = (0, import_rxml.parse)(normalized, itemSchema, { noChildNodes: [] });
1609
+ return typeof fixed === "string" ? null : fixed;
1610
+ } catch (e) {
1611
+ return null;
1612
+ }
1613
+ }
1614
+ function extractStepStatusFromString(normXml) {
1615
+ const stepMatch = normXml.match(STEP_TAG_RE);
1616
+ const statusMatch = normXml.match(STATUS_TAG_RE);
1617
+ if (stepMatch && statusMatch) {
1618
+ return { step: stepMatch[1], status: statusMatch[1] };
1619
+ }
1620
+ return null;
1621
+ }
1622
+
1623
+ // src/utils/type-guards.ts
1624
+ function isToolCallContent(content) {
1625
+ return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
1626
+ (typeof content.input === "string" || typeof content.input === "object");
1627
+ }
1628
+ function hasInputProperty(obj) {
1629
+ return typeof obj === "object" && obj !== null && "input" in obj;
1630
+ }
1631
+
1632
+ // src/protocols/morph-xml-protocol.ts
1633
+ var defaultPipelineConfig2 = defaultPipelineConfig;
1634
+ var applyHeuristicPipeline2 = applyHeuristicPipeline;
1635
+ var createIntermediateCall2 = createIntermediateCall;
1636
+ var mergePipelineConfigs2 = mergePipelineConfigs;
1637
+ var WHITESPACE_REGEX3 = /\s/;
1638
+ var MALFORMED_CLOSE_RE2 = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1639
+ var MALFORMED_CLOSE_RE_G2 = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1640
+ var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
1641
+ function normalizeCloseTags(xml) {
1642
+ return xml.replace(MALFORMED_CLOSE_RE_G2, "</$1>");
1643
+ }
1644
+ function tryParseSecondaryXml(content, toolSchema, options) {
1645
+ const normalized = normalizeCloseTags(content);
1646
+ const balanced = balanceTags(content);
1647
+ const hasMalformedClose = MALFORMED_CLOSE_RE2.test(content);
1648
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1649
+ return null;
1650
+ }
1651
+ try {
1652
+ let parsed = (0, import_rxml2.parse)(balanced, toolSchema, {
1653
+ onError: options == null ? void 0 : options.onError,
1654
+ noChildNodes: []
1655
+ });
1656
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1657
+ return parsed;
1658
+ } catch (e) {
1659
+ if (shouldDeduplicateStringTags(toolSchema)) {
1660
+ const deduped = dedupeStringTagsAgainstSchema(balanced, toolSchema);
1661
+ if (deduped !== balanced) {
1662
+ try {
1663
+ let reparsed = (0, import_rxml2.parse)(deduped, toolSchema, {
1664
+ onError: options == null ? void 0 : options.onError,
1665
+ noChildNodes: []
1666
+ });
1667
+ reparsed = repairParsedAgainstSchema(reparsed, toolSchema);
1668
+ return reparsed;
1669
+ } catch (e2) {
1670
+ return null;
1671
+ }
1672
+ }
1673
+ }
1674
+ return null;
1675
+ }
1676
+ }
1677
+ function dedupeStringTagsAgainstSchema(xml, schema) {
1678
+ const names = getStringPropertyNames(schema);
1679
+ let out = xml;
1680
+ for (const key of names) {
1681
+ out = dedupeSingleTag(out, key);
1682
+ }
1683
+ return out;
1684
+ }
1685
+ function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1686
+ if (toolCallStartIndex > currentIndex) {
1687
+ const textSegment = text.substring(currentIndex, toolCallStartIndex);
1688
+ if (textSegment.trim()) {
1689
+ processedElements.push({ type: "text", text: textSegment });
1690
+ }
1691
+ }
1692
+ return currentIndex;
1693
+ }
1694
+ function processToolCallWithPipeline(params) {
1695
+ var _a;
1696
+ const {
1697
+ toolCall,
1698
+ tools,
1699
+ options,
1700
+ text,
1701
+ processedElements,
1702
+ pipelineConfig = defaultPipelineConfig2,
1703
+ maxReparses
1704
+ } = params;
1705
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1706
+ const ctx = createIntermediateCall2(
1707
+ toolCall.toolName,
1708
+ toolCall.content,
1709
+ toolSchema
1710
+ );
1711
+ const result = applyHeuristicPipeline2(ctx, pipelineConfig, {
1712
+ parse: (xml, schema) => (0, import_rxml2.parse)(xml, schema, { onError: options == null ? void 0 : options.onError, noChildNodes: [] }),
1713
+ onError: options == null ? void 0 : options.onError,
1714
+ maxReparses
1715
+ });
1716
+ if (result.parsed !== null) {
1717
+ processedElements.push({
1718
+ type: "tool-call",
1719
+ toolCallId: (0, import_provider_utils2.generateId)(),
1720
+ toolName: toolCall.toolName,
1721
+ input: JSON.stringify(result.parsed)
1722
+ });
1723
+ } else {
1724
+ const originalCallText = text.substring(
1725
+ toolCall.startIndex,
1726
+ toolCall.endIndex
1727
+ );
1728
+ const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1729
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1730
+ toolCall: originalCallText,
1731
+ toolName: toolCall.toolName,
1732
+ error: result.errors[0]
1733
+ });
1734
+ processedElements.push({ type: "text", text: originalCallText });
1735
+ }
1736
+ }
1737
+ function processToolCall(params) {
1738
+ var _a;
1739
+ const { toolCall, tools, options, text, processedElements } = params;
1740
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1741
+ try {
1742
+ const primary = escapeInvalidLt(normalizeCloseTags(toolCall.content));
1743
+ let parsed = (0, import_rxml2.parse)(primary, toolSchema, {
1744
+ onError: options == null ? void 0 : options.onError,
1745
+ noChildNodes: []
1746
+ });
1747
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1748
+ processedElements.push({
1749
+ type: "tool-call",
1750
+ toolCallId: (0, import_provider_utils2.generateId)(),
1751
+ toolName: toolCall.toolName,
1752
+ input: JSON.stringify(parsed)
1753
+ });
1754
+ } catch (error) {
1755
+ const reparsed = tryParseSecondaryXml(
1756
+ toolCall.content,
1757
+ toolSchema,
1758
+ options
1759
+ );
1760
+ if (reparsed !== null) {
1761
+ processedElements.push({
1762
+ type: "tool-call",
1763
+ toolCallId: (0, import_provider_utils2.generateId)(),
1764
+ toolName: toolCall.toolName,
1765
+ input: JSON.stringify(reparsed)
1766
+ });
1767
+ return;
1768
+ }
1769
+ const originalCallText = text.substring(
1770
+ toolCall.startIndex,
1771
+ toolCall.endIndex
1772
+ );
1773
+ const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1774
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1775
+ toolCall: originalCallText,
1776
+ toolName: toolCall.toolName,
1777
+ error
1778
+ });
1779
+ processedElements.push({ type: "text", text: originalCallText });
1780
+ }
1781
+ }
1782
+ function addRemainingText(text, currentIndex, processedElements) {
1783
+ if (currentIndex < text.length) {
1784
+ const remainingText = text.substring(currentIndex);
1785
+ if (remainingText.trim()) {
1786
+ processedElements.push({ type: "text", text: remainingText });
1787
+ }
1788
+ }
1789
+ }
1790
+ function handleStreamingToolCallEndWithPipeline(params) {
1791
+ var _a;
1792
+ const {
1793
+ toolContent,
1356
1794
  currentToolCall,
1357
1795
  tools,
1358
1796
  options,
1359
- controller,
1797
+ ctrl,
1360
1798
  flushText,
1361
- setBuffer
1799
+ pipelineConfig = defaultPipelineConfig2,
1800
+ maxReparses
1362
1801
  } = params;
1363
- const endTag = `</${currentToolCall.name}>`;
1364
- const endTagIndex = buffer.indexOf(endTag);
1365
- if (endTagIndex !== -1) {
1366
- const toolContent = buffer.substring(0, endTagIndex);
1367
- const newBuffer = buffer.substring(endTagIndex + endTag.length);
1368
- setBuffer("");
1369
- handleStreamingToolCallEnd({
1370
- toolContent,
1802
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1803
+ const ctx = createIntermediateCall2(
1804
+ currentToolCall.name,
1805
+ toolContent,
1806
+ toolSchema
1807
+ );
1808
+ const result = applyHeuristicPipeline2(ctx, pipelineConfig, {
1809
+ parse: (xml, schema) => (0, import_rxml2.parse)(xml, schema, { onError: options == null ? void 0 : options.onError, noChildNodes: [] }),
1810
+ onError: options == null ? void 0 : options.onError,
1811
+ maxReparses
1812
+ });
1813
+ flushText(ctrl);
1814
+ if (result.parsed !== null) {
1815
+ ctrl.enqueue({
1816
+ type: "tool-call",
1817
+ toolCallId: (0, import_provider_utils2.generateId)(),
1818
+ toolName: currentToolCall.name,
1819
+ input: JSON.stringify(result.parsed)
1820
+ });
1821
+ } else {
1822
+ const endTag = `</${currentToolCall.name}>`;
1823
+ const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1824
+ const error = result.errors[0];
1825
+ let message = "Could not process streaming XML tool call; emitting original text.";
1826
+ if (error instanceof import_rxml2.RXMLDuplicateStringTagError) {
1827
+ message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1828
+ } else if (error instanceof import_rxml2.RXMLCoercionError) {
1829
+ message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1830
+ } else if (error instanceof import_rxml2.RXMLParseError) {
1831
+ message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1832
+ }
1833
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1834
+ toolCall: originalCallText,
1835
+ toolName: currentToolCall.name,
1836
+ error
1837
+ });
1838
+ flushText(ctrl, originalCallText);
1839
+ }
1840
+ }
1841
+ function handleStreamingToolCallEnd(params) {
1842
+ const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1843
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1844
+ try {
1845
+ const primary = escapeInvalidLt(normalizeCloseTags(toolContent));
1846
+ let parsed = (0, import_rxml2.parse)(primary, toolSchema, {
1847
+ onError: options == null ? void 0 : options.onError,
1848
+ noChildNodes: []
1849
+ });
1850
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1851
+ flushText(ctrl);
1852
+ ctrl.enqueue({
1853
+ type: "tool-call",
1854
+ toolCallId: (0, import_provider_utils2.generateId)(),
1855
+ toolName: currentToolCall.name,
1856
+ input: JSON.stringify(parsed)
1857
+ });
1858
+ } catch (error) {
1859
+ const parsed = tryParseSecondaryXml(toolContent, toolSchema, options);
1860
+ if (parsed !== null) {
1861
+ flushText(ctrl);
1862
+ ctrl.enqueue({
1863
+ type: "tool-call",
1864
+ toolCallId: (0, import_provider_utils2.generateId)(),
1865
+ toolName: currentToolCall.name,
1866
+ input: JSON.stringify(parsed)
1867
+ });
1868
+ return;
1869
+ }
1870
+ handleStreamingToolCallError({
1871
+ error,
1371
1872
  currentToolCall,
1372
- tools,
1873
+ toolContent,
1373
1874
  options,
1374
- ctrl: controller,
1875
+ ctrl,
1375
1876
  flushText
1376
1877
  });
1878
+ }
1879
+ }
1880
+ function handleStreamingToolCallError(params) {
1881
+ var _a;
1882
+ const { error, currentToolCall, toolContent, options, ctrl, flushText } = params;
1883
+ const endTag = `</${currentToolCall.name}>`;
1884
+ const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1885
+ let message = "Could not process streaming XML tool call; emitting original text.";
1886
+ if (error instanceof import_rxml2.RXMLDuplicateStringTagError) {
1887
+ message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1888
+ } else if (error instanceof import_rxml2.RXMLCoercionError) {
1889
+ message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1890
+ } else if (error instanceof import_rxml2.RXMLParseError) {
1891
+ message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1892
+ }
1893
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1894
+ toolCall: originalCallText,
1895
+ toolName: currentToolCall.name,
1896
+ error
1897
+ });
1898
+ flushText(ctrl, originalCallText);
1899
+ }
1900
+ function findEarliestToolTag(buffer, toolNames) {
1901
+ let bestIndex = -1;
1902
+ let bestName = "";
1903
+ let bestSelfClosing = false;
1904
+ if (toolNames.length > 0) {
1905
+ for (const name of toolNames) {
1906
+ const openTag = `<${name}>`;
1907
+ const selfTag = `<${name}/>`;
1908
+ const idxOpen = buffer.indexOf(openTag);
1909
+ const idxSelf = buffer.indexOf(selfTag);
1910
+ if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
1911
+ bestIndex = idxOpen;
1912
+ bestName = name;
1913
+ bestSelfClosing = false;
1914
+ }
1915
+ if (idxSelf !== -1 && (bestIndex === -1 || idxSelf < bestIndex)) {
1916
+ bestIndex = idxSelf;
1917
+ bestName = name;
1918
+ bestSelfClosing = true;
1919
+ }
1920
+ }
1921
+ }
1922
+ return { index: bestIndex, name: bestName, selfClosing: bestSelfClosing };
1923
+ }
1924
+ function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1925
+ const tail = Math.max(0, maxStartTagLen - 1);
1926
+ const safeLen = Math.max(0, buffer.length - tail);
1927
+ if (safeLen > 0) {
1928
+ const textToFlush = buffer.slice(0, safeLen);
1929
+ flushText(controller, textToFlush);
1930
+ return { buffer: buffer.slice(safeLen), shouldContinue: true };
1931
+ }
1932
+ return { buffer, shouldContinue: false };
1933
+ }
1934
+ function processToolCallInBuffer(params) {
1935
+ const {
1936
+ buffer,
1937
+ currentToolCall,
1938
+ tools,
1939
+ options,
1940
+ controller,
1941
+ flushText,
1942
+ setBuffer,
1943
+ pipelineConfig,
1944
+ maxReparses
1945
+ } = params;
1946
+ const endTag = `</${currentToolCall.name}>`;
1947
+ const normalized = normalizeCloseTags(buffer);
1948
+ const effectiveBuffer = normalized;
1949
+ const endTagIndex = effectiveBuffer.indexOf(endTag);
1950
+ if (endTagIndex !== -1) {
1951
+ const toolContent = effectiveBuffer.substring(0, endTagIndex);
1952
+ const newBuffer = effectiveBuffer.substring(endTagIndex + endTag.length);
1953
+ setBuffer("");
1954
+ if (pipelineConfig) {
1955
+ handleStreamingToolCallEndWithPipeline({
1956
+ toolContent,
1957
+ currentToolCall,
1958
+ tools,
1959
+ options,
1960
+ ctrl: controller,
1961
+ flushText,
1962
+ pipelineConfig,
1963
+ maxReparses
1964
+ });
1965
+ } else {
1966
+ handleStreamingToolCallEnd({
1967
+ toolContent,
1968
+ currentToolCall,
1969
+ tools,
1970
+ options,
1971
+ ctrl: controller,
1972
+ flushText
1973
+ });
1974
+ }
1377
1975
  setBuffer(newBuffer);
1378
1976
  return { buffer: newBuffer, currentToolCall: null, shouldBreak: false };
1379
1977
  }
1380
- return { buffer, currentToolCall, shouldBreak: true };
1978
+ return { buffer: effectiveBuffer, currentToolCall, shouldBreak: true };
1381
1979
  }
1382
1980
  function processNoToolCallInBuffer(params) {
1383
- const { buffer, toolNames, maxStartTagLen, controller, flushText } = params;
1384
- const { index: earliestStartTagIndex, name: earliestToolName } = findEarliestToolTag(buffer, toolNames);
1981
+ const {
1982
+ buffer,
1983
+ toolNames,
1984
+ maxStartTagLen,
1985
+ controller,
1986
+ flushText,
1987
+ tools,
1988
+ options,
1989
+ pipelineConfig,
1990
+ maxReparses
1991
+ } = params;
1992
+ const {
1993
+ index: earliestStartTagIndex,
1994
+ name: earliestToolName,
1995
+ selfClosing
1996
+ } = findEarliestToolTag(buffer, toolNames);
1385
1997
  if (earliestStartTagIndex !== -1) {
1386
1998
  const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
1387
1999
  flushText(controller, textBeforeTag);
2000
+ if (selfClosing) {
2001
+ const selfTag = `<${earliestToolName}/>`;
2002
+ const newBuffer2 = buffer.substring(
2003
+ earliestStartTagIndex + selfTag.length
2004
+ );
2005
+ if (pipelineConfig) {
2006
+ handleStreamingToolCallEndWithPipeline({
2007
+ toolContent: "",
2008
+ currentToolCall: { name: earliestToolName, content: "" },
2009
+ tools,
2010
+ options,
2011
+ ctrl: controller,
2012
+ flushText,
2013
+ pipelineConfig,
2014
+ maxReparses
2015
+ });
2016
+ } else {
2017
+ handleStreamingToolCallEnd({
2018
+ toolContent: "",
2019
+ currentToolCall: { name: earliestToolName, content: "" },
2020
+ tools,
2021
+ options,
2022
+ ctrl: controller,
2023
+ flushText
2024
+ });
2025
+ }
2026
+ return {
2027
+ buffer: newBuffer2,
2028
+ currentToolCall: null,
2029
+ shouldBreak: false,
2030
+ shouldContinue: false
2031
+ };
2032
+ }
1388
2033
  const startTag = `<${earliestToolName}>`;
1389
2034
  const newBuffer = buffer.substring(earliestStartTagIndex + startTag.length);
1390
2035
  return {
@@ -1413,7 +2058,7 @@ function createFlushTextHandler(getBuffer, setBuffer, getCurrentTextId, setCurre
1413
2058
  if (content) {
1414
2059
  const currentTextId2 = getCurrentTextId();
1415
2060
  if (!currentTextId2) {
1416
- const newId = (0, import_provider_utils3.generateId)();
2061
+ const newId = (0, import_provider_utils2.generateId)();
1417
2062
  setCurrentTextId(newId);
1418
2063
  controller.enqueue({ type: "text-start", id: newId });
1419
2064
  }
@@ -1441,7 +2086,9 @@ function processBufferWithToolCall(params, controller) {
1441
2086
  setCurrentToolCall,
1442
2087
  tools,
1443
2088
  options,
1444
- flushText
2089
+ flushText,
2090
+ pipelineConfig,
2091
+ maxReparses
1445
2092
  } = params;
1446
2093
  const currentToolCall = getCurrentToolCall();
1447
2094
  if (!currentToolCall) {
@@ -1454,7 +2101,9 @@ function processBufferWithToolCall(params, controller) {
1454
2101
  options,
1455
2102
  controller,
1456
2103
  flushText,
1457
- setBuffer
2104
+ setBuffer,
2105
+ pipelineConfig,
2106
+ maxReparses
1458
2107
  });
1459
2108
  setBuffer(result.buffer);
1460
2109
  setCurrentToolCall(result.currentToolCall);
@@ -1465,16 +2114,24 @@ function processBufferWithoutToolCall(params, controller) {
1465
2114
  getBuffer,
1466
2115
  setBuffer,
1467
2116
  setCurrentToolCall,
2117
+ tools,
2118
+ options,
1468
2119
  toolNames,
1469
2120
  maxStartTagLen,
1470
- flushText
2121
+ flushText,
2122
+ pipelineConfig,
2123
+ maxReparses
1471
2124
  } = params;
1472
2125
  const result = processNoToolCallInBuffer({
1473
2126
  buffer: getBuffer(),
1474
2127
  toolNames,
1475
2128
  maxStartTagLen,
1476
2129
  controller,
1477
- flushText
2130
+ flushText,
2131
+ tools,
2132
+ options,
2133
+ pipelineConfig,
2134
+ maxReparses
1478
2135
  });
1479
2136
  setBuffer(result.buffer);
1480
2137
  setCurrentToolCall(result.currentToolCall);
@@ -1496,194 +2153,425 @@ function processBufferLoop(params, controller) {
1496
2153
  params,
1497
2154
  controller
1498
2155
  );
1499
- if (shouldContinue) {
1500
- continue;
1501
- }
1502
- if (shouldBreak) {
1503
- break;
1504
- }
1505
- }
1506
- }
1507
- }
1508
- function createProcessBufferHandler(params) {
1509
- return (controller) => {
1510
- processBufferLoop(params, controller);
1511
- };
1512
- }
1513
- var morphXmlProtocol = () => ({
1514
- formatTools({ tools, toolSystemPromptTemplate }) {
1515
- const toolsForPrompt = (tools || []).map((tool) => ({
1516
- name: tool.name,
1517
- description: tool.description,
1518
- parameters: (0, import_rxml.unwrapJsonSchema)(tool.inputSchema)
1519
- }));
1520
- return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1521
- },
1522
- formatToolCall(toolCall) {
1523
- let args = {};
1524
- const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
1525
- if (typeof inputValue === "string") {
1526
- try {
1527
- args = JSON.parse(inputValue);
1528
- } catch (e) {
1529
- args = inputValue;
1530
- }
1531
- } else {
1532
- args = inputValue;
1533
- }
1534
- return (0, import_rxml.stringify)(toolCall.toolName, args, {
1535
- suppressEmptyNode: false,
1536
- format: false
1537
- });
1538
- },
1539
- formatToolResponse(toolResult) {
1540
- return (0, import_rxml.stringify)("tool_response", {
1541
- tool_name: toolResult.toolName,
1542
- result: toolResult.output
1543
- });
1544
- },
1545
- parseGeneratedText({ text, tools, options }) {
1546
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1547
- if (toolNames.length === 0) {
1548
- return [{ type: "text", text }];
1549
- }
1550
- const processedElements = [];
1551
- let currentIndex = 0;
1552
- const toolCalls = findToolCalls(text, toolNames);
1553
- for (const toolCall of toolCalls) {
1554
- currentIndex = processTextBeforeToolCall(
1555
- text,
1556
- currentIndex,
1557
- toolCall.startIndex,
1558
- processedElements
1559
- );
1560
- processToolCall({ toolCall, tools, options, text, processedElements });
1561
- currentIndex = toolCall.endIndex;
1562
- }
1563
- addRemainingText(text, currentIndex, processedElements);
1564
- return processedElements;
1565
- },
1566
- createStreamParser({ tools, options }) {
1567
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1568
- const maxStartTagLen = toolNames.length ? Math.max(...toolNames.map((n) => `<${n}>`.length)) : 0;
1569
- let buffer = "";
1570
- let currentToolCall = null;
1571
- let currentTextId = null;
1572
- const flushText = createFlushTextHandler(
1573
- () => buffer,
1574
- (newBuffer) => {
1575
- buffer = newBuffer;
1576
- },
1577
- () => currentTextId,
1578
- (newId) => {
1579
- currentTextId = newId;
2156
+ if (shouldContinue) {
2157
+ continue;
1580
2158
  }
1581
- );
1582
- const processChunk = (chunk, controller) => {
1583
- if (chunk.type !== "text-delta") {
1584
- if (buffer) {
1585
- flushText(controller);
1586
- }
1587
- controller.enqueue(chunk);
1588
- return;
2159
+ if (shouldBreak) {
2160
+ break;
1589
2161
  }
1590
- buffer += chunk.delta;
1591
- processBuffer(controller);
2162
+ }
2163
+ }
2164
+ }
2165
+ function createProcessBufferHandler(params) {
2166
+ return (controller) => {
2167
+ processBufferLoop(params, controller);
2168
+ };
2169
+ }
2170
+ function buildPipelineOptions(protocolOptions) {
2171
+ var _a, _b, _c;
2172
+ const maxReparses = protocolOptions == null ? void 0 : protocolOptions.maxReparses;
2173
+ if (protocolOptions == null ? void 0 : protocolOptions.pipeline) {
2174
+ return {
2175
+ pipelineConfig: mergePipelineConfigs2(
2176
+ defaultPipelineConfig2,
2177
+ protocolOptions.pipeline
2178
+ ),
2179
+ maxReparses
1592
2180
  };
1593
- const processBuffer = createProcessBufferHandler({
1594
- getBuffer: () => buffer,
1595
- setBuffer: (newBuffer) => {
1596
- buffer = newBuffer;
1597
- },
1598
- getCurrentToolCall: () => currentToolCall,
1599
- setCurrentToolCall: (newToolCall) => {
1600
- currentToolCall = newToolCall;
2181
+ }
2182
+ if (protocolOptions == null ? void 0 : protocolOptions.heuristics) {
2183
+ return {
2184
+ pipelineConfig: {
2185
+ ...defaultPipelineConfig2,
2186
+ preParse: [
2187
+ ...(_a = defaultPipelineConfig2.preParse) != null ? _a : [],
2188
+ ...protocolOptions.heuristics.filter((h) => h.phase === "pre-parse")
2189
+ ],
2190
+ fallbackReparse: [
2191
+ ...(_b = defaultPipelineConfig2.fallbackReparse) != null ? _b : [],
2192
+ ...protocolOptions.heuristics.filter(
2193
+ (h) => h.phase === "fallback-reparse"
2194
+ )
2195
+ ],
2196
+ postParse: [
2197
+ ...(_c = defaultPipelineConfig2.postParse) != null ? _c : [],
2198
+ ...protocolOptions.heuristics.filter((h) => h.phase === "post-parse")
2199
+ ]
1601
2200
  },
1602
- tools,
1603
- options,
1604
- toolNames,
1605
- maxStartTagLen,
1606
- flushText
1607
- });
1608
- const flushBuffer2 = (controller) => {
1609
- if (currentToolCall) {
1610
- const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
1611
- flushText(controller, unfinishedCall);
1612
- } else if (buffer) {
1613
- flushText(controller);
2201
+ maxReparses
2202
+ };
2203
+ }
2204
+ return { pipelineConfig: void 0, maxReparses };
2205
+ }
2206
+ var morphXmlProtocol = (protocolOptions) => {
2207
+ const { pipelineConfig, maxReparses } = buildPipelineOptions(protocolOptions);
2208
+ return {
2209
+ formatTools({ tools, toolSystemPromptTemplate }) {
2210
+ const toolsForPrompt = (tools || []).map((tool) => ({
2211
+ name: tool.name,
2212
+ description: tool.description,
2213
+ parameters: (0, import_rxml2.unwrapJsonSchema)(tool.inputSchema)
2214
+ }));
2215
+ return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
2216
+ },
2217
+ formatToolCall(toolCall) {
2218
+ let args = {};
2219
+ const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
2220
+ if (typeof inputValue === "string") {
2221
+ try {
2222
+ args = JSON.parse(inputValue);
2223
+ } catch (e) {
2224
+ args = inputValue;
2225
+ }
2226
+ } else {
2227
+ args = inputValue;
1614
2228
  }
1615
- if (currentTextId) {
1616
- controller.enqueue({ type: "text-end", id: currentTextId });
2229
+ return (0, import_rxml2.stringify)(toolCall.toolName, args, {
2230
+ suppressEmptyNode: false,
2231
+ format: false
2232
+ });
2233
+ },
2234
+ formatToolResponse(toolResult) {
2235
+ return (0, import_rxml2.stringify)("tool_response", {
2236
+ tool_name: toolResult.toolName,
2237
+ result: toolResult.output
2238
+ });
2239
+ },
2240
+ parseGeneratedText({ text, tools, options }) {
2241
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
2242
+ if (toolNames.length === 0) {
2243
+ return [{ type: "text", text }];
1617
2244
  }
1618
- };
1619
- return new TransformStream({
1620
- transform(chunk, controller) {
1621
- processChunk(chunk, controller);
1622
- },
1623
- flush(controller) {
1624
- flushBuffer2(controller);
2245
+ const processedElements = [];
2246
+ let currentIndex = 0;
2247
+ const toolCallsRaw = findToolCalls(text, toolNames);
2248
+ const toolCallsNorm = collectToolCallsFromNormalizedText(text, toolNames);
2249
+ const seen = /* @__PURE__ */ new Set();
2250
+ const toolCalls = [...toolCallsRaw, ...toolCallsNorm].filter((tc) => {
2251
+ const key = `${tc.toolName}:${tc.startIndex}:${tc.endIndex}`;
2252
+ if (seen.has(key)) {
2253
+ return false;
2254
+ }
2255
+ seen.add(key);
2256
+ return true;
2257
+ }).sort((a, b) => a.startIndex - b.startIndex);
2258
+ for (const toolCall of toolCalls) {
2259
+ currentIndex = processTextBeforeToolCall(
2260
+ text,
2261
+ currentIndex,
2262
+ toolCall.startIndex,
2263
+ processedElements
2264
+ );
2265
+ if (pipelineConfig) {
2266
+ processToolCallWithPipeline({
2267
+ toolCall,
2268
+ tools,
2269
+ options,
2270
+ text,
2271
+ processedElements,
2272
+ pipelineConfig,
2273
+ maxReparses
2274
+ });
2275
+ } else {
2276
+ processToolCall({
2277
+ toolCall,
2278
+ tools,
2279
+ options,
2280
+ text,
2281
+ processedElements
2282
+ });
2283
+ }
2284
+ currentIndex = toolCall.endIndex;
1625
2285
  }
1626
- });
1627
- },
1628
- extractToolCallSegments({ text, tools }) {
1629
- const toolNames = tools.map((t) => t.name).filter(Boolean);
1630
- if (toolNames.length === 0) {
1631
- return [];
2286
+ addRemainingText(text, currentIndex, processedElements);
2287
+ return processedElements;
2288
+ },
2289
+ createStreamParser({ tools, options }) {
2290
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
2291
+ const maxStartTagLen = toolNames.length ? Math.max(...toolNames.map((n) => `<${n}>`.length)) : 0;
2292
+ let buffer = "";
2293
+ let currentToolCall = null;
2294
+ let currentTextId = null;
2295
+ const flushText = createFlushTextHandler(
2296
+ () => buffer,
2297
+ (newBuffer) => {
2298
+ buffer = newBuffer;
2299
+ },
2300
+ () => currentTextId,
2301
+ (newId) => {
2302
+ currentTextId = newId;
2303
+ }
2304
+ );
2305
+ const processChunk = (chunk, controller) => {
2306
+ if (chunk.type !== "text-delta") {
2307
+ if (buffer) {
2308
+ flushText(controller);
2309
+ }
2310
+ controller.enqueue(chunk);
2311
+ return;
2312
+ }
2313
+ buffer += chunk.delta;
2314
+ processBuffer(controller);
2315
+ };
2316
+ const processBuffer = createProcessBufferHandler({
2317
+ getBuffer: () => buffer,
2318
+ setBuffer: (newBuffer) => {
2319
+ buffer = newBuffer;
2320
+ },
2321
+ getCurrentToolCall: () => currentToolCall,
2322
+ setCurrentToolCall: (newToolCall) => {
2323
+ currentToolCall = newToolCall;
2324
+ },
2325
+ tools,
2326
+ options,
2327
+ toolNames,
2328
+ maxStartTagLen,
2329
+ flushText,
2330
+ pipelineConfig,
2331
+ maxReparses
2332
+ });
2333
+ const flushBuffer2 = (controller) => {
2334
+ if (currentToolCall) {
2335
+ const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
2336
+ flushText(controller, unfinishedCall);
2337
+ } else if (buffer) {
2338
+ flushText(controller);
2339
+ }
2340
+ if (currentTextId) {
2341
+ controller.enqueue({ type: "text-end", id: currentTextId });
2342
+ }
2343
+ };
2344
+ return new TransformStream({
2345
+ transform(chunk, controller) {
2346
+ processChunk(chunk, controller);
2347
+ },
2348
+ flush(controller) {
2349
+ flushBuffer2(controller);
2350
+ }
2351
+ });
2352
+ },
2353
+ extractToolCallSegments({ text, tools }) {
2354
+ const toolNames = tools.map((t) => t.name).filter(Boolean);
2355
+ if (toolNames.length === 0) {
2356
+ return [];
2357
+ }
2358
+ return findToolCalls(text, toolNames).map((tc) => tc.segment);
1632
2359
  }
1633
- return findToolCalls(text, toolNames).map((tc) => tc.segment);
1634
- }
1635
- });
2360
+ };
2361
+ };
1636
2362
  function getToolSchema(tools, toolName) {
1637
2363
  var _a;
1638
2364
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
1639
2365
  }
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;
1645
- while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2366
+ function findClosingTagEndFlexible(text, contentStart, toolName) {
2367
+ let pos = contentStart;
2368
+ let depth = 1;
2369
+ while (pos < text.length) {
2370
+ const tok = nextTagToken(text, pos);
2371
+ if (tok.kind === "eof") {
2372
+ break;
2373
+ }
2374
+ const result = updateDepthWithToken(tok, toolName, depth);
2375
+ depth = result.depth;
2376
+ if (result.closedAt !== void 0) {
2377
+ return result.closedAt;
2378
+ }
2379
+ pos = tok.nextPos;
2380
+ }
2381
+ return -1;
2382
+ }
2383
+ function skipSpecialSegment(text, lt) {
2384
+ const next = text[lt + 1];
2385
+ if (next !== "!" && next !== "?") {
2386
+ return null;
2387
+ }
2388
+ const gt = text.indexOf(">", lt + 1);
2389
+ if (gt === -1) {
2390
+ return null;
2391
+ }
2392
+ return gt + 1;
2393
+ }
2394
+ function consumeClosingTag(text, lt, _toolName) {
2395
+ let p = lt + 2;
2396
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
2397
+ p += 1;
2398
+ }
2399
+ const gt = text.indexOf(">", lt + 1);
2400
+ const endPos = gt === -1 ? text.length : gt + 1;
2401
+ return { matched: false, endPos };
2402
+ }
2403
+ function consumeOpenTag(text, lt) {
2404
+ let p = lt + 1;
2405
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
2406
+ p += 1;
2407
+ }
2408
+ const nameStart = p;
2409
+ while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
2410
+ p += 1;
2411
+ }
2412
+ const name = text.slice(nameStart, p);
2413
+ const q = text.indexOf(">", p);
2414
+ if (q === -1) {
2415
+ return null;
2416
+ }
2417
+ let r = q - 1;
2418
+ while (r >= nameStart && WHITESPACE_REGEX3.test(text[r])) {
2419
+ r -= 1;
2420
+ }
2421
+ const selfClosing = text[r] === "/";
2422
+ return { name, selfClosing, nextPos: q + 1 };
2423
+ }
2424
+ function updateDepthWithToken(tok, toolName, depth) {
2425
+ if (tok.kind === "close" && tok.name === toolName) {
2426
+ const newDepth = depth - 1;
2427
+ return newDepth === 0 ? { depth: newDepth, closedAt: tok.nextPos } : { depth: newDepth };
2428
+ }
2429
+ if (tok.kind === "open" && tok.name === toolName && !tok.selfClosing) {
2430
+ return { depth: depth + 1 };
2431
+ }
2432
+ return { depth };
2433
+ }
2434
+ function nextTagToken(text, fromPos) {
2435
+ const lt = text.indexOf("<", fromPos);
2436
+ if (lt === -1 || lt + 1 >= text.length) {
2437
+ return { kind: "eof", nextPos: text.length };
2438
+ }
2439
+ const next = text[lt + 1];
2440
+ const specialEnd = skipSpecialSegment(text, lt);
2441
+ if (specialEnd !== null) {
2442
+ return { kind: "special", nextPos: specialEnd };
2443
+ }
2444
+ if (next === "/") {
2445
+ const closing = consumeClosingTag(text, lt, "");
2446
+ let p = lt + 2;
2447
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
1646
2448
  p += 1;
1647
2449
  }
1648
- if (text[p] === ">") {
1649
- fullTagEnd = p + 1;
2450
+ const nameStart = p;
2451
+ while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
2452
+ p += 1;
1650
2453
  }
2454
+ const name = text.slice(nameStart, p);
2455
+ return { kind: "close", name, nextPos: closing.endPos };
2456
+ }
2457
+ const open = consumeOpenTag(text, lt);
2458
+ if (open === null) {
2459
+ return { kind: "eof", nextPos: text.length };
1651
2460
  }
1652
- return fullTagEnd;
2461
+ return {
2462
+ kind: "open",
2463
+ name: open.name,
2464
+ selfClosing: open.selfClosing,
2465
+ nextPos: open.nextPos
2466
+ };
1653
2467
  }
1654
- function extractToolCallInfo(text, tagStart, toolName, range) {
2468
+ function collectToolCallsFromNormalizedText(text, toolNames) {
1655
2469
  var _a;
2470
+ const normalizedText = normalizeCloseTags(text);
2471
+ const collected = [];
2472
+ for (const toolName of toolNames) {
2473
+ const startTag = `<${toolName}>`;
2474
+ let idx = 0;
2475
+ let lastOrigIdx = 0;
2476
+ while (idx < normalizedText.length) {
2477
+ const tagStartNorm = normalizedText.indexOf(startTag, idx);
2478
+ if (tagStartNorm === -1) {
2479
+ break;
2480
+ }
2481
+ const contentStartNorm = tagStartNorm + startTag.length;
2482
+ const endNorm = findClosingTagEndFlexible(
2483
+ normalizedText,
2484
+ contentStartNorm,
2485
+ toolName
2486
+ );
2487
+ if (endNorm > contentStartNorm) {
2488
+ const tagStartOrig = text.indexOf(startTag, lastOrigIdx);
2489
+ const contentStartOrig = tagStartOrig + startTag.length;
2490
+ let endOrig = findClosingTagEndFlexible(
2491
+ text,
2492
+ contentStartOrig,
2493
+ toolName
2494
+ );
2495
+ if (endOrig === -1) {
2496
+ const approxLen = endNorm - tagStartNorm;
2497
+ endOrig = Math.min(text.length, tagStartOrig + approxLen);
2498
+ }
2499
+ const segment = text.substring(tagStartOrig, endOrig);
2500
+ const inner = (_a = (0, import_rxml2.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2501
+ collected.push({
2502
+ toolName,
2503
+ startIndex: tagStartOrig,
2504
+ endIndex: endOrig,
2505
+ content: inner,
2506
+ segment
2507
+ });
2508
+ lastOrigIdx = endOrig;
2509
+ idx = endNorm;
2510
+ } else {
2511
+ idx = contentStartNorm;
2512
+ }
2513
+ }
2514
+ }
2515
+ return collected.sort((a, b) => a.startIndex - b.startIndex);
2516
+ }
2517
+ function getNextTagInfo(text, toolName, fromIndex) {
1656
2518
  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);
1662
- return {
1663
- toolName,
1664
- startIndex: tagStart,
1665
- endIndex: fullTagEnd,
1666
- content,
1667
- segment
1668
- };
2519
+ const selfTag = `<${toolName}/>`;
2520
+ const openIdx = text.indexOf(startTag, fromIndex);
2521
+ const selfIdx = text.indexOf(selfTag, fromIndex);
2522
+ const hasOpen = openIdx !== -1;
2523
+ const hasSelf = selfIdx !== -1;
2524
+ if (!(hasOpen || hasSelf)) {
2525
+ return {
2526
+ found: false,
2527
+ tagStart: -1,
2528
+ selfClosing: false,
2529
+ startTag,
2530
+ selfTag
2531
+ };
2532
+ }
2533
+ const pickSelf = hasSelf && (!hasOpen || selfIdx < openIdx);
2534
+ const tagStart = pickSelf ? selfIdx : openIdx;
2535
+ return { found: true, tagStart, selfClosing: pickSelf, startTag, selfTag };
1669
2536
  }
1670
2537
  function findToolCallsForName(text, toolName) {
2538
+ var _a;
1671
2539
  const toolCalls = [];
1672
- const startTag = `<${toolName}>`;
1673
2540
  let searchIndex = 0;
1674
2541
  while (searchIndex < text.length) {
1675
- const tagStart = text.indexOf(startTag, searchIndex);
1676
- if (tagStart === -1) {
2542
+ const info = getNextTagInfo(text, toolName, searchIndex);
2543
+ if (!info.found) {
1677
2544
  break;
1678
2545
  }
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;
2546
+ const { tagStart, selfClosing, startTag, selfTag } = info;
2547
+ if (selfClosing) {
2548
+ const endIndex = tagStart + selfTag.length;
2549
+ const segment = text.substring(tagStart, endIndex);
2550
+ toolCalls.push({
2551
+ toolName,
2552
+ startIndex: tagStart,
2553
+ endIndex,
2554
+ content: "",
2555
+ segment
2556
+ });
2557
+ searchIndex = endIndex;
2558
+ continue;
2559
+ }
2560
+ const contentStart = tagStart + startTag.length;
2561
+ const fullTagEnd = findClosingTagEndFlexible(text, contentStart, toolName);
2562
+ if (fullTagEnd !== -1 && fullTagEnd > contentStart) {
2563
+ const segment = text.substring(tagStart, fullTagEnd);
2564
+ const inner = (_a = (0, import_rxml2.extractRawInner)(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2565
+ toolCalls.push({
2566
+ toolName,
2567
+ startIndex: tagStart,
2568
+ endIndex: fullTagEnd,
2569
+ content: inner,
2570
+ segment
2571
+ });
2572
+ searchIndex = fullTagEnd;
1685
2573
  } else {
1686
- searchIndex = tagStart + startTag.length;
2574
+ searchIndex = contentStart;
1687
2575
  }
1688
2576
  }
1689
2577
  return toolCalls;
@@ -1697,14 +2585,50 @@ function findToolCalls(text, toolNames) {
1697
2585
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1698
2586
  }
1699
2587
 
1700
- // src/protocols/tool-call-protocol.ts
1701
- function isProtocolFactory(protocol) {
1702
- return typeof protocol === "function";
2588
+ // src/generate-handler.ts
2589
+ var import_provider_utils3 = require("@ai-sdk/provider-utils");
2590
+ var import_rxml3 = require("@ai-sdk-tool/rxml");
2591
+
2592
+ // src/utils/on-error.ts
2593
+ function extractOnErrorOption(providerOptions) {
2594
+ var _a;
2595
+ if (providerOptions && typeof providerOptions === "object") {
2596
+ const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
2597
+ return onError ? { onError } : void 0;
2598
+ }
2599
+ return;
2600
+ }
2601
+
2602
+ // src/utils/provider-options.ts
2603
+ var originalToolsSchema = {
2604
+ encode: encodeOriginalTools,
2605
+ decode: decodeOriginalTools
2606
+ };
2607
+ function encodeOriginalTools(tools) {
2608
+ return (tools == null ? void 0 : tools.map((t) => ({
2609
+ name: t.name,
2610
+ inputSchema: JSON.stringify(t.inputSchema)
2611
+ }))) || [];
2612
+ }
2613
+ function decodeOriginalTools(originalTools) {
2614
+ if (!originalTools) {
2615
+ return [];
2616
+ }
2617
+ return originalTools.map(
2618
+ (t) => ({
2619
+ type: "function",
2620
+ name: t.name,
2621
+ inputSchema: JSON.parse(t.inputSchema)
2622
+ })
2623
+ );
2624
+ }
2625
+ function isToolChoiceActive(params) {
2626
+ var _a, _b, _c;
2627
+ const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
2628
+ 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
2629
  }
1704
2630
 
1705
2631
  // src/generate-handler.ts
1706
- var import_provider_utils4 = require("@ai-sdk/provider-utils");
1707
- var import_rxml2 = require("@ai-sdk-tool/rxml");
1708
2632
  function parseToolChoiceJson(text, providerOptions) {
1709
2633
  var _a;
1710
2634
  try {
@@ -1748,7 +2672,7 @@ async function handleToolChoice(doGenerate, params) {
1748
2672
  }
1749
2673
  const toolCall = {
1750
2674
  type: "tool-call",
1751
- toolCallId: (0, import_provider_utils4.generateId)(),
2675
+ toolCallId: (0, import_provider_utils3.generateId)(),
1752
2676
  toolName: parsed.name || "unknown",
1753
2677
  input: JSON.stringify(parsed.arguments || {})
1754
2678
  };
@@ -1867,15 +2791,20 @@ function fixToolCallWithSchema(part, tools) {
1867
2791
  args = tc.input;
1868
2792
  }
1869
2793
  const schema = (_a = tools.find((t) => t.name === tc.toolName)) == null ? void 0 : _a.inputSchema;
1870
- const coerced = (0, import_rxml2.coerceBySchema)(args, schema);
2794
+ const coerced = (0, import_rxml3.coerceBySchema)(args, schema);
1871
2795
  return {
1872
2796
  ...part,
1873
2797
  input: JSON.stringify(coerced != null ? coerced : {})
1874
2798
  };
1875
2799
  }
1876
2800
 
2801
+ // src/protocols/tool-call-protocol.ts
2802
+ function isProtocolFactory(protocol) {
2803
+ return typeof protocol === "function";
2804
+ }
2805
+
1877
2806
  // src/stream-handler.ts
1878
- var import_provider_utils5 = require("@ai-sdk/provider-utils");
2807
+ var import_provider_utils4 = require("@ai-sdk/provider-utils");
1879
2808
  function extractToolCallSegments(protocol, fullRawText, tools) {
1880
2809
  const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({
1881
2810
  text: fullRawText,
@@ -1912,7 +2841,7 @@ function handleDebugSummary(parsedToolCalls, origin, params) {
1912
2841
  }
1913
2842
  function createDebugSummaryTransform({
1914
2843
  protocol,
1915
- fullRawText,
2844
+ getFullRawText,
1916
2845
  tools,
1917
2846
  params
1918
2847
  }) {
@@ -1925,11 +2854,9 @@ function createDebugSummaryTransform({
1925
2854
  }
1926
2855
  if (part.type === "finish") {
1927
2856
  try {
1928
- const origin = extractToolCallSegments(
1929
- protocol,
1930
- fullRawText,
1931
- tools
1932
- );
2857
+ const raw = getFullRawText();
2858
+ logRawChunk(raw);
2859
+ const origin = extractToolCallSegments(protocol, raw, tools);
1933
2860
  handleDebugSummary(parsedToolCalls, origin, params);
1934
2861
  } catch (e) {
1935
2862
  }
@@ -2027,7 +2954,7 @@ async function wrapStream({
2027
2954
  const withSummary = parsed.pipeThrough(
2028
2955
  createDebugSummaryTransform({
2029
2956
  protocol,
2030
- fullRawText,
2957
+ getFullRawText: () => fullRawText,
2031
2958
  tools,
2032
2959
  params
2033
2960
  })
@@ -2061,19 +2988,26 @@ async function toolChoiceStream({
2061
2988
  }
2062
2989
  const toolCallChunk = {
2063
2990
  type: "tool-call",
2064
- toolCallId: (0, import_provider_utils5.generateId)(),
2991
+ toolCallId: (0, import_provider_utils4.generateId)(),
2065
2992
  toolName: toolJson.name || "unknown",
2066
2993
  input: JSON.stringify(toolJson.arguments || {})
2067
2994
  };
2068
2995
  const finishChunk = {
2069
2996
  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
2997
+ usage: (result == null ? void 0 : result.usage) || {
2998
+ inputTokens: {
2999
+ total: 0,
3000
+ noCache: void 0,
3001
+ cacheRead: void 0,
3002
+ cacheWrite: void 0
3003
+ },
3004
+ outputTokens: {
3005
+ total: 0,
3006
+ text: void 0,
3007
+ reasoning: void 0
3008
+ }
2075
3009
  },
2076
- finishReason: "tool-calls"
3010
+ finishReason: { unified: "tool-calls", raw: void 0 }
2077
3011
  };
2078
3012
  const stream = new ReadableStream({
2079
3013
  start(controller) {
@@ -2107,26 +3041,106 @@ async function toolChoiceStream({
2107
3041
  };
2108
3042
  }
2109
3043
 
3044
+ // src/utils/dynamic-tool-schema.ts
3045
+ function createDynamicIfThenElseSchema(tools) {
3046
+ let currentSchema = {};
3047
+ const toolNames = [];
3048
+ for (let i = tools.length - 1; i >= 0; i -= 1) {
3049
+ const tool = tools[i];
3050
+ if (tool.type === "provider") {
3051
+ throw new Error(
3052
+ "Provider tools are not supported by this middleware. Please use function tools."
3053
+ );
3054
+ }
3055
+ toolNames.unshift(tool.name);
3056
+ const toolCondition = {
3057
+ if: {
3058
+ properties: {
3059
+ name: {
3060
+ const: tool.name
3061
+ }
3062
+ },
3063
+ required: ["name"]
3064
+ },
3065
+ // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
3066
+ then: {
3067
+ properties: {
3068
+ name: {
3069
+ const: tool.name
3070
+ },
3071
+ arguments: tool.inputSchema
3072
+ },
3073
+ required: ["name", "arguments"]
3074
+ }
3075
+ };
3076
+ if (Object.keys(currentSchema).length > 0) {
3077
+ toolCondition.else = currentSchema;
3078
+ }
3079
+ currentSchema = toolCondition;
3080
+ }
3081
+ return {
3082
+ type: "object",
3083
+ // Explicitly specify type as "object"
3084
+ properties: {
3085
+ name: {
3086
+ type: "string",
3087
+ description: "Name of the tool to call",
3088
+ enum: toolNames
3089
+ },
3090
+ arguments: {
3091
+ type: "object",
3092
+ // By default, arguments is also specified as object type
3093
+ description: "Argument object to be passed to the tool"
3094
+ }
3095
+ },
3096
+ required: ["name", "arguments"],
3097
+ ...currentSchema
3098
+ };
3099
+ }
3100
+
2110
3101
  // src/transform-handler.ts
2111
- function buildFinalPrompt(systemPrompt, processedPrompt) {
2112
- var _a;
2113
- if (((_a = processedPrompt[0]) == null ? void 0 : _a.role) === "system") {
3102
+ function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
3103
+ const systemIndex = processedPrompt.findIndex((m) => m.role === "system");
3104
+ if (systemIndex !== -1) {
3105
+ const existing = processedPrompt[systemIndex].content;
3106
+ let existingText = "";
3107
+ if (typeof existing === "string") {
3108
+ existingText = existing;
3109
+ } else if (Array.isArray(existing)) {
3110
+ existingText = existing.map((p) => {
3111
+ var _a;
3112
+ return (p == null ? void 0 : p.type) === "text" ? (_a = p.text) != null ? _a : "" : "";
3113
+ }).filter(Boolean).join("\n");
3114
+ } else {
3115
+ existingText = String(existing != null ? existing : "");
3116
+ }
3117
+ const mergedContent = placement === "first" ? `${systemPrompt}
3118
+
3119
+ ${existingText}` : `${existingText}
3120
+
3121
+ ${systemPrompt}`;
3122
+ return processedPrompt.map(
3123
+ (m, idx) => idx === systemIndex ? {
3124
+ ...m,
3125
+ content: mergedContent
3126
+ } : m
3127
+ );
3128
+ }
3129
+ if (placement === "first") {
2114
3130
  return [
2115
3131
  {
2116
3132
  role: "system",
2117
- content: `${systemPrompt}
2118
-
2119
- ${processedPrompt[0].content}`
3133
+ content: systemPrompt
2120
3134
  },
2121
- ...processedPrompt.slice(1)
3135
+ ...processedPrompt
2122
3136
  ];
2123
3137
  }
2124
3138
  return [
3139
+ ...processedPrompt,
2125
3140
  {
2126
3141
  role: "system",
2127
3142
  content: systemPrompt
2128
- },
2129
- ...processedPrompt
3143
+ }
2130
3144
  ];
2131
3145
  }
2132
3146
  function buildBaseReturnParams(params, finalPrompt, functionTools) {
@@ -2226,7 +3240,8 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2226
3240
  function transformParams({
2227
3241
  params,
2228
3242
  protocol,
2229
- toolSystemPromptTemplate
3243
+ toolSystemPromptTemplate,
3244
+ placement = "first"
2230
3245
  }) {
2231
3246
  var _a, _b, _c, _d, _e;
2232
3247
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
@@ -2242,7 +3257,11 @@ function transformParams({
2242
3257
  resolvedProtocol,
2243
3258
  extractOnErrorOption(params.providerOptions)
2244
3259
  );
2245
- const finalPrompt = buildFinalPrompt(systemPrompt, processedPrompt);
3260
+ const finalPrompt = buildFinalPrompt(
3261
+ systemPrompt,
3262
+ processedPrompt,
3263
+ placement
3264
+ );
2246
3265
  const baseReturnParams = buildBaseReturnParams(
2247
3266
  params,
2248
3267
  finalPrompt,
@@ -2319,7 +3338,10 @@ function processMessage(message, resolvedProtocol, providerOptions) {
2319
3338
  };
2320
3339
  }
2321
3340
  if (message.role === "tool") {
2322
- return processToolMessage(message.content, resolvedProtocol);
3341
+ const toolResultParts = message.content.filter(
3342
+ (part) => part.type === "tool-result"
3343
+ );
3344
+ return processToolMessage(toolResultParts, resolvedProtocol);
2323
3345
  }
2324
3346
  return message;
2325
3347
  }
@@ -2394,7 +3416,8 @@ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
2394
3416
  // src/tool-call-middleware.ts
2395
3417
  function createToolMiddleware({
2396
3418
  protocol,
2397
- toolSystemPromptTemplate
3419
+ toolSystemPromptTemplate,
3420
+ placement = "last"
2398
3421
  }) {
2399
3422
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2400
3423
  return {
@@ -2421,6 +3444,7 @@ function createToolMiddleware({
2421
3444
  transformParams: async ({ params }) => transformParams({
2422
3445
  protocol: resolvedProtocol,
2423
3446
  toolSystemPromptTemplate,
3447
+ placement,
2424
3448
  params
2425
3449
  })
2426
3450
  };
@@ -2466,6 +3490,7 @@ For each function call return a json object with function name and arguments wit
2466
3490
  });
2467
3491
  var morphXmlToolMiddleware = createToolMiddleware({
2468
3492
  protocol: morphXmlProtocol,
3493
+ placement: "last",
2469
3494
  toolSystemPromptTemplate(tools) {
2470
3495
  return `You are a function calling AI model.
2471
3496
 
@@ -2488,7 +3513,7 @@ Available functions are listed inside <tools></tools>.
2488
3513
  }
2489
3514
  });
2490
3515
 
2491
- // src/community/index.ts
3516
+ // src/community/sijawara.ts
2492
3517
  var sijawaraDetailedXmlToolMiddleware = createToolMiddleware({
2493
3518
  protocol: morphXmlProtocol,
2494
3519
  toolSystemPromptTemplate(tools) {