@kortyx/hooks 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -298,7 +298,8 @@ var emitStructuredData = (args) => {
298
298
  data: validated
299
299
  });
300
300
  };
301
- var shouldEmitStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
301
+ var shouldEmitStructured = (cfg) => Boolean(cfg);
302
+ var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
302
303
 
303
304
  // src/reason/checkpoint.ts
304
305
  var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
@@ -861,12 +862,12 @@ var withStructuredStreamHints = (input, args) => {
861
862
  const rules = [];
862
863
  for (const path of args.setFieldPaths ?? []) {
863
864
  rules.push(
864
- `- For the top-level field \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
865
+ `- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
865
866
  );
866
867
  }
867
868
  for (const path of args.appendFieldPaths ?? []) {
868
869
  rules.push(
869
- `- For the top-level array field \`${path}\`, write items in final order and finish each item before starting the next.`
870
+ `- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
870
871
  );
871
872
  rules.push(
872
873
  `- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
@@ -874,7 +875,7 @@ var withStructuredStreamHints = (input, args) => {
874
875
  }
875
876
  for (const path of args.textDeltaFieldPaths ?? []) {
876
877
  rules.push(
877
- `- For the top-level string field \`${path}\`, write the value progressively by appending text to the end.`
878
+ `- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
878
879
  );
879
880
  rules.push(
880
881
  `- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
@@ -890,22 +891,12 @@ var withStructuredStreamHints = (input, args) => {
890
891
  };
891
892
 
892
893
  // src/reason/structured-stream.ts
893
- var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
894
- var findTopLevelArrayStart = (text, path) => {
895
- const match = new RegExp(`"${escapeRegex(path)}"\\s*:\\s*\\[`).exec(text);
896
- return match ? match.index + match[0].length : -1;
897
- };
898
894
  var resolveAppendFieldPaths = (cfg) => {
899
895
  const fields = cfg?.fields;
900
896
  if (!fields) return [];
901
897
  const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
902
898
  for (const path of appendPaths) {
903
899
  assertStructuredPath(path, "useReason structured append");
904
- if (path.includes(".")) {
905
- throw new Error(
906
- "useReason structured append streaming requires non-empty top-level array field keys."
907
- );
908
- }
909
900
  }
910
901
  return appendPaths;
911
902
  };
@@ -915,11 +906,6 @@ var resolveSetFieldPaths = (cfg) => {
915
906
  const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
916
907
  for (const path of setPaths) {
917
908
  assertStructuredPath(path, "useReason structured set");
918
- if (path.includes(".")) {
919
- throw new Error(
920
- "useReason structured set streaming requires non-empty top-level field keys."
921
- );
922
- }
923
909
  }
924
910
  return setPaths;
925
911
  };
@@ -929,22 +915,9 @@ var resolveTextDeltaFieldPaths = (cfg) => {
929
915
  const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
930
916
  for (const path of textPaths) {
931
917
  assertStructuredPath(path, "useReason structured text-delta");
932
- if (path.includes(".")) {
933
- throw new Error(
934
- "useReason structured text-delta streaming requires non-empty top-level string field keys."
935
- );
936
- }
937
918
  }
938
919
  return textPaths;
939
920
  };
940
- var findTopLevelFieldValueStart = (text, path) => {
941
- const match = new RegExp(`"${escapeRegex(path)}"\\s*:\\s*`).exec(text);
942
- return match ? match.index + match[0].length : -1;
943
- };
944
- var findTopLevelStringStart = (text, path) => {
945
- const match = new RegExp(`"${escapeRegex(path)}"\\s*:\\s*"`).exec(text);
946
- return match ? match.index + match[0].length : -1;
947
- };
948
921
  var extractCompletedStringValueAt = (args) => {
949
922
  let value = "";
950
923
  for (let i = args.start; i < args.text.length; i += 1) {
@@ -990,12 +963,52 @@ var extractCompletedStringValueAt = (args) => {
990
963
  complete: false
991
964
  };
992
965
  };
993
- var extractCompletedNestedValueAt = (args) => {
966
+ var skipWhitespace = (text, start) => {
967
+ let i = start;
968
+ while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
969
+ return i;
970
+ };
971
+ var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
972
+ var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
973
+ (part, index) => pathSegmentMatches(patternParts[index] ?? "", part)
974
+ );
975
+ var isPathPrefix = (prefix, full) => prefix.length <= full.length && prefix.every((part, index) => pathSegmentMatches(full[index] ?? "", part));
976
+ var parseStringToken = (text, start) => {
977
+ if (text[start] !== '"') return { complete: false };
978
+ const parsed = extractCompletedStringValueAt({ text, start: start + 1 });
979
+ if (!parsed.complete || parsed.value === void 0)
980
+ return { complete: false };
981
+ let inEscape = false;
982
+ for (let i = start + 1; i < text.length; i += 1) {
983
+ const ch = text[i];
984
+ if (ch === void 0) break;
985
+ if (inEscape) {
986
+ inEscape = false;
987
+ if (ch === "u") i += 4;
988
+ continue;
989
+ }
990
+ if (ch === "\\") {
991
+ inEscape = true;
992
+ continue;
993
+ }
994
+ if (ch === '"') {
995
+ return {
996
+ complete: true,
997
+ end: i + 1,
998
+ value: parsed.value
999
+ };
1000
+ }
1001
+ }
1002
+ return { complete: false };
1003
+ };
1004
+ var skipNestedValue = (text, start) => {
1005
+ const opener = text[start];
1006
+ const closer = opener === "{" ? "}" : "]";
994
1007
  let inString = false;
995
1008
  let isEscaped = false;
996
1009
  let nesting = 0;
997
- for (let i = args.start; i < args.text.length; i += 1) {
998
- const ch = args.text[i];
1010
+ for (let i = start; i < text.length; i += 1) {
1011
+ const ch = text[i];
999
1012
  if (ch === void 0) break;
1000
1013
  if (inString) {
1001
1014
  if (isEscaped) {
@@ -1015,124 +1028,266 @@ var extractCompletedNestedValueAt = (args) => {
1015
1028
  inString = true;
1016
1029
  continue;
1017
1030
  }
1018
- if (ch === args.opener) {
1031
+ if (ch === opener) {
1019
1032
  nesting += 1;
1020
1033
  continue;
1021
1034
  }
1022
- if (ch === args.closer) {
1035
+ if (ch === closer) {
1023
1036
  nesting -= 1;
1024
1037
  if (nesting === 0) {
1025
- const raw = args.text.slice(args.start, i + 1);
1026
- try {
1027
- return {
1028
- complete: true,
1029
- value: JSON.parse(raw)
1030
- };
1031
- } catch {
1032
- return {
1033
- complete: false
1034
- };
1035
- }
1038
+ return { complete: true, end: i + 1 };
1036
1039
  }
1037
1040
  }
1038
1041
  }
1039
- return {
1040
- complete: false
1041
- };
1042
+ return { complete: false };
1042
1043
  };
1043
- var extractCompletedFieldValue = (args) => {
1044
- const start = findTopLevelFieldValueStart(args.text, args.path);
1045
- if (start < 0) {
1046
- return {
1047
- found: false,
1048
- complete: false
1049
- };
1044
+ var skipCompleteValue = (text, start) => {
1045
+ const i = skipWhitespace(text, start);
1046
+ const first = text[i];
1047
+ if (!first) return { complete: false };
1048
+ if (first === '"') {
1049
+ const parsed = parseStringToken(text, i);
1050
+ return parsed.complete && parsed.end !== void 0 ? { complete: true, end: parsed.end } : { complete: false };
1050
1051
  }
1051
- const first = args.text[start];
1052
- if (!first) {
1053
- return {
1054
- found: true,
1055
- complete: false
1056
- };
1052
+ if (first === "{" || first === "[") {
1053
+ return skipNestedValue(text, i);
1057
1054
  }
1055
+ for (let j = i; j < text.length; j += 1) {
1056
+ const ch = text[j];
1057
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1058
+ const raw = text.slice(i, j).trim();
1059
+ if (!raw) return { complete: false };
1060
+ try {
1061
+ JSON.parse(raw);
1062
+ return { complete: true, end: j };
1063
+ } catch {
1064
+ return { complete: false };
1065
+ }
1066
+ }
1067
+ return { complete: false };
1068
+ };
1069
+ var locateValueStart = (args) => {
1070
+ const [first] = locateValueStarts(args);
1071
+ return first ? {
1072
+ found: true,
1073
+ start: first.start
1074
+ } : {
1075
+ found: false
1076
+ };
1077
+ };
1078
+ var locateValueStarts = (args) => {
1079
+ const targetParts = parseStructuredPath(
1080
+ args.path,
1081
+ "useReason structured field"
1082
+ );
1083
+ const matches = [];
1084
+ const visitValue = (start, currentParts) => {
1085
+ const valueStart = skipWhitespace(args.text, start);
1086
+ if (pathMatchesPattern(currentParts, targetParts)) {
1087
+ matches.push({
1088
+ path: currentParts.join("."),
1089
+ start: valueStart
1090
+ });
1091
+ return;
1092
+ }
1093
+ if (!isPathPrefix(currentParts, targetParts)) {
1094
+ return;
1095
+ }
1096
+ const first = args.text[valueStart];
1097
+ if (first === "{") visitObject(valueStart + 1, currentParts);
1098
+ if (first === "[") visitArray(valueStart + 1, currentParts);
1099
+ };
1100
+ const visitObject = (start, currentParts) => {
1101
+ let i = skipWhitespace(args.text, start);
1102
+ while (i < args.text.length) {
1103
+ const ch = args.text[i];
1104
+ if (ch === "}") return;
1105
+ if (ch === ",") {
1106
+ i = skipWhitespace(args.text, i + 1);
1107
+ continue;
1108
+ }
1109
+ if (ch !== '"') return;
1110
+ const key = parseStringToken(args.text, i);
1111
+ if (!key.complete || key.end === void 0 || key.value === void 0) {
1112
+ return;
1113
+ }
1114
+ i = skipWhitespace(args.text, key.end);
1115
+ if (args.text[i] !== ":") return;
1116
+ const valueStart = skipWhitespace(args.text, i + 1);
1117
+ const childParts = [...currentParts, key.value];
1118
+ if (pathMatchesPattern(childParts, targetParts)) {
1119
+ matches.push({
1120
+ path: childParts.join("."),
1121
+ start: valueStart
1122
+ });
1123
+ } else if (isPathPrefix(childParts, targetParts)) {
1124
+ visitValue(valueStart, childParts);
1125
+ }
1126
+ const skipped = skipCompleteValue(args.text, valueStart);
1127
+ if (!skipped.complete || skipped.end === void 0) {
1128
+ return;
1129
+ }
1130
+ i = skipWhitespace(args.text, skipped.end);
1131
+ if (args.text[i] === ",") {
1132
+ i = skipWhitespace(args.text, i + 1);
1133
+ continue;
1134
+ }
1135
+ if (args.text[i] === "}") return;
1136
+ return;
1137
+ }
1138
+ };
1139
+ const visitArray = (start, currentParts) => {
1140
+ let i = skipWhitespace(args.text, start);
1141
+ let index = 0;
1142
+ while (i < args.text.length) {
1143
+ if (args.text[i] === "]") return;
1144
+ const childParts = [...currentParts, String(index)];
1145
+ if (pathMatchesPattern(childParts, targetParts)) {
1146
+ matches.push({
1147
+ path: childParts.join("."),
1148
+ start: i
1149
+ });
1150
+ } else if (isPathPrefix(childParts, targetParts)) {
1151
+ visitValue(i, childParts);
1152
+ }
1153
+ const skipped = skipCompleteValue(args.text, i);
1154
+ if (!skipped.complete || skipped.end === void 0) {
1155
+ return;
1156
+ }
1157
+ i = skipWhitespace(args.text, skipped.end);
1158
+ if (args.text[i] === ",") {
1159
+ index += 1;
1160
+ i = skipWhitespace(args.text, i + 1);
1161
+ continue;
1162
+ }
1163
+ if (args.text[i] === "]") return;
1164
+ return;
1165
+ }
1166
+ };
1167
+ visitValue(0, []);
1168
+ return matches;
1169
+ };
1170
+ var extractCompletedValueAt = (args) => {
1171
+ const start = skipWhitespace(args.text, args.start);
1172
+ const first = args.text[start];
1173
+ if (!first) return { complete: false };
1058
1174
  if (first === '"') {
1059
1175
  const parsed = extractCompletedStringValueAt({
1060
1176
  text: args.text,
1061
1177
  start: start + 1
1062
1178
  });
1063
1179
  return parsed.complete ? {
1064
- found: true,
1065
1180
  complete: true,
1066
1181
  value: parsed.value
1067
1182
  } : {
1068
- found: true,
1069
1183
  complete: false
1070
1184
  };
1071
1185
  }
1072
1186
  if (first === "{") {
1073
- const parsed = extractCompletedNestedValueAt({
1187
+ return extractCompletedNestedValueAt({
1074
1188
  text: args.text,
1075
1189
  start,
1076
1190
  opener: "{",
1077
1191
  closer: "}"
1078
1192
  });
1079
- return parsed.complete ? {
1080
- found: true,
1081
- complete: true,
1082
- value: parsed.value
1083
- } : {
1084
- found: true,
1085
- complete: false
1086
- };
1087
1193
  }
1088
1194
  if (first === "[") {
1089
- const parsed = extractCompletedNestedValueAt({
1195
+ return extractCompletedNestedValueAt({
1090
1196
  text: args.text,
1091
1197
  start,
1092
1198
  opener: "[",
1093
1199
  closer: "]"
1094
1200
  });
1095
- return parsed.complete ? {
1096
- found: true,
1097
- complete: true,
1098
- value: parsed.value
1099
- } : {
1100
- found: true,
1101
- complete: false
1102
- };
1103
1201
  }
1104
1202
  for (let i = start; i < args.text.length; i += 1) {
1105
1203
  const ch = args.text[i];
1106
1204
  if (ch === void 0) break;
1107
- if (ch !== "," && ch !== "}") continue;
1205
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1108
1206
  const raw = args.text.slice(start, i).trim();
1109
- if (!raw) {
1110
- return {
1111
- found: true,
1112
- complete: false
1113
- };
1114
- }
1207
+ if (!raw) return { complete: false };
1115
1208
  try {
1116
1209
  return {
1117
- found: true,
1118
1210
  complete: true,
1119
1211
  value: JSON.parse(raw)
1120
1212
  };
1121
1213
  } catch {
1122
- return {
1123
- found: true,
1124
- complete: false
1125
- };
1214
+ return { complete: false };
1215
+ }
1216
+ }
1217
+ return { complete: false };
1218
+ };
1219
+ var extractCompletedNestedValueAt = (args) => {
1220
+ let inString = false;
1221
+ let isEscaped = false;
1222
+ let nesting = 0;
1223
+ for (let i = args.start; i < args.text.length; i += 1) {
1224
+ const ch = args.text[i];
1225
+ if (ch === void 0) break;
1226
+ if (inString) {
1227
+ if (isEscaped) {
1228
+ isEscaped = false;
1229
+ continue;
1230
+ }
1231
+ if (ch === "\\") {
1232
+ isEscaped = true;
1233
+ continue;
1234
+ }
1235
+ if (ch === '"') {
1236
+ inString = false;
1237
+ }
1238
+ continue;
1239
+ }
1240
+ if (ch === '"') {
1241
+ inString = true;
1242
+ continue;
1243
+ }
1244
+ if (ch === args.opener) {
1245
+ nesting += 1;
1246
+ continue;
1247
+ }
1248
+ if (ch === args.closer) {
1249
+ nesting -= 1;
1250
+ if (nesting === 0) {
1251
+ const raw = args.text.slice(args.start, i + 1);
1252
+ try {
1253
+ return {
1254
+ complete: true,
1255
+ value: JSON.parse(raw)
1256
+ };
1257
+ } catch {
1258
+ return {
1259
+ complete: false
1260
+ };
1261
+ }
1262
+ }
1126
1263
  }
1127
1264
  }
1128
1265
  return {
1129
- found: true,
1130
1266
  complete: false
1131
1267
  };
1132
1268
  };
1269
+ var extractCompletedFieldValues = (args) => {
1270
+ const located = locateValueStarts(args);
1271
+ const values = [];
1272
+ for (const match of located) {
1273
+ const parsed = extractCompletedValueAt({
1274
+ text: args.text,
1275
+ start: match.start
1276
+ });
1277
+ if (!parsed.complete) continue;
1278
+ values.push({
1279
+ path: match.path,
1280
+ value: parsed.value
1281
+ });
1282
+ }
1283
+ return values;
1284
+ };
1133
1285
  var extractCompletedArrayItems = (args) => {
1134
- const start = findTopLevelArrayStart(args.text, args.path);
1135
- if (start < 0) return [];
1286
+ const located = locateValueStart(args);
1287
+ if (!located.found || located.start === void 0) return [];
1288
+ const arrayStart = skipWhitespace(args.text, located.start);
1289
+ if (args.text[arrayStart] !== "[") return [];
1290
+ const start = arrayStart + 1;
1136
1291
  const items = [];
1137
1292
  let inString = false;
1138
1293
  let isEscaped = false;
@@ -1195,17 +1350,39 @@ var extractCompletedArrayItems = (args) => {
1195
1350
  }
1196
1351
  return items;
1197
1352
  };
1353
+ var extractCompletedArrayItemGroups = (args) => {
1354
+ return locateValueStarts(args).flatMap((match) => {
1355
+ const items = extractCompletedArrayItems({
1356
+ text: args.text,
1357
+ path: match.path
1358
+ });
1359
+ return items.length > 0 ? [
1360
+ {
1361
+ path: match.path,
1362
+ items
1363
+ }
1364
+ ] : [];
1365
+ });
1366
+ };
1198
1367
  var extractStreamingStringValue = (args) => {
1199
- const start = findTopLevelStringStart(args.text, args.path);
1200
- if (start < 0) {
1368
+ const located = locateValueStart(args);
1369
+ if (!located.found || located.start === void 0) {
1201
1370
  return {
1202
- found: false,
1371
+ found: located.found,
1372
+ complete: false,
1373
+ value: ""
1374
+ };
1375
+ }
1376
+ const stringStart = skipWhitespace(args.text, located.start);
1377
+ if (args.text[stringStart] !== '"') {
1378
+ return {
1379
+ found: true,
1203
1380
  complete: false,
1204
1381
  value: ""
1205
1382
  };
1206
1383
  }
1207
1384
  let value = "";
1208
- for (let i = start; i < args.text.length; i += 1) {
1385
+ for (let i = stringStart + 1; i < args.text.length; i += 1) {
1209
1386
  const ch = args.text[i];
1210
1387
  if (ch === void 0) break;
1211
1388
  if (ch === '"') {
@@ -1257,6 +1434,21 @@ var extractStreamingStringValue = (args) => {
1257
1434
  value
1258
1435
  };
1259
1436
  };
1437
+ var extractStreamingStringValues = (args) => {
1438
+ return locateValueStarts(args).flatMap((match) => {
1439
+ const current = extractStreamingStringValue({
1440
+ text: args.text,
1441
+ path: match.path
1442
+ });
1443
+ return current.found ? [
1444
+ {
1445
+ path: match.path,
1446
+ complete: current.complete,
1447
+ value: current.value
1448
+ }
1449
+ ] : [];
1450
+ });
1451
+ };
1260
1452
 
1261
1453
  // src/reason/use-reason.ts
1262
1454
  var mergeUsage = (left, right) => {
@@ -1316,7 +1508,7 @@ var emitReasonStructuredOutput = (args) => {
1316
1508
  streamId: args.opId
1317
1509
  });
1318
1510
  };
1319
- var shouldEmitReasonStructured = (args) => (args.emit ?? true) && shouldEmitStructured(args.structured);
1511
+ var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
1320
1512
  var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
1321
1513
  var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
1322
1514
  var assertReasoningThoughtsCompatibility = (args) => {
@@ -1366,7 +1558,7 @@ async function useReason(args) {
1366
1558
  const appendFieldPaths = resolveAppendFieldPaths(args.structured);
1367
1559
  const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
1368
1560
  const useStructuredIncrementalStreaming = Boolean(
1369
- args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldEmitReasonStructured(args)
1561
+ args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
1370
1562
  );
1371
1563
  let firstText = "";
1372
1564
  let firstRaw;
@@ -1407,70 +1599,73 @@ async function useReason(args) {
1407
1599
  onTextChunk: (delta) => {
1408
1600
  firstText += delta;
1409
1601
  finalText = firstText;
1410
- for (const path of setFieldPaths) {
1411
- const current = extractCompletedFieldValue({
1602
+ for (const fieldPath of setFieldPaths) {
1603
+ const values = extractCompletedFieldValues({
1412
1604
  text: firstText,
1413
- path
1414
- });
1415
- if (!current.found || !current.complete) continue;
1416
- const nextSerialized = JSON.stringify(current.value);
1417
- if (emittedSetValues.get(path) === nextSerialized) continue;
1418
- emittedSetValues.set(path, nextSerialized);
1419
- emitStructuredData({
1420
- kind: "set",
1421
- path,
1422
- value: current.value,
1423
- dataType: args.structured?.dataType ?? "reason-output",
1424
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1425
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1426
- ...id ? { id } : {},
1427
- streamId: opId
1605
+ path: fieldPath
1428
1606
  });
1607
+ for (const { path, value } of values) {
1608
+ const nextSerialized = JSON.stringify(value);
1609
+ if (emittedSetValues.get(path) === nextSerialized) continue;
1610
+ emittedSetValues.set(path, nextSerialized);
1611
+ emitStructuredData({
1612
+ kind: "set",
1613
+ path,
1614
+ value,
1615
+ dataType: args.structured?.dataType ?? "reason-output",
1616
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1617
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1618
+ ...id ? { id } : {},
1619
+ streamId: opId
1620
+ });
1621
+ }
1429
1622
  }
1430
- for (const path of appendFieldPaths) {
1431
- const items = extractCompletedArrayItems({
1623
+ for (const fieldPath of appendFieldPaths) {
1624
+ const groups = extractCompletedArrayItemGroups({
1432
1625
  text: firstText,
1433
- path
1434
- });
1435
- const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
1436
- if (items.length <= emittedAppendCount) continue;
1437
- const nextItems = items.slice(emittedAppendCount);
1438
- emittedAppendCounts.set(path, items.length);
1439
- emitStructuredData({
1440
- kind: "append",
1441
- path,
1442
- items: nextItems,
1443
- dataType: args.structured?.dataType ?? "reason-output",
1444
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1445
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1446
- ...id ? { id } : {},
1447
- streamId: opId
1626
+ path: fieldPath
1448
1627
  });
1628
+ for (const { path, items } of groups) {
1629
+ const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
1630
+ if (items.length <= emittedAppendCount) continue;
1631
+ const nextItems = items.slice(emittedAppendCount);
1632
+ emittedAppendCounts.set(path, items.length);
1633
+ emitStructuredData({
1634
+ kind: "append",
1635
+ path,
1636
+ items: nextItems,
1637
+ dataType: args.structured?.dataType ?? "reason-output",
1638
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1639
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1640
+ ...id ? { id } : {},
1641
+ streamId: opId
1642
+ });
1643
+ }
1449
1644
  }
1450
- for (const path of textDeltaFieldPaths) {
1451
- const current = extractStreamingStringValue({
1645
+ for (const fieldPath of textDeltaFieldPaths) {
1646
+ const values = extractStreamingStringValues({
1452
1647
  text: firstText,
1453
- path
1648
+ path: fieldPath
1454
1649
  });
1455
- const emittedTextValue = emittedTextValues.get(path) ?? "";
1456
- if (!current.found || current.value.length <= emittedTextValue.length || !current.value.startsWith(emittedTextValue)) {
1457
- continue;
1650
+ for (const { path, value } of values) {
1651
+ const emittedTextValue = emittedTextValues.get(path) ?? "";
1652
+ if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
1653
+ continue;
1654
+ }
1655
+ const nextDelta = value.slice(emittedTextValue.length);
1656
+ emittedTextValues.set(path, value);
1657
+ if (nextDelta.length === 0) continue;
1658
+ emitStructuredData({
1659
+ kind: "text-delta",
1660
+ path,
1661
+ delta: nextDelta,
1662
+ dataType: args.structured?.dataType ?? "reason-output",
1663
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1664
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1665
+ ...id ? { id } : {},
1666
+ streamId: opId
1667
+ });
1458
1668
  }
1459
- const nextDelta = current.value.slice(
1460
- emittedTextValue.length
1461
- );
1462
- emittedTextValues.set(path, current.value);
1463
- if (nextDelta.length === 0) continue;
1464
- emitStructuredData({
1465
- kind: "text-delta",
1466
- path,
1467
- delta: nextDelta,
1468
- dataType: args.structured?.dataType ?? "reason-output",
1469
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1470
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1471
- ...id ? { id } : {},
1472
- streamId: opId
1473
- });
1474
1669
  }
1475
1670
  }
1476
1671
  } : {}