@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.
@@ -1,14 +1,5 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- // src/protocols/dummy-protocol.ts
8
- import { generateId } from "@ai-sdk/provider-utils";
9
-
10
1
  // src/protocols/json-mix-protocol.ts
11
- import { generateId as generateId2 } from "@ai-sdk/provider-utils";
2
+ import { generateId } from "@ai-sdk/provider-utils";
12
3
 
13
4
  // src/utils/debug.ts
14
5
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -56,6 +47,7 @@ var cBgGreen = color(ANSI_BG_GREEN);
56
47
  var cInverse = color(ANSI_INVERSE);
57
48
  var cUnderline = color(ANSI_UNDERLINE);
58
49
  var cBold = color(ANSI_BOLD);
50
+ var MAX_SNIPPET_LENGTH = 800;
59
51
  function safeStringify(value) {
60
52
  try {
61
53
  return `
@@ -64,6 +56,41 @@ ${typeof value === "string" ? value : JSON.stringify(value, null, 2)}`;
64
56
  return String(value);
65
57
  }
66
58
  }
59
+ function formatError(error) {
60
+ if (error instanceof Error) {
61
+ const stack = error.stack ? `
62
+ ${error.stack}` : "";
63
+ return `
64
+ ${error.name}: ${error.message}${stack}`;
65
+ }
66
+ return safeStringify(error);
67
+ }
68
+ function truncateSnippet(snippet) {
69
+ if (snippet.length <= MAX_SNIPPET_LENGTH) {
70
+ return snippet;
71
+ }
72
+ return `${snippet.slice(0, MAX_SNIPPET_LENGTH)}
73
+ \u2026[truncated ${snippet.length - MAX_SNIPPET_LENGTH} chars]`;
74
+ }
75
+ function logParseFailure({
76
+ phase,
77
+ reason,
78
+ snippet,
79
+ error
80
+ }) {
81
+ if (getDebugLevel() !== "parse") {
82
+ return;
83
+ }
84
+ const label = cBgBlue(`[${phase}]`);
85
+ console.log(cGray("[debug:mw:fail]"), label, cYellow(reason));
86
+ if (snippet) {
87
+ const formatted = truncateSnippet(snippet);
88
+ console.log(cGray("[debug:mw:fail:snippet]"), formatted);
89
+ }
90
+ if (error) {
91
+ console.log(cGray("[debug:mw:fail:error]"), cCyan(formatError(error)));
92
+ }
93
+ }
67
94
  function logRawChunk(part) {
68
95
  console.log(cGray("[debug:mw:raw]"), cYellow(safeStringify(part)));
69
96
  }
@@ -129,63 +156,6 @@ ${rendered}`);
129
156
  }
130
157
  }
131
158
 
132
- // src/utils/dynamic-tool-schema.ts
133
- function createDynamicIfThenElseSchema(tools) {
134
- let currentSchema = {};
135
- const toolNames = [];
136
- for (let i = tools.length - 1; i >= 0; i -= 1) {
137
- const tool = tools[i];
138
- if (tool.type === "provider-defined") {
139
- throw new Error(
140
- "Provider-defined tools are not supported by this middleware. Please use custom tools."
141
- );
142
- }
143
- toolNames.unshift(tool.name);
144
- const toolCondition = {
145
- if: {
146
- properties: {
147
- name: {
148
- const: tool.name
149
- }
150
- },
151
- required: ["name"]
152
- },
153
- // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
154
- then: {
155
- properties: {
156
- name: {
157
- const: tool.name
158
- },
159
- arguments: tool.inputSchema
160
- },
161
- required: ["name", "arguments"]
162
- }
163
- };
164
- if (Object.keys(currentSchema).length > 0) {
165
- toolCondition.else = currentSchema;
166
- }
167
- currentSchema = toolCondition;
168
- }
169
- return {
170
- type: "object",
171
- // Explicitly specify type as "object"
172
- properties: {
173
- name: {
174
- type: "string",
175
- description: "Name of the tool to call",
176
- enum: toolNames
177
- },
178
- arguments: {
179
- type: "object",
180
- // By default, arguments is also specified as object type
181
- description: "Argument object to be passed to the tool"
182
- }
183
- },
184
- required: ["name", "arguments"],
185
- ...currentSchema
186
- };
187
- }
188
-
189
159
  // src/utils/get-potential-start-index.ts
190
160
  function getPotentialStartIndex(text, searchedText) {
191
161
  if (searchedText.length === 0) {
@@ -204,60 +174,12 @@ function getPotentialStartIndex(text, searchedText) {
204
174
  return null;
205
175
  }
206
176
 
207
- // src/utils/on-error.ts
208
- function extractOnErrorOption(providerOptions) {
209
- var _a;
210
- if (providerOptions && typeof providerOptions === "object") {
211
- const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
212
- return onError ? { onError } : void 0;
213
- }
214
- return;
215
- }
216
-
217
- // src/utils/provider-options.ts
218
- var originalToolsSchema = {
219
- encode: encodeOriginalTools,
220
- decode: decodeOriginalTools
221
- };
222
- function encodeOriginalTools(tools) {
223
- return (tools == null ? void 0 : tools.map((t) => ({
224
- name: t.name,
225
- inputSchema: JSON.stringify(t.inputSchema)
226
- }))) || [];
227
- }
228
- function decodeOriginalTools(originalTools) {
229
- if (!originalTools) {
230
- return [];
231
- }
232
- return originalTools.map(
233
- (t) => ({
234
- type: "function",
235
- name: t.name,
236
- inputSchema: JSON.parse(t.inputSchema)
237
- })
238
- );
239
- }
240
- function extractToolNamesFromOriginalTools(originalTools) {
241
- return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
242
- }
243
- function isToolChoiceActive(params) {
244
- var _a, _b, _c;
245
- const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
246
- 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"));
247
- }
248
-
249
177
  // src/utils/regex.ts
250
178
  function escapeRegExp(literal) {
251
179
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
252
180
  }
253
181
 
254
182
  // src/utils/robust-json.ts
255
- var robust_json_exports = {};
256
- __export(robust_json_exports, {
257
- parse: () => parse,
258
- stringify: () => stringify,
259
- transform: () => transform
260
- });
261
183
  var WHITESPACE_TEST_REGEX = /\s/;
262
184
  var WHITESPACE_REGEX = /^\s+/;
263
185
  var OBJECT_START_REGEX = /^\{/;
@@ -922,19 +844,6 @@ function stringify(obj) {
922
844
  return "null";
923
845
  }
924
846
 
925
- // src/utils/type-guards.ts
926
- function isToolCallContent(content) {
927
- return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
928
- (typeof content.input === "string" || typeof content.input === "object");
929
- }
930
- function isToolResultPart(content) {
931
- const c = content;
932
- return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
933
- }
934
- function hasInputProperty(obj) {
935
- return typeof obj === "object" && obj !== null && "input" in obj;
936
- }
937
-
938
847
  // src/protocols/json-mix-protocol.ts
939
848
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
940
849
  var _a;
@@ -942,11 +851,17 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
942
851
  const parsedToolCall = parse(toolCallJson);
943
852
  processedElements.push({
944
853
  type: "tool-call",
945
- toolCallId: generateId2(),
854
+ toolCallId: generateId(),
946
855
  toolName: parsedToolCall.name,
947
856
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
948
857
  });
949
858
  } catch (error) {
859
+ logParseFailure({
860
+ phase: "generated-text",
861
+ reason: "Failed to parse tool call JSON segment",
862
+ snippet: fullMatch,
863
+ error
864
+ });
950
865
  if (options == null ? void 0 : options.onError) {
951
866
  options.onError(
952
867
  "Could not process JSON tool call, keeping original text.",
@@ -979,7 +894,7 @@ function flushBuffer(state, controller, toolCallStart) {
979
894
  return;
980
895
  }
981
896
  if (!state.currentTextId) {
982
- state.currentTextId = generateId2();
897
+ state.currentTextId = generateId();
983
898
  controller.enqueue({ type: "text-start", id: state.currentTextId });
984
899
  state.hasEmittedTextStart = true;
985
900
  }
@@ -1002,7 +917,12 @@ function emitIncompleteToolCall(state, controller, toolCallStart) {
1002
917
  if (!state.currentToolCallJson) {
1003
918
  return;
1004
919
  }
1005
- const errorId = generateId2();
920
+ logParseFailure({
921
+ phase: "stream",
922
+ reason: "Incomplete streaming tool call segment emitted as text",
923
+ snippet: `${toolCallStart}${state.currentToolCallJson}`
924
+ });
925
+ const errorId = generateId();
1006
926
  controller.enqueue({ type: "text-start", id: errorId });
1007
927
  controller.enqueue({
1008
928
  type: "text-delta",
@@ -1026,7 +946,7 @@ function publishText(text, state, controller) {
1026
946
  state.currentToolCallJson += text;
1027
947
  } else if (text.length > 0) {
1028
948
  if (!state.currentTextId) {
1029
- state.currentTextId = generateId2();
949
+ state.currentTextId = generateId();
1030
950
  controller.enqueue({ type: "text-start", id: state.currentTextId });
1031
951
  state.hasEmittedTextStart = true;
1032
952
  }
@@ -1041,16 +961,22 @@ function emitToolCall(context) {
1041
961
  var _a;
1042
962
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
1043
963
  try {
1044
- const parsedToolCall = robust_json_exports.parse(state.currentToolCallJson);
964
+ const parsedToolCall = parse(state.currentToolCallJson);
1045
965
  closeTextBlock(state, controller);
1046
966
  controller.enqueue({
1047
967
  type: "tool-call",
1048
- toolCallId: generateId2(),
968
+ toolCallId: generateId(),
1049
969
  toolName: parsedToolCall.name,
1050
970
  input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1051
971
  });
1052
- } catch (e) {
1053
- const errorId = generateId2();
972
+ } catch (error) {
973
+ logParseFailure({
974
+ phase: "stream",
975
+ reason: "Failed to parse streaming tool call JSON segment",
976
+ snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
977
+ error
978
+ });
979
+ const errorId = generateId();
1054
980
  controller.enqueue({ type: "text-start", id: errorId });
1055
981
  controller.enqueue({
1056
982
  type: "text-delta",
@@ -1215,171 +1141,908 @@ var jsonMixProtocol = ({
1215
1141
  });
1216
1142
 
1217
1143
  // src/protocols/morph-xml-protocol.ts
1218
- import { generateId as generateId3 } from "@ai-sdk/provider-utils";
1144
+ import { generateId as generateId2 } from "@ai-sdk/provider-utils";
1219
1145
  import {
1220
1146
  extractRawInner,
1221
- findFirstTopLevelRange,
1222
- parse as parse2,
1147
+ parse as parse3,
1223
1148
  RXMLCoercionError,
1224
1149
  RXMLDuplicateStringTagError,
1225
1150
  RXMLParseError,
1226
1151
  stringify as stringify2,
1227
- unwrapJsonSchema
1152
+ unwrapJsonSchema as unwrapJsonSchema2
1228
1153
  } from "@ai-sdk-tool/rxml";
1229
- var WHITESPACE_REGEX2 = /\s/;
1230
- function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1231
- if (toolCallStartIndex > currentIndex) {
1232
- const textSegment = text.substring(currentIndex, toolCallStartIndex);
1233
- if (textSegment.trim()) {
1234
- processedElements.push({ type: "text", text: textSegment });
1235
- }
1154
+
1155
+ // src/heuristics/engine.ts
1156
+ function applyRawSegmentUpdate(current, result) {
1157
+ if (result.rawSegment !== void 0) {
1158
+ return { ...current, rawSegment: result.rawSegment };
1236
1159
  }
1237
- return currentIndex;
1160
+ return current;
1238
1161
  }
1239
- function processToolCall(params) {
1240
- var _a;
1241
- const { toolCall, tools, options, text, processedElements } = params;
1242
- try {
1243
- const toolSchema = getToolSchema(tools, toolCall.toolName);
1244
- const parsed = parse2(toolCall.content, toolSchema, {
1245
- onError: options == null ? void 0 : options.onError,
1246
- // Disable HTML self-closing tag behavior to allow base, meta, link etc. as regular tags
1247
- noChildNodes: []
1248
- });
1249
- processedElements.push({
1250
- type: "tool-call",
1251
- toolCallId: generateId3(),
1252
- toolName: toolCall.toolName,
1253
- input: JSON.stringify(parsed)
1254
- });
1255
- } catch (error) {
1256
- const originalCallText = text.substring(
1257
- toolCall.startIndex,
1258
- toolCall.endIndex
1259
- );
1260
- const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1261
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1262
- toolCall: originalCallText,
1263
- toolName: toolCall.toolName,
1264
- error
1265
- });
1266
- processedElements.push({ type: "text", text: originalCallText });
1162
+ function applyParsedUpdate(current, result) {
1163
+ if (result.parsed !== void 0) {
1164
+ return { ...current, parsed: result.parsed };
1267
1165
  }
1166
+ return current;
1268
1167
  }
1269
- function addRemainingText(text, currentIndex, processedElements) {
1270
- if (currentIndex < text.length) {
1271
- const remainingText = text.substring(currentIndex);
1272
- if (remainingText.trim()) {
1273
- processedElements.push({ type: "text", text: remainingText });
1274
- }
1168
+ function applyWarningsUpdate(current, result) {
1169
+ var _a, _b;
1170
+ if (result.warnings && result.warnings.length > 0) {
1171
+ const meta = (_a = current.meta) != null ? _a : {};
1172
+ const existingWarnings = (_b = meta.warnings) != null ? _b : [];
1173
+ return {
1174
+ ...current,
1175
+ meta: { ...meta, warnings: [...existingWarnings, ...result.warnings] }
1176
+ };
1275
1177
  }
1178
+ return current;
1276
1179
  }
1277
- function handleStreamingToolCallEnd(params) {
1278
- const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1180
+ function attemptReparse(current, result, reparseCount, maxReparses, parse4) {
1181
+ if (!result.reparse || result.rawSegment === void 0 || reparseCount >= maxReparses) {
1182
+ return { state: current, newCount: reparseCount };
1183
+ }
1279
1184
  try {
1280
- const toolSchema = getToolSchema(tools, currentToolCall.name);
1281
- const parsed = parse2(toolContent, toolSchema, {
1282
- onError: options == null ? void 0 : options.onError,
1283
- noChildNodes: []
1284
- });
1285
- flushText(ctrl);
1286
- ctrl.enqueue({
1287
- type: "tool-call",
1288
- toolCallId: generateId3(),
1289
- toolName: currentToolCall.name,
1290
- input: JSON.stringify(parsed)
1291
- });
1185
+ const reparsed = parse4(result.rawSegment, current.schema);
1186
+ return {
1187
+ state: { ...current, parsed: reparsed, errors: [] },
1188
+ newCount: reparseCount + 1
1189
+ };
1292
1190
  } catch (error) {
1293
- handleStreamingToolCallError({
1294
- error,
1295
- currentToolCall,
1296
- toolContent,
1297
- options,
1298
- ctrl,
1299
- flushText
1300
- });
1191
+ return {
1192
+ state: { ...current, errors: [...current.errors, error] },
1193
+ newCount: reparseCount + 1
1194
+ };
1301
1195
  }
1302
1196
  }
1303
- function handleStreamingToolCallError(params) {
1197
+ function executePhase(ctx, heuristics, options) {
1304
1198
  var _a;
1305
- const { error, currentToolCall, toolContent, options, ctrl, flushText } = params;
1306
- const endTag = `</${currentToolCall.name}>`;
1307
- const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1308
- let message = "Could not process streaming XML tool call; emitting original text.";
1309
- if (error instanceof RXMLDuplicateStringTagError) {
1310
- message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1311
- } else if (error instanceof RXMLCoercionError) {
1312
- message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1313
- } else if (error instanceof RXMLParseError) {
1314
- message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1199
+ let current = ctx;
1200
+ let reparseCount = 0;
1201
+ const maxReparses = (_a = options.maxReparses) != null ? _a : 2;
1202
+ for (const heuristic of heuristics) {
1203
+ if (!heuristic.applies(current)) {
1204
+ continue;
1205
+ }
1206
+ const result = heuristic.run(current);
1207
+ current = applyRawSegmentUpdate(current, result);
1208
+ current = applyParsedUpdate(current, result);
1209
+ current = applyWarningsUpdate(current, result);
1210
+ const reparseResult = attemptReparse(
1211
+ current,
1212
+ result,
1213
+ reparseCount,
1214
+ maxReparses,
1215
+ options.parse
1216
+ );
1217
+ current = reparseResult.state;
1218
+ reparseCount = reparseResult.newCount;
1219
+ if (result.stop) {
1220
+ break;
1221
+ }
1315
1222
  }
1316
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1317
- toolCall: originalCallText,
1318
- toolName: currentToolCall.name,
1319
- error
1320
- });
1321
- flushText(ctrl, originalCallText);
1223
+ return current;
1322
1224
  }
1323
- function findEarliestToolTag(buffer, toolNames) {
1324
- let earliestStartTagIndex = -1;
1325
- let earliestToolName = "";
1326
- if (toolNames.length > 0) {
1327
- for (const name of toolNames) {
1328
- const startTag = `<${name}>`;
1329
- const index = buffer.indexOf(startTag);
1330
- if (index !== -1 && (earliestStartTagIndex === -1 || index < earliestStartTagIndex)) {
1331
- earliestStartTagIndex = index;
1332
- earliestToolName = name;
1333
- }
1225
+ function applyHeuristicPipeline(ctx, config, options) {
1226
+ let current = ctx;
1227
+ if (config.preParse && config.preParse.length > 0) {
1228
+ current = executePhase(current, config.preParse, options);
1229
+ }
1230
+ if (current.parsed === null && current.errors.length === 0) {
1231
+ try {
1232
+ const parsed = options.parse(current.rawSegment, current.schema);
1233
+ current = { ...current, parsed, errors: [] };
1234
+ } catch (error) {
1235
+ current = { ...current, errors: [error] };
1334
1236
  }
1335
1237
  }
1336
- return { index: earliestStartTagIndex, name: earliestToolName };
1238
+ if (current.errors.length > 0 && config.fallbackReparse && config.fallbackReparse.length > 0) {
1239
+ current = executePhase(current, config.fallbackReparse, options);
1240
+ }
1241
+ if (current.parsed !== null && config.postParse && config.postParse.length > 0) {
1242
+ current = executePhase(current, config.postParse, options);
1243
+ }
1244
+ return current;
1337
1245
  }
1338
- function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1339
- const tail = Math.max(0, maxStartTagLen - 1);
1340
- const safeLen = Math.max(0, buffer.length - tail);
1341
- if (safeLen > 0) {
1342
- const textToFlush = buffer.slice(0, safeLen);
1343
- flushText(controller, textToFlush);
1344
- return { buffer: buffer.slice(safeLen), shouldContinue: true };
1246
+ function createIntermediateCall(toolName, rawSegment, schema) {
1247
+ return {
1248
+ toolName,
1249
+ schema,
1250
+ rawSegment,
1251
+ parsed: null,
1252
+ errors: [],
1253
+ meta: { originalContent: rawSegment }
1254
+ };
1255
+ }
1256
+ function mergePipelineConfigs(...configs) {
1257
+ var _a, _b, _c;
1258
+ const result = {
1259
+ preParse: [],
1260
+ fallbackReparse: [],
1261
+ postParse: []
1262
+ };
1263
+ for (const config of configs) {
1264
+ if (config.preParse) {
1265
+ result.preParse = [...(_a = result.preParse) != null ? _a : [], ...config.preParse];
1266
+ }
1267
+ if (config.fallbackReparse) {
1268
+ result.fallbackReparse = [
1269
+ ...(_b = result.fallbackReparse) != null ? _b : [],
1270
+ ...config.fallbackReparse
1271
+ ];
1272
+ }
1273
+ if (config.postParse) {
1274
+ result.postParse = [...(_c = result.postParse) != null ? _c : [], ...config.postParse];
1275
+ }
1345
1276
  }
1346
- return { buffer, shouldContinue: false };
1277
+ return result;
1347
1278
  }
1348
- function processToolCallInBuffer(params) {
1349
- const {
1279
+
1280
+ // src/heuristics/xml-defaults.ts
1281
+ import { parse as parse2, unwrapJsonSchema } from "@ai-sdk-tool/rxml";
1282
+ var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1283
+ var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1284
+ var STATUS_TO_STEP_BOUNDARY_RE = /<\/status>\s*<step>/g;
1285
+ var WHITESPACE_REGEX2 = /\s/;
1286
+ var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
1287
+ var NAME_START_CHAR_RE = /[A-Za-z_:]/;
1288
+ var STEP_TAG_RE = /<step>([\s\S]*?)<\/step>/i;
1289
+ var STATUS_TAG_RE = /<status>([\s\S]*?)<\/status>/i;
1290
+ var normalizeCloseTagsHeuristic = {
1291
+ id: "normalize-close-tags",
1292
+ phase: "pre-parse",
1293
+ applies: () => true,
1294
+ run: (ctx) => {
1295
+ const normalized = ctx.rawSegment.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1296
+ if (normalized !== ctx.rawSegment) {
1297
+ return { rawSegment: normalized };
1298
+ }
1299
+ return {};
1300
+ }
1301
+ };
1302
+ var escapeInvalidLtHeuristic = {
1303
+ id: "escape-invalid-lt",
1304
+ phase: "pre-parse",
1305
+ applies: () => true,
1306
+ run: (ctx) => {
1307
+ const escaped = escapeInvalidLt(ctx.rawSegment);
1308
+ if (escaped !== ctx.rawSegment) {
1309
+ return { rawSegment: escaped };
1310
+ }
1311
+ return {};
1312
+ }
1313
+ };
1314
+ var balanceTagsHeuristic = {
1315
+ id: "balance-tags",
1316
+ phase: "fallback-reparse",
1317
+ applies: (ctx) => {
1318
+ var _a;
1319
+ const original = ((_a = ctx.meta) == null ? void 0 : _a.originalContent) || ctx.rawSegment;
1320
+ const normalized = original.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1321
+ const balanced = balanceTags(original);
1322
+ const hasMalformedClose = MALFORMED_CLOSE_RE.test(original);
1323
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1324
+ return false;
1325
+ }
1326
+ return balanced !== normalized;
1327
+ },
1328
+ run: (ctx) => {
1329
+ var _a;
1330
+ const original = ((_a = ctx.meta) == null ? void 0 : _a.originalContent) || ctx.rawSegment;
1331
+ const balanced = balanceTags(original);
1332
+ const escaped = escapeInvalidLt(balanced);
1333
+ return { rawSegment: escaped, reparse: true };
1334
+ }
1335
+ };
1336
+ var dedupeShellStringTagsHeuristic = {
1337
+ id: "dedupe-shell-string-tags",
1338
+ phase: "fallback-reparse",
1339
+ applies: (ctx) => shouldDeduplicateStringTags(ctx.schema),
1340
+ run: (ctx) => {
1341
+ const names = getStringPropertyNames(ctx.schema);
1342
+ let deduped = ctx.rawSegment;
1343
+ for (const key of names) {
1344
+ deduped = dedupeSingleTag(deduped, key);
1345
+ }
1346
+ if (deduped !== ctx.rawSegment) {
1347
+ return { rawSegment: deduped, reparse: true };
1348
+ }
1349
+ return {};
1350
+ }
1351
+ };
1352
+ var repairAgainstSchemaHeuristic = {
1353
+ id: "repair-against-schema",
1354
+ phase: "post-parse",
1355
+ applies: (ctx) => ctx.parsed !== null && typeof ctx.parsed === "object",
1356
+ run: (ctx) => {
1357
+ const repaired = repairParsedAgainstSchema(ctx.parsed, ctx.schema);
1358
+ if (repaired !== ctx.parsed) {
1359
+ return { parsed: repaired };
1360
+ }
1361
+ return {};
1362
+ }
1363
+ };
1364
+ var defaultPipelineConfig = {
1365
+ preParse: [normalizeCloseTagsHeuristic, escapeInvalidLtHeuristic],
1366
+ fallbackReparse: [balanceTagsHeuristic, dedupeShellStringTagsHeuristic],
1367
+ postParse: [repairAgainstSchemaHeuristic]
1368
+ };
1369
+ var INDEX_TAG_RE = /^<(\d+)(?:>|\/?>)/;
1370
+ function isIndexTagAt(xml, pos) {
1371
+ const remaining = xml.slice(pos);
1372
+ return INDEX_TAG_RE.test(remaining);
1373
+ }
1374
+ function escapeInvalidLt(xml) {
1375
+ const len = xml.length;
1376
+ let out = "";
1377
+ for (let i = 0; i < len; i += 1) {
1378
+ const ch = xml[i];
1379
+ if (ch === "<") {
1380
+ const next = i + 1 < len ? xml[i + 1] : "";
1381
+ const isValidStart = NAME_START_CHAR_RE.test(next) || next === "/" || next === "!" || next === "?";
1382
+ const isIndexTag = !isValidStart && isIndexTagAt(xml, i);
1383
+ if (!(isValidStart || isIndexTag)) {
1384
+ out += "&lt;";
1385
+ continue;
1386
+ }
1387
+ }
1388
+ out += ch;
1389
+ }
1390
+ return out;
1391
+ }
1392
+ function balanceTags(xml) {
1393
+ const src = xml.replace(MALFORMED_CLOSE_RE_G, "</$1>").replace(STATUS_TO_STEP_BOUNDARY_RE, "</status></step><step>");
1394
+ let i = 0;
1395
+ const len = src.length;
1396
+ const out = [];
1397
+ const stack = [];
1398
+ while (i < len) {
1399
+ const lt = src.indexOf("<", i);
1400
+ if (lt === -1) {
1401
+ out.push(src.slice(i));
1402
+ break;
1403
+ }
1404
+ out.push(src.slice(i, lt));
1405
+ if (lt + 1 >= len) {
1406
+ break;
1407
+ }
1408
+ const next = src[lt + 1];
1409
+ if (next === "!" || next === "?") {
1410
+ i = handleSpecialTagSegment(src, lt, out);
1411
+ continue;
1412
+ }
1413
+ if (next === "/") {
1414
+ i = handleClosingTagSegment(src, lt, out, stack);
1415
+ continue;
1416
+ }
1417
+ i = handleOpeningTagSegment(src, lt, out, stack);
1418
+ }
1419
+ for (let k = stack.length - 1; k >= 0; k -= 1) {
1420
+ out.push(`</${stack[k]}>`);
1421
+ }
1422
+ return out.join("");
1423
+ }
1424
+ function skipWs(s, p, len) {
1425
+ let idx = p;
1426
+ while (idx < len && WHITESPACE_REGEX2.test(s[idx])) {
1427
+ idx += 1;
1428
+ }
1429
+ return idx;
1430
+ }
1431
+ function parseTagNameAt(s, p, len) {
1432
+ let idx = p;
1433
+ const start = idx;
1434
+ while (idx < len && NAME_CHAR_RE.test(s[idx])) {
1435
+ idx += 1;
1436
+ }
1437
+ return { name: s.slice(start, idx), pos: idx };
1438
+ }
1439
+ function handleSpecialTagSegment(src, lt, out) {
1440
+ const gt = src.indexOf(">", lt + 1);
1441
+ if (gt === -1) {
1442
+ out.push(src.slice(lt));
1443
+ return src.length;
1444
+ }
1445
+ out.push(src.slice(lt, gt + 1));
1446
+ return gt + 1;
1447
+ }
1448
+ function handleClosingTagSegment(src, lt, out, stack) {
1449
+ const len = src.length;
1450
+ let p = skipWs(src, lt + 2, len);
1451
+ const { name, pos } = parseTagNameAt(src, p, len);
1452
+ p = pos;
1453
+ const gt = src.indexOf(">", p);
1454
+ const closingText = gt === -1 ? src.slice(lt) : src.slice(lt, gt + 1);
1455
+ const idx = stack.lastIndexOf(name);
1456
+ if (idx !== -1) {
1457
+ for (let k = stack.length - 1; k > idx; k -= 1) {
1458
+ out.push(`</${stack[k]}>`);
1459
+ stack.pop();
1460
+ }
1461
+ out.push(closingText);
1462
+ stack.pop();
1463
+ }
1464
+ return gt === -1 ? len : gt + 1;
1465
+ }
1466
+ function handleOpeningTagSegment(src, lt, out, stack) {
1467
+ const len = src.length;
1468
+ let p = skipWs(src, lt + 1, len);
1469
+ const nameStart = p;
1470
+ const parsed = parseTagNameAt(src, p, len);
1471
+ p = parsed.pos;
1472
+ const name = src.slice(nameStart, p);
1473
+ const q = src.indexOf(">", p);
1474
+ if (q === -1) {
1475
+ out.push(src.slice(lt));
1476
+ return len;
1477
+ }
1478
+ let r = q - 1;
1479
+ while (r >= nameStart && WHITESPACE_REGEX2.test(src[r])) {
1480
+ r -= 1;
1481
+ }
1482
+ const selfClosing = src[r] === "/";
1483
+ out.push(src.slice(lt, q + 1));
1484
+ if (!selfClosing && name) {
1485
+ stack.push(name);
1486
+ }
1487
+ return q + 1;
1488
+ }
1489
+ function shouldDeduplicateStringTags(schema) {
1490
+ const unwrapped = unwrapJsonSchema(schema);
1491
+ if (!unwrapped || typeof unwrapped !== "object") {
1492
+ return false;
1493
+ }
1494
+ const props = unwrapped.properties;
1495
+ if (!props) {
1496
+ return false;
1497
+ }
1498
+ const commandRaw = props.command;
1499
+ if (!commandRaw) {
1500
+ return false;
1501
+ }
1502
+ const command = unwrapJsonSchema(commandRaw);
1503
+ return (command == null ? void 0 : command.type) === "array";
1504
+ }
1505
+ function getStringPropertyNames(schema) {
1506
+ const unwrapped = unwrapJsonSchema(schema);
1507
+ if (!unwrapped || typeof unwrapped !== "object") {
1508
+ return [];
1509
+ }
1510
+ const props = unwrapped.properties;
1511
+ if (!props) {
1512
+ return [];
1513
+ }
1514
+ const names = [];
1515
+ for (const key of Object.keys(props)) {
1516
+ const prop = unwrapJsonSchema(
1517
+ props[key]
1518
+ );
1519
+ const type = prop.type;
1520
+ if (type === "string") {
1521
+ names.push(key);
1522
+ }
1523
+ }
1524
+ return names;
1525
+ }
1526
+ function escapeRegExp2(s) {
1527
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1528
+ }
1529
+ function dedupeSingleTag(xml, key) {
1530
+ var _a, _b;
1531
+ const escaped = escapeRegExp2(key);
1532
+ const re = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
1533
+ const matches = Array.from(xml.matchAll(re));
1534
+ if (matches.length <= 1) {
1535
+ return xml;
1536
+ }
1537
+ const last = matches.at(-1);
1538
+ let result = "";
1539
+ let cursor = 0;
1540
+ for (const m of matches) {
1541
+ const idx = (_a = m.index) != null ? _a : 0;
1542
+ result += xml.slice(cursor, idx);
1543
+ if (last && idx === ((_b = last.index) != null ? _b : -1)) {
1544
+ result += m[0];
1545
+ }
1546
+ cursor = idx + m[0].length;
1547
+ }
1548
+ result += xml.slice(cursor);
1549
+ return result;
1550
+ }
1551
+ function repairParsedAgainstSchema(input, schema) {
1552
+ if (!input || typeof input !== "object") {
1553
+ return input;
1554
+ }
1555
+ const unwrapped = unwrapJsonSchema(schema);
1556
+ if (!unwrapped || typeof unwrapped !== "object") {
1557
+ return input;
1558
+ }
1559
+ const properties = unwrapped.properties;
1560
+ if (!properties) {
1561
+ return input;
1562
+ }
1563
+ applySchemaProps(input, properties);
1564
+ return input;
1565
+ }
1566
+ function applySchemaProps(obj, properties) {
1567
+ for (const key of Object.keys(obj)) {
1568
+ const propSchema = properties[key];
1569
+ if (!propSchema) {
1570
+ continue;
1571
+ }
1572
+ const prop = unwrapJsonSchema(propSchema);
1573
+ const propType = prop.type;
1574
+ if (propType === "array" && prop.items) {
1575
+ const itemSchemaRaw = prop.items;
1576
+ const itemSchema = unwrapJsonSchema(itemSchemaRaw);
1577
+ obj[key] = coerceArrayItems(obj[key], itemSchema);
1578
+ continue;
1579
+ }
1580
+ if (propType === "object") {
1581
+ const val = obj[key];
1582
+ if (val && typeof val === "object") {
1583
+ obj[key] = repairParsedAgainstSchema(val, prop);
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+ function coerceArrayItems(val, itemSchema) {
1589
+ if (!Array.isArray(val)) {
1590
+ return val;
1591
+ }
1592
+ return val.map((v) => coerceArrayItem(v, itemSchema));
1593
+ }
1594
+ function coerceArrayItem(v, itemSchema) {
1595
+ const itemType = itemSchema == null ? void 0 : itemSchema.type;
1596
+ if (typeof v === "string" && itemType === "object") {
1597
+ const parsed = tryParseStringToSchemaObject(v, itemSchema);
1598
+ if (parsed !== null) {
1599
+ return parsed;
1600
+ }
1601
+ const fallback = extractStepStatusFromString(
1602
+ v.replace(MALFORMED_CLOSE_RE_G, "</$1>")
1603
+ );
1604
+ if (fallback) {
1605
+ return fallback;
1606
+ }
1607
+ return v;
1608
+ }
1609
+ if (v && typeof v === "object" && itemType === "object") {
1610
+ return repairParsedAgainstSchema(v, itemSchema);
1611
+ }
1612
+ return v;
1613
+ }
1614
+ function tryParseStringToSchemaObject(xml, itemSchema) {
1615
+ try {
1616
+ const normalized = xml.replace(MALFORMED_CLOSE_RE_G, "</$1>");
1617
+ const fixed = parse2(normalized, itemSchema, { noChildNodes: [] });
1618
+ return typeof fixed === "string" ? null : fixed;
1619
+ } catch (e) {
1620
+ return null;
1621
+ }
1622
+ }
1623
+ function extractStepStatusFromString(normXml) {
1624
+ const stepMatch = normXml.match(STEP_TAG_RE);
1625
+ const statusMatch = normXml.match(STATUS_TAG_RE);
1626
+ if (stepMatch && statusMatch) {
1627
+ return { step: stepMatch[1], status: statusMatch[1] };
1628
+ }
1629
+ return null;
1630
+ }
1631
+
1632
+ // src/utils/type-guards.ts
1633
+ function isToolCallContent(content) {
1634
+ return content.type === "tool-call" && typeof content.toolName === "string" && // input may be a JSON string or an already-parsed object depending on provider/runtime
1635
+ (typeof content.input === "string" || typeof content.input === "object");
1636
+ }
1637
+ function isToolResultPart(content) {
1638
+ const c = content;
1639
+ return !!c && c.type === "tool-result" && typeof c.toolName === "string" && typeof c.toolCallId === "string" && "output" in c;
1640
+ }
1641
+ function hasInputProperty(obj) {
1642
+ return typeof obj === "object" && obj !== null && "input" in obj;
1643
+ }
1644
+
1645
+ // src/protocols/morph-xml-protocol.ts
1646
+ var defaultPipelineConfig2 = defaultPipelineConfig;
1647
+ var applyHeuristicPipeline2 = applyHeuristicPipeline;
1648
+ var createIntermediateCall2 = createIntermediateCall;
1649
+ var mergePipelineConfigs2 = mergePipelineConfigs;
1650
+ var WHITESPACE_REGEX3 = /\s/;
1651
+ var MALFORMED_CLOSE_RE2 = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
1652
+ var MALFORMED_CLOSE_RE_G2 = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
1653
+ var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
1654
+ function normalizeCloseTags(xml) {
1655
+ return xml.replace(MALFORMED_CLOSE_RE_G2, "</$1>");
1656
+ }
1657
+ function tryParseSecondaryXml(content, toolSchema, options) {
1658
+ const normalized = normalizeCloseTags(content);
1659
+ const balanced = balanceTags(content);
1660
+ const hasMalformedClose = MALFORMED_CLOSE_RE2.test(content);
1661
+ if (!hasMalformedClose && balanced.length > normalized.length) {
1662
+ return null;
1663
+ }
1664
+ try {
1665
+ let parsed = parse3(balanced, toolSchema, {
1666
+ onError: options == null ? void 0 : options.onError,
1667
+ noChildNodes: []
1668
+ });
1669
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1670
+ return parsed;
1671
+ } catch (e) {
1672
+ if (shouldDeduplicateStringTags(toolSchema)) {
1673
+ const deduped = dedupeStringTagsAgainstSchema(balanced, toolSchema);
1674
+ if (deduped !== balanced) {
1675
+ try {
1676
+ let reparsed = parse3(deduped, toolSchema, {
1677
+ onError: options == null ? void 0 : options.onError,
1678
+ noChildNodes: []
1679
+ });
1680
+ reparsed = repairParsedAgainstSchema(reparsed, toolSchema);
1681
+ return reparsed;
1682
+ } catch (e2) {
1683
+ return null;
1684
+ }
1685
+ }
1686
+ }
1687
+ return null;
1688
+ }
1689
+ }
1690
+ function dedupeStringTagsAgainstSchema(xml, schema) {
1691
+ const names = getStringPropertyNames(schema);
1692
+ let out = xml;
1693
+ for (const key of names) {
1694
+ out = dedupeSingleTag(out, key);
1695
+ }
1696
+ return out;
1697
+ }
1698
+ function processTextBeforeToolCall(text, currentIndex, toolCallStartIndex, processedElements) {
1699
+ if (toolCallStartIndex > currentIndex) {
1700
+ const textSegment = text.substring(currentIndex, toolCallStartIndex);
1701
+ if (textSegment.trim()) {
1702
+ processedElements.push({ type: "text", text: textSegment });
1703
+ }
1704
+ }
1705
+ return currentIndex;
1706
+ }
1707
+ function processToolCallWithPipeline(params) {
1708
+ var _a;
1709
+ const {
1710
+ toolCall,
1711
+ tools,
1712
+ options,
1713
+ text,
1714
+ processedElements,
1715
+ pipelineConfig = defaultPipelineConfig2,
1716
+ maxReparses
1717
+ } = params;
1718
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1719
+ const ctx = createIntermediateCall2(
1720
+ toolCall.toolName,
1721
+ toolCall.content,
1722
+ toolSchema
1723
+ );
1724
+ const result = applyHeuristicPipeline2(ctx, pipelineConfig, {
1725
+ parse: (xml, schema) => parse3(xml, schema, { onError: options == null ? void 0 : options.onError, noChildNodes: [] }),
1726
+ onError: options == null ? void 0 : options.onError,
1727
+ maxReparses
1728
+ });
1729
+ if (result.parsed !== null) {
1730
+ processedElements.push({
1731
+ type: "tool-call",
1732
+ toolCallId: generateId2(),
1733
+ toolName: toolCall.toolName,
1734
+ input: JSON.stringify(result.parsed)
1735
+ });
1736
+ } else {
1737
+ const originalCallText = text.substring(
1738
+ toolCall.startIndex,
1739
+ toolCall.endIndex
1740
+ );
1741
+ const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1742
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1743
+ toolCall: originalCallText,
1744
+ toolName: toolCall.toolName,
1745
+ error: result.errors[0]
1746
+ });
1747
+ processedElements.push({ type: "text", text: originalCallText });
1748
+ }
1749
+ }
1750
+ function processToolCall(params) {
1751
+ var _a;
1752
+ const { toolCall, tools, options, text, processedElements } = params;
1753
+ const toolSchema = getToolSchema(tools, toolCall.toolName);
1754
+ try {
1755
+ const primary = escapeInvalidLt(normalizeCloseTags(toolCall.content));
1756
+ let parsed = parse3(primary, toolSchema, {
1757
+ onError: options == null ? void 0 : options.onError,
1758
+ noChildNodes: []
1759
+ });
1760
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1761
+ processedElements.push({
1762
+ type: "tool-call",
1763
+ toolCallId: generateId2(),
1764
+ toolName: toolCall.toolName,
1765
+ input: JSON.stringify(parsed)
1766
+ });
1767
+ } catch (error) {
1768
+ const reparsed = tryParseSecondaryXml(
1769
+ toolCall.content,
1770
+ toolSchema,
1771
+ options
1772
+ );
1773
+ if (reparsed !== null) {
1774
+ processedElements.push({
1775
+ type: "tool-call",
1776
+ toolCallId: generateId2(),
1777
+ toolName: toolCall.toolName,
1778
+ input: JSON.stringify(reparsed)
1779
+ });
1780
+ return;
1781
+ }
1782
+ const originalCallText = text.substring(
1783
+ toolCall.startIndex,
1784
+ toolCall.endIndex
1785
+ );
1786
+ const message = `Could not process XML tool call, keeping original text: ${originalCallText}`;
1787
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1788
+ toolCall: originalCallText,
1789
+ toolName: toolCall.toolName,
1790
+ error
1791
+ });
1792
+ processedElements.push({ type: "text", text: originalCallText });
1793
+ }
1794
+ }
1795
+ function addRemainingText(text, currentIndex, processedElements) {
1796
+ if (currentIndex < text.length) {
1797
+ const remainingText = text.substring(currentIndex);
1798
+ if (remainingText.trim()) {
1799
+ processedElements.push({ type: "text", text: remainingText });
1800
+ }
1801
+ }
1802
+ }
1803
+ function handleStreamingToolCallEndWithPipeline(params) {
1804
+ var _a;
1805
+ const {
1806
+ toolContent,
1807
+ currentToolCall,
1808
+ tools,
1809
+ options,
1810
+ ctrl,
1811
+ flushText,
1812
+ pipelineConfig = defaultPipelineConfig2,
1813
+ maxReparses
1814
+ } = params;
1815
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1816
+ const ctx = createIntermediateCall2(
1817
+ currentToolCall.name,
1818
+ toolContent,
1819
+ toolSchema
1820
+ );
1821
+ const result = applyHeuristicPipeline2(ctx, pipelineConfig, {
1822
+ parse: (xml, schema) => parse3(xml, schema, { onError: options == null ? void 0 : options.onError, noChildNodes: [] }),
1823
+ onError: options == null ? void 0 : options.onError,
1824
+ maxReparses
1825
+ });
1826
+ flushText(ctrl);
1827
+ if (result.parsed !== null) {
1828
+ ctrl.enqueue({
1829
+ type: "tool-call",
1830
+ toolCallId: generateId2(),
1831
+ toolName: currentToolCall.name,
1832
+ input: JSON.stringify(result.parsed)
1833
+ });
1834
+ } else {
1835
+ const endTag = `</${currentToolCall.name}>`;
1836
+ const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1837
+ const error = result.errors[0];
1838
+ let message = "Could not process streaming XML tool call; emitting original text.";
1839
+ if (error instanceof RXMLDuplicateStringTagError) {
1840
+ message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1841
+ } else if (error instanceof RXMLCoercionError) {
1842
+ message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1843
+ } else if (error instanceof RXMLParseError) {
1844
+ message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1845
+ }
1846
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1847
+ toolCall: originalCallText,
1848
+ toolName: currentToolCall.name,
1849
+ error
1850
+ });
1851
+ flushText(ctrl, originalCallText);
1852
+ }
1853
+ }
1854
+ function handleStreamingToolCallEnd(params) {
1855
+ const { toolContent, currentToolCall, tools, options, ctrl, flushText } = params;
1856
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
1857
+ try {
1858
+ const primary = escapeInvalidLt(normalizeCloseTags(toolContent));
1859
+ let parsed = parse3(primary, toolSchema, {
1860
+ onError: options == null ? void 0 : options.onError,
1861
+ noChildNodes: []
1862
+ });
1863
+ parsed = repairParsedAgainstSchema(parsed, toolSchema);
1864
+ flushText(ctrl);
1865
+ ctrl.enqueue({
1866
+ type: "tool-call",
1867
+ toolCallId: generateId2(),
1868
+ toolName: currentToolCall.name,
1869
+ input: JSON.stringify(parsed)
1870
+ });
1871
+ } catch (error) {
1872
+ const parsed = tryParseSecondaryXml(toolContent, toolSchema, options);
1873
+ if (parsed !== null) {
1874
+ flushText(ctrl);
1875
+ ctrl.enqueue({
1876
+ type: "tool-call",
1877
+ toolCallId: generateId2(),
1878
+ toolName: currentToolCall.name,
1879
+ input: JSON.stringify(parsed)
1880
+ });
1881
+ return;
1882
+ }
1883
+ handleStreamingToolCallError({
1884
+ error,
1885
+ currentToolCall,
1886
+ toolContent,
1887
+ options,
1888
+ ctrl,
1889
+ flushText
1890
+ });
1891
+ }
1892
+ }
1893
+ function handleStreamingToolCallError(params) {
1894
+ var _a;
1895
+ const { error, currentToolCall, toolContent, options, ctrl, flushText } = params;
1896
+ const endTag = `</${currentToolCall.name}>`;
1897
+ const originalCallText = `<${currentToolCall.name}>${toolContent}${endTag}`;
1898
+ let message = "Could not process streaming XML tool call; emitting original text.";
1899
+ if (error instanceof RXMLDuplicateStringTagError) {
1900
+ message = `Duplicate string tags detected in streaming tool call '${currentToolCall.name}'; emitting original text.`;
1901
+ } else if (error instanceof RXMLCoercionError) {
1902
+ message = `Failed to coerce arguments for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1903
+ } else if (error instanceof RXMLParseError) {
1904
+ message = `Failed to parse XML for streaming tool call '${currentToolCall.name}'; emitting original text.`;
1905
+ }
1906
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, message, {
1907
+ toolCall: originalCallText,
1908
+ toolName: currentToolCall.name,
1909
+ error
1910
+ });
1911
+ flushText(ctrl, originalCallText);
1912
+ }
1913
+ function findEarliestToolTag(buffer, toolNames) {
1914
+ let bestIndex = -1;
1915
+ let bestName = "";
1916
+ let bestSelfClosing = false;
1917
+ if (toolNames.length > 0) {
1918
+ for (const name of toolNames) {
1919
+ const openTag = `<${name}>`;
1920
+ const selfTag = `<${name}/>`;
1921
+ const idxOpen = buffer.indexOf(openTag);
1922
+ const idxSelf = buffer.indexOf(selfTag);
1923
+ if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
1924
+ bestIndex = idxOpen;
1925
+ bestName = name;
1926
+ bestSelfClosing = false;
1927
+ }
1928
+ if (idxSelf !== -1 && (bestIndex === -1 || idxSelf < bestIndex)) {
1929
+ bestIndex = idxSelf;
1930
+ bestName = name;
1931
+ bestSelfClosing = true;
1932
+ }
1933
+ }
1934
+ }
1935
+ return { index: bestIndex, name: bestName, selfClosing: bestSelfClosing };
1936
+ }
1937
+ function handleNoToolTagInBuffer(buffer, maxStartTagLen, controller, flushText) {
1938
+ const tail = Math.max(0, maxStartTagLen - 1);
1939
+ const safeLen = Math.max(0, buffer.length - tail);
1940
+ if (safeLen > 0) {
1941
+ const textToFlush = buffer.slice(0, safeLen);
1942
+ flushText(controller, textToFlush);
1943
+ return { buffer: buffer.slice(safeLen), shouldContinue: true };
1944
+ }
1945
+ return { buffer, shouldContinue: false };
1946
+ }
1947
+ function processToolCallInBuffer(params) {
1948
+ const {
1350
1949
  buffer,
1351
1950
  currentToolCall,
1352
1951
  tools,
1353
1952
  options,
1354
1953
  controller,
1355
1954
  flushText,
1356
- setBuffer
1955
+ setBuffer,
1956
+ pipelineConfig,
1957
+ maxReparses
1357
1958
  } = params;
1358
1959
  const endTag = `</${currentToolCall.name}>`;
1359
- const endTagIndex = buffer.indexOf(endTag);
1960
+ const normalized = normalizeCloseTags(buffer);
1961
+ const effectiveBuffer = normalized;
1962
+ const endTagIndex = effectiveBuffer.indexOf(endTag);
1360
1963
  if (endTagIndex !== -1) {
1361
- const toolContent = buffer.substring(0, endTagIndex);
1362
- const newBuffer = buffer.substring(endTagIndex + endTag.length);
1964
+ const toolContent = effectiveBuffer.substring(0, endTagIndex);
1965
+ const newBuffer = effectiveBuffer.substring(endTagIndex + endTag.length);
1363
1966
  setBuffer("");
1364
- handleStreamingToolCallEnd({
1365
- toolContent,
1366
- currentToolCall,
1367
- tools,
1368
- options,
1369
- ctrl: controller,
1370
- flushText
1371
- });
1967
+ if (pipelineConfig) {
1968
+ handleStreamingToolCallEndWithPipeline({
1969
+ toolContent,
1970
+ currentToolCall,
1971
+ tools,
1972
+ options,
1973
+ ctrl: controller,
1974
+ flushText,
1975
+ pipelineConfig,
1976
+ maxReparses
1977
+ });
1978
+ } else {
1979
+ handleStreamingToolCallEnd({
1980
+ toolContent,
1981
+ currentToolCall,
1982
+ tools,
1983
+ options,
1984
+ ctrl: controller,
1985
+ flushText
1986
+ });
1987
+ }
1372
1988
  setBuffer(newBuffer);
1373
1989
  return { buffer: newBuffer, currentToolCall: null, shouldBreak: false };
1374
1990
  }
1375
- return { buffer, currentToolCall, shouldBreak: true };
1991
+ return { buffer: effectiveBuffer, currentToolCall, shouldBreak: true };
1376
1992
  }
1377
1993
  function processNoToolCallInBuffer(params) {
1378
- const { buffer, toolNames, maxStartTagLen, controller, flushText } = params;
1379
- const { index: earliestStartTagIndex, name: earliestToolName } = findEarliestToolTag(buffer, toolNames);
1994
+ const {
1995
+ buffer,
1996
+ toolNames,
1997
+ maxStartTagLen,
1998
+ controller,
1999
+ flushText,
2000
+ tools,
2001
+ options,
2002
+ pipelineConfig,
2003
+ maxReparses
2004
+ } = params;
2005
+ const {
2006
+ index: earliestStartTagIndex,
2007
+ name: earliestToolName,
2008
+ selfClosing
2009
+ } = findEarliestToolTag(buffer, toolNames);
1380
2010
  if (earliestStartTagIndex !== -1) {
1381
2011
  const textBeforeTag = buffer.substring(0, earliestStartTagIndex);
1382
2012
  flushText(controller, textBeforeTag);
2013
+ if (selfClosing) {
2014
+ const selfTag = `<${earliestToolName}/>`;
2015
+ const newBuffer2 = buffer.substring(
2016
+ earliestStartTagIndex + selfTag.length
2017
+ );
2018
+ if (pipelineConfig) {
2019
+ handleStreamingToolCallEndWithPipeline({
2020
+ toolContent: "",
2021
+ currentToolCall: { name: earliestToolName, content: "" },
2022
+ tools,
2023
+ options,
2024
+ ctrl: controller,
2025
+ flushText,
2026
+ pipelineConfig,
2027
+ maxReparses
2028
+ });
2029
+ } else {
2030
+ handleStreamingToolCallEnd({
2031
+ toolContent: "",
2032
+ currentToolCall: { name: earliestToolName, content: "" },
2033
+ tools,
2034
+ options,
2035
+ ctrl: controller,
2036
+ flushText
2037
+ });
2038
+ }
2039
+ return {
2040
+ buffer: newBuffer2,
2041
+ currentToolCall: null,
2042
+ shouldBreak: false,
2043
+ shouldContinue: false
2044
+ };
2045
+ }
1383
2046
  const startTag = `<${earliestToolName}>`;
1384
2047
  const newBuffer = buffer.substring(earliestStartTagIndex + startTag.length);
1385
2048
  return {
@@ -1408,7 +2071,7 @@ function createFlushTextHandler(getBuffer, setBuffer, getCurrentTextId, setCurre
1408
2071
  if (content) {
1409
2072
  const currentTextId2 = getCurrentTextId();
1410
2073
  if (!currentTextId2) {
1411
- const newId = generateId3();
2074
+ const newId = generateId2();
1412
2075
  setCurrentTextId(newId);
1413
2076
  controller.enqueue({ type: "text-start", id: newId });
1414
2077
  }
@@ -1436,7 +2099,9 @@ function processBufferWithToolCall(params, controller) {
1436
2099
  setCurrentToolCall,
1437
2100
  tools,
1438
2101
  options,
1439
- flushText
2102
+ flushText,
2103
+ pipelineConfig,
2104
+ maxReparses
1440
2105
  } = params;
1441
2106
  const currentToolCall = getCurrentToolCall();
1442
2107
  if (!currentToolCall) {
@@ -1449,7 +2114,9 @@ function processBufferWithToolCall(params, controller) {
1449
2114
  options,
1450
2115
  controller,
1451
2116
  flushText,
1452
- setBuffer
2117
+ setBuffer,
2118
+ pipelineConfig,
2119
+ maxReparses
1453
2120
  });
1454
2121
  setBuffer(result.buffer);
1455
2122
  setCurrentToolCall(result.currentToolCall);
@@ -1460,16 +2127,24 @@ function processBufferWithoutToolCall(params, controller) {
1460
2127
  getBuffer,
1461
2128
  setBuffer,
1462
2129
  setCurrentToolCall,
2130
+ tools,
2131
+ options,
1463
2132
  toolNames,
1464
2133
  maxStartTagLen,
1465
- flushText
2134
+ flushText,
2135
+ pipelineConfig,
2136
+ maxReparses
1466
2137
  } = params;
1467
2138
  const result = processNoToolCallInBuffer({
1468
2139
  buffer: getBuffer(),
1469
2140
  toolNames,
1470
2141
  maxStartTagLen,
1471
2142
  controller,
1472
- flushText
2143
+ flushText,
2144
+ tools,
2145
+ options,
2146
+ pipelineConfig,
2147
+ maxReparses
1473
2148
  });
1474
2149
  setBuffer(result.buffer);
1475
2150
  setCurrentToolCall(result.currentToolCall);
@@ -1505,180 +2180,411 @@ function createProcessBufferHandler(params) {
1505
2180
  processBufferLoop(params, controller);
1506
2181
  };
1507
2182
  }
1508
- var morphXmlProtocol = () => ({
1509
- formatTools({ tools, toolSystemPromptTemplate }) {
1510
- const toolsForPrompt = (tools || []).map((tool) => ({
1511
- name: tool.name,
1512
- description: tool.description,
1513
- parameters: unwrapJsonSchema(tool.inputSchema)
1514
- }));
1515
- return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
1516
- },
1517
- formatToolCall(toolCall) {
1518
- let args = {};
1519
- const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
1520
- if (typeof inputValue === "string") {
1521
- try {
1522
- args = JSON.parse(inputValue);
1523
- } catch (e) {
1524
- args = inputValue;
1525
- }
1526
- } else {
1527
- args = inputValue;
1528
- }
1529
- return stringify2(toolCall.toolName, args, {
1530
- suppressEmptyNode: false,
1531
- format: false
1532
- });
1533
- },
1534
- formatToolResponse(toolResult) {
1535
- return stringify2("tool_response", {
1536
- tool_name: toolResult.toolName,
1537
- result: toolResult.output
1538
- });
1539
- },
1540
- parseGeneratedText({ text, tools, options }) {
1541
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1542
- if (toolNames.length === 0) {
1543
- return [{ type: "text", text }];
1544
- }
1545
- const processedElements = [];
1546
- let currentIndex = 0;
1547
- const toolCalls = findToolCalls(text, toolNames);
1548
- for (const toolCall of toolCalls) {
1549
- currentIndex = processTextBeforeToolCall(
1550
- text,
1551
- currentIndex,
1552
- toolCall.startIndex,
1553
- processedElements
1554
- );
1555
- processToolCall({ toolCall, tools, options, text, processedElements });
1556
- currentIndex = toolCall.endIndex;
1557
- }
1558
- addRemainingText(text, currentIndex, processedElements);
1559
- return processedElements;
1560
- },
1561
- createStreamParser({ tools, options }) {
1562
- const toolNames = tools.map((t) => t.name).filter((name) => name != null);
1563
- const maxStartTagLen = toolNames.length ? Math.max(...toolNames.map((n) => `<${n}>`.length)) : 0;
1564
- let buffer = "";
1565
- let currentToolCall = null;
1566
- let currentTextId = null;
1567
- const flushText = createFlushTextHandler(
1568
- () => buffer,
1569
- (newBuffer) => {
1570
- buffer = newBuffer;
2183
+ function buildPipelineOptions(protocolOptions) {
2184
+ var _a, _b, _c;
2185
+ const maxReparses = protocolOptions == null ? void 0 : protocolOptions.maxReparses;
2186
+ if (protocolOptions == null ? void 0 : protocolOptions.pipeline) {
2187
+ return {
2188
+ pipelineConfig: mergePipelineConfigs2(
2189
+ defaultPipelineConfig2,
2190
+ protocolOptions.pipeline
2191
+ ),
2192
+ maxReparses
2193
+ };
2194
+ }
2195
+ if (protocolOptions == null ? void 0 : protocolOptions.heuristics) {
2196
+ return {
2197
+ pipelineConfig: {
2198
+ ...defaultPipelineConfig2,
2199
+ preParse: [
2200
+ ...(_a = defaultPipelineConfig2.preParse) != null ? _a : [],
2201
+ ...protocolOptions.heuristics.filter((h) => h.phase === "pre-parse")
2202
+ ],
2203
+ fallbackReparse: [
2204
+ ...(_b = defaultPipelineConfig2.fallbackReparse) != null ? _b : [],
2205
+ ...protocolOptions.heuristics.filter(
2206
+ (h) => h.phase === "fallback-reparse"
2207
+ )
2208
+ ],
2209
+ postParse: [
2210
+ ...(_c = defaultPipelineConfig2.postParse) != null ? _c : [],
2211
+ ...protocolOptions.heuristics.filter((h) => h.phase === "post-parse")
2212
+ ]
1571
2213
  },
1572
- () => currentTextId,
1573
- (newId) => {
1574
- currentTextId = newId;
1575
- }
1576
- );
1577
- const processChunk = (chunk, controller) => {
1578
- if (chunk.type !== "text-delta") {
1579
- if (buffer) {
1580
- flushText(controller);
2214
+ maxReparses
2215
+ };
2216
+ }
2217
+ return { pipelineConfig: void 0, maxReparses };
2218
+ }
2219
+ var morphXmlProtocol = (protocolOptions) => {
2220
+ const { pipelineConfig, maxReparses } = buildPipelineOptions(protocolOptions);
2221
+ return {
2222
+ formatTools({ tools, toolSystemPromptTemplate }) {
2223
+ const toolsForPrompt = (tools || []).map((tool) => ({
2224
+ name: tool.name,
2225
+ description: tool.description,
2226
+ parameters: unwrapJsonSchema2(tool.inputSchema)
2227
+ }));
2228
+ return toolSystemPromptTemplate(JSON.stringify(toolsForPrompt));
2229
+ },
2230
+ formatToolCall(toolCall) {
2231
+ let args = {};
2232
+ const inputValue = hasInputProperty(toolCall) ? toolCall.input : void 0;
2233
+ if (typeof inputValue === "string") {
2234
+ try {
2235
+ args = JSON.parse(inputValue);
2236
+ } catch (e) {
2237
+ args = inputValue;
1581
2238
  }
1582
- controller.enqueue(chunk);
1583
- return;
2239
+ } else {
2240
+ args = inputValue;
1584
2241
  }
1585
- buffer += chunk.delta;
1586
- processBuffer(controller);
1587
- };
1588
- const processBuffer = createProcessBufferHandler({
1589
- getBuffer: () => buffer,
1590
- setBuffer: (newBuffer) => {
1591
- buffer = newBuffer;
1592
- },
1593
- getCurrentToolCall: () => currentToolCall,
1594
- setCurrentToolCall: (newToolCall) => {
1595
- currentToolCall = newToolCall;
1596
- },
1597
- tools,
1598
- options,
1599
- toolNames,
1600
- maxStartTagLen,
1601
- flushText
1602
- });
1603
- const flushBuffer2 = (controller) => {
1604
- if (currentToolCall) {
1605
- const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
1606
- flushText(controller, unfinishedCall);
1607
- } else if (buffer) {
1608
- flushText(controller);
2242
+ return stringify2(toolCall.toolName, args, {
2243
+ suppressEmptyNode: false,
2244
+ format: false
2245
+ });
2246
+ },
2247
+ formatToolResponse(toolResult) {
2248
+ return stringify2("tool_response", {
2249
+ tool_name: toolResult.toolName,
2250
+ result: toolResult.output
2251
+ });
2252
+ },
2253
+ parseGeneratedText({ text, tools, options }) {
2254
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
2255
+ if (toolNames.length === 0) {
2256
+ return [{ type: "text", text }];
1609
2257
  }
1610
- if (currentTextId) {
1611
- controller.enqueue({ type: "text-end", id: currentTextId });
2258
+ const processedElements = [];
2259
+ let currentIndex = 0;
2260
+ const toolCallsRaw = findToolCalls(text, toolNames);
2261
+ const toolCallsNorm = collectToolCallsFromNormalizedText(text, toolNames);
2262
+ const seen = /* @__PURE__ */ new Set();
2263
+ const toolCalls = [...toolCallsRaw, ...toolCallsNorm].filter((tc) => {
2264
+ const key = `${tc.toolName}:${tc.startIndex}:${tc.endIndex}`;
2265
+ if (seen.has(key)) {
2266
+ return false;
2267
+ }
2268
+ seen.add(key);
2269
+ return true;
2270
+ }).sort((a, b) => a.startIndex - b.startIndex);
2271
+ for (const toolCall of toolCalls) {
2272
+ currentIndex = processTextBeforeToolCall(
2273
+ text,
2274
+ currentIndex,
2275
+ toolCall.startIndex,
2276
+ processedElements
2277
+ );
2278
+ if (pipelineConfig) {
2279
+ processToolCallWithPipeline({
2280
+ toolCall,
2281
+ tools,
2282
+ options,
2283
+ text,
2284
+ processedElements,
2285
+ pipelineConfig,
2286
+ maxReparses
2287
+ });
2288
+ } else {
2289
+ processToolCall({
2290
+ toolCall,
2291
+ tools,
2292
+ options,
2293
+ text,
2294
+ processedElements
2295
+ });
2296
+ }
2297
+ currentIndex = toolCall.endIndex;
1612
2298
  }
1613
- };
1614
- return new TransformStream({
1615
- transform(chunk, controller) {
1616
- processChunk(chunk, controller);
1617
- },
1618
- flush(controller) {
1619
- flushBuffer2(controller);
2299
+ addRemainingText(text, currentIndex, processedElements);
2300
+ return processedElements;
2301
+ },
2302
+ createStreamParser({ tools, options }) {
2303
+ const toolNames = tools.map((t) => t.name).filter((name) => name != null);
2304
+ const maxStartTagLen = toolNames.length ? Math.max(...toolNames.map((n) => `<${n}>`.length)) : 0;
2305
+ let buffer = "";
2306
+ let currentToolCall = null;
2307
+ let currentTextId = null;
2308
+ const flushText = createFlushTextHandler(
2309
+ () => buffer,
2310
+ (newBuffer) => {
2311
+ buffer = newBuffer;
2312
+ },
2313
+ () => currentTextId,
2314
+ (newId) => {
2315
+ currentTextId = newId;
2316
+ }
2317
+ );
2318
+ const processChunk = (chunk, controller) => {
2319
+ if (chunk.type !== "text-delta") {
2320
+ if (buffer) {
2321
+ flushText(controller);
2322
+ }
2323
+ controller.enqueue(chunk);
2324
+ return;
2325
+ }
2326
+ buffer += chunk.delta;
2327
+ processBuffer(controller);
2328
+ };
2329
+ const processBuffer = createProcessBufferHandler({
2330
+ getBuffer: () => buffer,
2331
+ setBuffer: (newBuffer) => {
2332
+ buffer = newBuffer;
2333
+ },
2334
+ getCurrentToolCall: () => currentToolCall,
2335
+ setCurrentToolCall: (newToolCall) => {
2336
+ currentToolCall = newToolCall;
2337
+ },
2338
+ tools,
2339
+ options,
2340
+ toolNames,
2341
+ maxStartTagLen,
2342
+ flushText,
2343
+ pipelineConfig,
2344
+ maxReparses
2345
+ });
2346
+ const flushBuffer2 = (controller) => {
2347
+ if (currentToolCall) {
2348
+ const unfinishedCall = `<${currentToolCall.name}>${buffer}`;
2349
+ flushText(controller, unfinishedCall);
2350
+ } else if (buffer) {
2351
+ flushText(controller);
2352
+ }
2353
+ if (currentTextId) {
2354
+ controller.enqueue({ type: "text-end", id: currentTextId });
2355
+ }
2356
+ };
2357
+ return new TransformStream({
2358
+ transform(chunk, controller) {
2359
+ processChunk(chunk, controller);
2360
+ },
2361
+ flush(controller) {
2362
+ flushBuffer2(controller);
2363
+ }
2364
+ });
2365
+ },
2366
+ extractToolCallSegments({ text, tools }) {
2367
+ const toolNames = tools.map((t) => t.name).filter(Boolean);
2368
+ if (toolNames.length === 0) {
2369
+ return [];
1620
2370
  }
1621
- });
1622
- },
1623
- extractToolCallSegments({ text, tools }) {
1624
- const toolNames = tools.map((t) => t.name).filter(Boolean);
1625
- if (toolNames.length === 0) {
1626
- return [];
2371
+ return findToolCalls(text, toolNames).map((tc) => tc.segment);
1627
2372
  }
1628
- return findToolCalls(text, toolNames).map((tc) => tc.segment);
1629
- }
1630
- });
2373
+ };
2374
+ };
1631
2375
  function getToolSchema(tools, toolName) {
1632
2376
  var _a;
1633
2377
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
1634
2378
  }
1635
- function computeFullTagEnd(text, contentEnd, toolName) {
1636
- let fullTagEnd = contentEnd + `</${toolName}>`.length;
1637
- const closeHead = text.indexOf(`</${toolName}`, contentEnd);
1638
- if (closeHead === contentEnd) {
1639
- let p = closeHead + 2 + toolName.length;
1640
- while (p < text.length && WHITESPACE_REGEX2.test(text[p])) {
2379
+ function findClosingTagEndFlexible(text, contentStart, toolName) {
2380
+ let pos = contentStart;
2381
+ let depth = 1;
2382
+ while (pos < text.length) {
2383
+ const tok = nextTagToken(text, pos);
2384
+ if (tok.kind === "eof") {
2385
+ break;
2386
+ }
2387
+ const result = updateDepthWithToken(tok, toolName, depth);
2388
+ depth = result.depth;
2389
+ if (result.closedAt !== void 0) {
2390
+ return result.closedAt;
2391
+ }
2392
+ pos = tok.nextPos;
2393
+ }
2394
+ return -1;
2395
+ }
2396
+ function skipSpecialSegment(text, lt) {
2397
+ const next = text[lt + 1];
2398
+ if (next !== "!" && next !== "?") {
2399
+ return null;
2400
+ }
2401
+ const gt = text.indexOf(">", lt + 1);
2402
+ if (gt === -1) {
2403
+ return null;
2404
+ }
2405
+ return gt + 1;
2406
+ }
2407
+ function consumeClosingTag(text, lt, _toolName) {
2408
+ let p = lt + 2;
2409
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
2410
+ p += 1;
2411
+ }
2412
+ const gt = text.indexOf(">", lt + 1);
2413
+ const endPos = gt === -1 ? text.length : gt + 1;
2414
+ return { matched: false, endPos };
2415
+ }
2416
+ function consumeOpenTag(text, lt) {
2417
+ let p = lt + 1;
2418
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
2419
+ p += 1;
2420
+ }
2421
+ const nameStart = p;
2422
+ while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
2423
+ p += 1;
2424
+ }
2425
+ const name = text.slice(nameStart, p);
2426
+ const q = text.indexOf(">", p);
2427
+ if (q === -1) {
2428
+ return null;
2429
+ }
2430
+ let r = q - 1;
2431
+ while (r >= nameStart && WHITESPACE_REGEX3.test(text[r])) {
2432
+ r -= 1;
2433
+ }
2434
+ const selfClosing = text[r] === "/";
2435
+ return { name, selfClosing, nextPos: q + 1 };
2436
+ }
2437
+ function updateDepthWithToken(tok, toolName, depth) {
2438
+ if (tok.kind === "close" && tok.name === toolName) {
2439
+ const newDepth = depth - 1;
2440
+ return newDepth === 0 ? { depth: newDepth, closedAt: tok.nextPos } : { depth: newDepth };
2441
+ }
2442
+ if (tok.kind === "open" && tok.name === toolName && !tok.selfClosing) {
2443
+ return { depth: depth + 1 };
2444
+ }
2445
+ return { depth };
2446
+ }
2447
+ function nextTagToken(text, fromPos) {
2448
+ const lt = text.indexOf("<", fromPos);
2449
+ if (lt === -1 || lt + 1 >= text.length) {
2450
+ return { kind: "eof", nextPos: text.length };
2451
+ }
2452
+ const next = text[lt + 1];
2453
+ const specialEnd = skipSpecialSegment(text, lt);
2454
+ if (specialEnd !== null) {
2455
+ return { kind: "special", nextPos: specialEnd };
2456
+ }
2457
+ if (next === "/") {
2458
+ const closing = consumeClosingTag(text, lt, "");
2459
+ let p = lt + 2;
2460
+ while (p < text.length && WHITESPACE_REGEX3.test(text[p])) {
1641
2461
  p += 1;
1642
2462
  }
1643
- if (text[p] === ">") {
1644
- fullTagEnd = p + 1;
2463
+ const nameStart = p;
2464
+ while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
2465
+ p += 1;
1645
2466
  }
2467
+ const name = text.slice(nameStart, p);
2468
+ return { kind: "close", name, nextPos: closing.endPos };
1646
2469
  }
1647
- return fullTagEnd;
2470
+ const open = consumeOpenTag(text, lt);
2471
+ if (open === null) {
2472
+ return { kind: "eof", nextPos: text.length };
2473
+ }
2474
+ return {
2475
+ kind: "open",
2476
+ name: open.name,
2477
+ selfClosing: open.selfClosing,
2478
+ nextPos: open.nextPos
2479
+ };
1648
2480
  }
1649
- function extractToolCallInfo(text, tagStart, toolName, range) {
2481
+ function collectToolCallsFromNormalizedText(text, toolNames) {
1650
2482
  var _a;
2483
+ const normalizedText = normalizeCloseTags(text);
2484
+ const collected = [];
2485
+ for (const toolName of toolNames) {
2486
+ const startTag = `<${toolName}>`;
2487
+ let idx = 0;
2488
+ let lastOrigIdx = 0;
2489
+ while (idx < normalizedText.length) {
2490
+ const tagStartNorm = normalizedText.indexOf(startTag, idx);
2491
+ if (tagStartNorm === -1) {
2492
+ break;
2493
+ }
2494
+ const contentStartNorm = tagStartNorm + startTag.length;
2495
+ const endNorm = findClosingTagEndFlexible(
2496
+ normalizedText,
2497
+ contentStartNorm,
2498
+ toolName
2499
+ );
2500
+ if (endNorm > contentStartNorm) {
2501
+ const tagStartOrig = text.indexOf(startTag, lastOrigIdx);
2502
+ const contentStartOrig = tagStartOrig + startTag.length;
2503
+ let endOrig = findClosingTagEndFlexible(
2504
+ text,
2505
+ contentStartOrig,
2506
+ toolName
2507
+ );
2508
+ if (endOrig === -1) {
2509
+ const approxLen = endNorm - tagStartNorm;
2510
+ endOrig = Math.min(text.length, tagStartOrig + approxLen);
2511
+ }
2512
+ const segment = text.substring(tagStartOrig, endOrig);
2513
+ const inner = (_a = extractRawInner(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2514
+ collected.push({
2515
+ toolName,
2516
+ startIndex: tagStartOrig,
2517
+ endIndex: endOrig,
2518
+ content: inner,
2519
+ segment
2520
+ });
2521
+ lastOrigIdx = endOrig;
2522
+ idx = endNorm;
2523
+ } else {
2524
+ idx = contentStartNorm;
2525
+ }
2526
+ }
2527
+ }
2528
+ return collected.sort((a, b) => a.startIndex - b.startIndex);
2529
+ }
2530
+ function getNextTagInfo(text, toolName, fromIndex) {
1651
2531
  const startTag = `<${toolName}>`;
1652
- const contentStart = tagStart + startTag.length;
1653
- const contentEnd = contentStart + (range.end - range.start);
1654
- const fullTagEnd = computeFullTagEnd(text, contentEnd, toolName);
1655
- const segment = text.substring(tagStart, fullTagEnd);
1656
- const content = (_a = extractRawInner(segment, toolName)) != null ? _a : text.substring(contentStart, contentEnd);
1657
- return {
1658
- toolName,
1659
- startIndex: tagStart,
1660
- endIndex: fullTagEnd,
1661
- content,
1662
- segment
1663
- };
2532
+ const selfTag = `<${toolName}/>`;
2533
+ const openIdx = text.indexOf(startTag, fromIndex);
2534
+ const selfIdx = text.indexOf(selfTag, fromIndex);
2535
+ const hasOpen = openIdx !== -1;
2536
+ const hasSelf = selfIdx !== -1;
2537
+ if (!(hasOpen || hasSelf)) {
2538
+ return {
2539
+ found: false,
2540
+ tagStart: -1,
2541
+ selfClosing: false,
2542
+ startTag,
2543
+ selfTag
2544
+ };
2545
+ }
2546
+ const pickSelf = hasSelf && (!hasOpen || selfIdx < openIdx);
2547
+ const tagStart = pickSelf ? selfIdx : openIdx;
2548
+ return { found: true, tagStart, selfClosing: pickSelf, startTag, selfTag };
1664
2549
  }
1665
2550
  function findToolCallsForName(text, toolName) {
2551
+ var _a;
1666
2552
  const toolCalls = [];
1667
- const startTag = `<${toolName}>`;
1668
2553
  let searchIndex = 0;
1669
2554
  while (searchIndex < text.length) {
1670
- const tagStart = text.indexOf(startTag, searchIndex);
1671
- if (tagStart === -1) {
2555
+ const info = getNextTagInfo(text, toolName, searchIndex);
2556
+ if (!info.found) {
1672
2557
  break;
1673
2558
  }
1674
- const remainingText = text.substring(tagStart);
1675
- const range = findFirstTopLevelRange(remainingText, toolName);
1676
- if (range) {
1677
- const toolCallInfo = extractToolCallInfo(text, tagStart, toolName, range);
1678
- toolCalls.push(toolCallInfo);
1679
- searchIndex = toolCallInfo.endIndex;
2559
+ const { tagStart, selfClosing, startTag, selfTag } = info;
2560
+ if (selfClosing) {
2561
+ const endIndex = tagStart + selfTag.length;
2562
+ const segment = text.substring(tagStart, endIndex);
2563
+ toolCalls.push({
2564
+ toolName,
2565
+ startIndex: tagStart,
2566
+ endIndex,
2567
+ content: "",
2568
+ segment
2569
+ });
2570
+ searchIndex = endIndex;
2571
+ continue;
2572
+ }
2573
+ const contentStart = tagStart + startTag.length;
2574
+ const fullTagEnd = findClosingTagEndFlexible(text, contentStart, toolName);
2575
+ if (fullTagEnd !== -1 && fullTagEnd > contentStart) {
2576
+ const segment = text.substring(tagStart, fullTagEnd);
2577
+ const inner = (_a = extractRawInner(segment, toolName)) != null ? _a : segment.substring(startTag.length, segment.lastIndexOf("<"));
2578
+ toolCalls.push({
2579
+ toolName,
2580
+ startIndex: tagStart,
2581
+ endIndex: fullTagEnd,
2582
+ content: inner,
2583
+ segment
2584
+ });
2585
+ searchIndex = fullTagEnd;
1680
2586
  } else {
1681
- searchIndex = tagStart + startTag.length;
2587
+ searchIndex = contentStart;
1682
2588
  }
1683
2589
  }
1684
2590
  return toolCalls;
@@ -1692,14 +2598,53 @@ function findToolCalls(text, toolNames) {
1692
2598
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1693
2599
  }
1694
2600
 
1695
- // src/protocols/tool-call-protocol.ts
1696
- function isProtocolFactory(protocol) {
1697
- return typeof protocol === "function";
2601
+ // src/generate-handler.ts
2602
+ import { generateId as generateId3 } from "@ai-sdk/provider-utils";
2603
+ import { coerceBySchema } from "@ai-sdk-tool/rxml";
2604
+
2605
+ // src/utils/on-error.ts
2606
+ function extractOnErrorOption(providerOptions) {
2607
+ var _a;
2608
+ if (providerOptions && typeof providerOptions === "object") {
2609
+ const onError = (_a = providerOptions.toolCallMiddleware) == null ? void 0 : _a.onError;
2610
+ return onError ? { onError } : void 0;
2611
+ }
2612
+ return;
2613
+ }
2614
+
2615
+ // src/utils/provider-options.ts
2616
+ var originalToolsSchema = {
2617
+ encode: encodeOriginalTools,
2618
+ decode: decodeOriginalTools
2619
+ };
2620
+ function encodeOriginalTools(tools) {
2621
+ return (tools == null ? void 0 : tools.map((t) => ({
2622
+ name: t.name,
2623
+ inputSchema: JSON.stringify(t.inputSchema)
2624
+ }))) || [];
2625
+ }
2626
+ function decodeOriginalTools(originalTools) {
2627
+ if (!originalTools) {
2628
+ return [];
2629
+ }
2630
+ return originalTools.map(
2631
+ (t) => ({
2632
+ type: "function",
2633
+ name: t.name,
2634
+ inputSchema: JSON.parse(t.inputSchema)
2635
+ })
2636
+ );
2637
+ }
2638
+ function extractToolNamesFromOriginalTools(originalTools) {
2639
+ return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
2640
+ }
2641
+ function isToolChoiceActive(params) {
2642
+ var _a, _b, _c;
2643
+ const toolChoice = (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.toolChoice;
2644
+ 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"));
1698
2645
  }
1699
2646
 
1700
2647
  // src/generate-handler.ts
1701
- import { generateId as generateId4 } from "@ai-sdk/provider-utils";
1702
- import { coerceBySchema } from "@ai-sdk-tool/rxml";
1703
2648
  function parseToolChoiceJson(text, providerOptions) {
1704
2649
  var _a;
1705
2650
  try {
@@ -1743,7 +2688,7 @@ async function handleToolChoice(doGenerate, params) {
1743
2688
  }
1744
2689
  const toolCall = {
1745
2690
  type: "tool-call",
1746
- toolCallId: generateId4(),
2691
+ toolCallId: generateId3(),
1747
2692
  toolName: parsed.name || "unknown",
1748
2693
  input: JSON.stringify(parsed.arguments || {})
1749
2694
  };
@@ -1869,8 +2814,13 @@ function fixToolCallWithSchema(part, tools) {
1869
2814
  };
1870
2815
  }
1871
2816
 
2817
+ // src/protocols/tool-call-protocol.ts
2818
+ function isProtocolFactory(protocol) {
2819
+ return typeof protocol === "function";
2820
+ }
2821
+
1872
2822
  // src/stream-handler.ts
1873
- import { generateId as generateId5 } from "@ai-sdk/provider-utils";
2823
+ import { generateId as generateId4 } from "@ai-sdk/provider-utils";
1874
2824
  function extractToolCallSegments(protocol, fullRawText, tools) {
1875
2825
  const segments = protocol.extractToolCallSegments ? protocol.extractToolCallSegments({
1876
2826
  text: fullRawText,
@@ -1907,7 +2857,7 @@ function handleDebugSummary(parsedToolCalls, origin, params) {
1907
2857
  }
1908
2858
  function createDebugSummaryTransform({
1909
2859
  protocol,
1910
- fullRawText,
2860
+ getFullRawText,
1911
2861
  tools,
1912
2862
  params
1913
2863
  }) {
@@ -1920,11 +2870,9 @@ function createDebugSummaryTransform({
1920
2870
  }
1921
2871
  if (part.type === "finish") {
1922
2872
  try {
1923
- const origin = extractToolCallSegments(
1924
- protocol,
1925
- fullRawText,
1926
- tools
1927
- );
2873
+ const raw = getFullRawText();
2874
+ logRawChunk(raw);
2875
+ const origin = extractToolCallSegments(protocol, raw, tools);
1928
2876
  handleDebugSummary(parsedToolCalls, origin, params);
1929
2877
  } catch (e) {
1930
2878
  }
@@ -2022,7 +2970,7 @@ async function wrapStream({
2022
2970
  const withSummary = parsed.pipeThrough(
2023
2971
  createDebugSummaryTransform({
2024
2972
  protocol,
2025
- fullRawText,
2973
+ getFullRawText: () => fullRawText,
2026
2974
  tools,
2027
2975
  params
2028
2976
  })
@@ -2056,19 +3004,26 @@ async function toolChoiceStream({
2056
3004
  }
2057
3005
  const toolCallChunk = {
2058
3006
  type: "tool-call",
2059
- toolCallId: generateId5(),
3007
+ toolCallId: generateId4(),
2060
3008
  toolName: toolJson.name || "unknown",
2061
3009
  input: JSON.stringify(toolJson.arguments || {})
2062
3010
  };
2063
3011
  const finishChunk = {
2064
3012
  type: "finish",
2065
- usage: (result == null ? void 0 : result.usage) || // TODO: If possible, try to return a certain amount of LLM usage.
2066
- {
2067
- inputTokens: 0,
2068
- outputTokens: 0,
2069
- totalTokens: 0
3013
+ usage: (result == null ? void 0 : result.usage) || {
3014
+ inputTokens: {
3015
+ total: 0,
3016
+ noCache: void 0,
3017
+ cacheRead: void 0,
3018
+ cacheWrite: void 0
3019
+ },
3020
+ outputTokens: {
3021
+ total: 0,
3022
+ text: void 0,
3023
+ reasoning: void 0
3024
+ }
2070
3025
  },
2071
- finishReason: "tool-calls"
3026
+ finishReason: { unified: "tool-calls", raw: void 0 }
2072
3027
  };
2073
3028
  const stream = new ReadableStream({
2074
3029
  start(controller) {
@@ -2102,26 +3057,106 @@ async function toolChoiceStream({
2102
3057
  };
2103
3058
  }
2104
3059
 
3060
+ // src/utils/dynamic-tool-schema.ts
3061
+ function createDynamicIfThenElseSchema(tools) {
3062
+ let currentSchema = {};
3063
+ const toolNames = [];
3064
+ for (let i = tools.length - 1; i >= 0; i -= 1) {
3065
+ const tool = tools[i];
3066
+ if (tool.type === "provider") {
3067
+ throw new Error(
3068
+ "Provider tools are not supported by this middleware. Please use function tools."
3069
+ );
3070
+ }
3071
+ toolNames.unshift(tool.name);
3072
+ const toolCondition = {
3073
+ if: {
3074
+ properties: {
3075
+ name: {
3076
+ const: tool.name
3077
+ }
3078
+ },
3079
+ required: ["name"]
3080
+ },
3081
+ // biome-ignore lint/suspicious/noThenProperty: JSON Schema uses 'then' as a keyword
3082
+ then: {
3083
+ properties: {
3084
+ name: {
3085
+ const: tool.name
3086
+ },
3087
+ arguments: tool.inputSchema
3088
+ },
3089
+ required: ["name", "arguments"]
3090
+ }
3091
+ };
3092
+ if (Object.keys(currentSchema).length > 0) {
3093
+ toolCondition.else = currentSchema;
3094
+ }
3095
+ currentSchema = toolCondition;
3096
+ }
3097
+ return {
3098
+ type: "object",
3099
+ // Explicitly specify type as "object"
3100
+ properties: {
3101
+ name: {
3102
+ type: "string",
3103
+ description: "Name of the tool to call",
3104
+ enum: toolNames
3105
+ },
3106
+ arguments: {
3107
+ type: "object",
3108
+ // By default, arguments is also specified as object type
3109
+ description: "Argument object to be passed to the tool"
3110
+ }
3111
+ },
3112
+ required: ["name", "arguments"],
3113
+ ...currentSchema
3114
+ };
3115
+ }
3116
+
2105
3117
  // src/transform-handler.ts
2106
- function buildFinalPrompt(systemPrompt, processedPrompt) {
2107
- var _a;
2108
- if (((_a = processedPrompt[0]) == null ? void 0 : _a.role) === "system") {
3118
+ function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
3119
+ const systemIndex = processedPrompt.findIndex((m) => m.role === "system");
3120
+ if (systemIndex !== -1) {
3121
+ const existing = processedPrompt[systemIndex].content;
3122
+ let existingText = "";
3123
+ if (typeof existing === "string") {
3124
+ existingText = existing;
3125
+ } else if (Array.isArray(existing)) {
3126
+ existingText = existing.map((p) => {
3127
+ var _a;
3128
+ return (p == null ? void 0 : p.type) === "text" ? (_a = p.text) != null ? _a : "" : "";
3129
+ }).filter(Boolean).join("\n");
3130
+ } else {
3131
+ existingText = String(existing != null ? existing : "");
3132
+ }
3133
+ const mergedContent = placement === "first" ? `${systemPrompt}
3134
+
3135
+ ${existingText}` : `${existingText}
3136
+
3137
+ ${systemPrompt}`;
3138
+ return processedPrompt.map(
3139
+ (m, idx) => idx === systemIndex ? {
3140
+ ...m,
3141
+ content: mergedContent
3142
+ } : m
3143
+ );
3144
+ }
3145
+ if (placement === "first") {
2109
3146
  return [
2110
3147
  {
2111
3148
  role: "system",
2112
- content: `${systemPrompt}
2113
-
2114
- ${processedPrompt[0].content}`
3149
+ content: systemPrompt
2115
3150
  },
2116
- ...processedPrompt.slice(1)
3151
+ ...processedPrompt
2117
3152
  ];
2118
3153
  }
2119
3154
  return [
3155
+ ...processedPrompt,
2120
3156
  {
2121
3157
  role: "system",
2122
3158
  content: systemPrompt
2123
- },
2124
- ...processedPrompt
3159
+ }
2125
3160
  ];
2126
3161
  }
2127
3162
  function buildBaseReturnParams(params, finalPrompt, functionTools) {
@@ -2221,7 +3256,8 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2221
3256
  function transformParams({
2222
3257
  params,
2223
3258
  protocol,
2224
- toolSystemPromptTemplate
3259
+ toolSystemPromptTemplate,
3260
+ placement = "first"
2225
3261
  }) {
2226
3262
  var _a, _b, _c, _d, _e;
2227
3263
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
@@ -2237,7 +3273,11 @@ function transformParams({
2237
3273
  resolvedProtocol,
2238
3274
  extractOnErrorOption(params.providerOptions)
2239
3275
  );
2240
- const finalPrompt = buildFinalPrompt(systemPrompt, processedPrompt);
3276
+ const finalPrompt = buildFinalPrompt(
3277
+ systemPrompt,
3278
+ processedPrompt,
3279
+ placement
3280
+ );
2241
3281
  const baseReturnParams = buildBaseReturnParams(
2242
3282
  params,
2243
3283
  finalPrompt,
@@ -2314,7 +3354,10 @@ function processMessage(message, resolvedProtocol, providerOptions) {
2314
3354
  };
2315
3355
  }
2316
3356
  if (message.role === "tool") {
2317
- return processToolMessage(message.content, resolvedProtocol);
3357
+ const toolResultParts = message.content.filter(
3358
+ (part) => part.type === "tool-result"
3359
+ );
3360
+ return processToolMessage(toolResultParts, resolvedProtocol);
2318
3361
  }
2319
3362
  return message;
2320
3363
  }
@@ -2389,7 +3432,8 @@ function convertToolPrompt(prompt, resolvedProtocol, providerOptions) {
2389
3432
  // src/tool-call-middleware.ts
2390
3433
  function createToolMiddleware({
2391
3434
  protocol,
2392
- toolSystemPromptTemplate
3435
+ toolSystemPromptTemplate,
3436
+ placement = "last"
2393
3437
  }) {
2394
3438
  const resolvedProtocol = isProtocolFactory(protocol) ? protocol() : protocol;
2395
3439
  return {
@@ -2416,6 +3460,7 @@ function createToolMiddleware({
2416
3460
  transformParams: async ({ params }) => transformParams({
2417
3461
  protocol: resolvedProtocol,
2418
3462
  toolSystemPromptTemplate,
3463
+ placement,
2419
3464
  params
2420
3465
  })
2421
3466
  };
@@ -2461,6 +3506,7 @@ For each function call return a json object with function name and arguments wit
2461
3506
  });
2462
3507
  var morphXmlToolMiddleware = createToolMiddleware({
2463
3508
  protocol: morphXmlProtocol,
3509
+ placement: "last",
2464
3510
  toolSystemPromptTemplate(tools) {
2465
3511
  return `You are a function calling AI model.
2466
3512
 
@@ -2485,30 +3531,39 @@ Available functions are listed inside <tools></tools>.
2485
3531
 
2486
3532
  export {
2487
3533
  getDebugLevel,
3534
+ logParseFailure,
2488
3535
  logRawChunk,
2489
3536
  logParsedChunk,
2490
3537
  logParsedSummary,
2491
- createDynamicIfThenElseSchema,
2492
3538
  getPotentialStartIndex,
2493
- extractOnErrorOption,
2494
- originalToolsSchema,
2495
- encodeOriginalTools,
2496
- decodeOriginalTools,
2497
- extractToolNamesFromOriginalTools,
2498
- isToolChoiceActive,
2499
3539
  escapeRegExp,
2500
3540
  transform,
2501
3541
  parse,
2502
3542
  stringify,
2503
- robust_json_exports,
3543
+ jsonMixProtocol,
3544
+ applyHeuristicPipeline,
3545
+ createIntermediateCall,
3546
+ mergePipelineConfigs,
3547
+ normalizeCloseTagsHeuristic,
3548
+ escapeInvalidLtHeuristic,
3549
+ balanceTagsHeuristic,
3550
+ dedupeShellStringTagsHeuristic,
3551
+ repairAgainstSchemaHeuristic,
3552
+ defaultPipelineConfig,
2504
3553
  isToolCallContent,
2505
3554
  isToolResultPart,
2506
3555
  hasInputProperty,
2507
- jsonMixProtocol,
2508
3556
  morphXmlProtocol,
3557
+ extractOnErrorOption,
3558
+ originalToolsSchema,
3559
+ encodeOriginalTools,
3560
+ decodeOriginalTools,
3561
+ extractToolNamesFromOriginalTools,
3562
+ isToolChoiceActive,
3563
+ createDynamicIfThenElseSchema,
2509
3564
  createToolMiddleware,
2510
3565
  gemmaToolMiddleware,
2511
3566
  hermesToolMiddleware,
2512
3567
  morphXmlToolMiddleware
2513
3568
  };
2514
- //# sourceMappingURL=chunk-FOANBZRH.js.map
3569
+ //# sourceMappingURL=chunk-LB5ALTRD.js.map