@kortyx/agent 0.19.0 → 0.20.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
@@ -157,7 +157,7 @@ var require_dist = __commonJS({
157
157
  if (!usage) return void 0;
158
158
  const input = usage.input ?? 0;
159
159
  const output = usage.output ?? 0;
160
- const total = usage.total ?? input + output + (usage.reasoning ?? 0);
160
+ const total = usage.total ?? input + output + (usage.outputIncludesReasoning ? 0 : usage.reasoning ?? 0);
161
161
  if (input === 0 && output === 0 && total === 0) {
162
162
  return void 0;
163
163
  }
@@ -433,7 +433,7 @@ var require_dist = __commonJS({
433
433
  });
434
434
  };
435
435
  async function runReasonEngine(args) {
436
- const stream = args.stream ?? args.model.options?.streaming ?? true;
436
+ let stream = args.stream ?? args.model.options?.streaming ?? true;
437
437
  const emit = args.emit ?? true;
438
438
  const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
439
439
  const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
@@ -441,7 +441,14 @@ var require_dist = __commonJS({
441
441
  const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
442
442
  const reasoning = args.reasoning ?? args.model.options?.reasoning;
443
443
  const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
444
- const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
444
+ const defaults = args.model.options?.providerOptions;
445
+ const providerOptions = defaults || args.providerOptions ? { ...defaults, ...args.providerOptions } : void 0;
446
+ for (const [key, value] of Object.entries(args.providerOptions ?? {})) {
447
+ const previous = defaults?.[key];
448
+ if (previous && value && typeof previous === "object" && typeof value === "object" && !Array.isArray(previous) && !Array.isArray(value)) {
449
+ if (providerOptions) providerOptions[key] = { ...previous, ...value };
450
+ }
451
+ }
445
452
  const tools = args.tools ?? args.model.options?.tools;
446
453
  (0, import_core22.throwIfExecutionAborted)(abortSignal);
447
454
  const model = args.model.provider.getModel(args.model.modelId, {
@@ -455,6 +462,7 @@ var require_dist = __commonJS({
455
462
  ...tools !== void 0 ? { tools } : {},
456
463
  ...providerOptions !== void 0 ? { providerOptions } : {}
457
464
  });
465
+ if (tools?.length && !model.supportsToolStreaming) stream = false;
458
466
  const messages = args.messages !== void 0 ? args.messages : [
459
467
  ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
460
468
  { role: "user", content: String(args.input ?? "") }
@@ -482,7 +490,9 @@ var require_dist = __commonJS({
482
490
  },
483
491
  telemetry: {
484
492
  ...args.telemetry ?? {},
485
- input: args.telemetry?.input ?? messages
493
+ input: args.telemetry?.input ?? messages.map(
494
+ ({ continuation: _continuation, raw: _raw, ...message }) => message
495
+ )
486
496
  }
487
497
  });
488
498
  const modelRequestStartedAt = Date.now();
@@ -513,7 +523,11 @@ var require_dist = __commonJS({
513
523
  });
514
524
  };
515
525
  const failTrace = (span, error) => {
526
+ const detail = error;
516
527
  span?.fail?.(error, {
528
+ ...detail?.usage ? { usage: detail.usage } : {},
529
+ ...detail?.finishReason ? { finishReason: detail.finishReason } : {},
530
+ ...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
517
531
  attributes: {
518
532
  providerId: args.model.provider.id,
519
533
  modelId: args.model.modelId
@@ -522,6 +536,7 @@ var require_dist = __commonJS({
522
536
  };
523
537
  try {
524
538
  if (stream) {
539
+ let continuation;
525
540
  let final = "";
526
541
  let raw;
527
542
  let usage;
@@ -560,6 +575,7 @@ var require_dist = __commonJS({
560
575
  break;
561
576
  }
562
577
  case "finish": {
578
+ continuation = chunk.continuation;
563
579
  if (chunk.raw !== void 0) {
564
580
  raw = chunk.raw;
565
581
  }
@@ -608,6 +624,7 @@ var require_dist = __commonJS({
608
624
  }
609
625
  const result2 = {
610
626
  text: final,
627
+ ...continuation ? { continuation } : {},
611
628
  ...raw !== void 0 ? { raw } : {},
612
629
  ...usage !== void 0 ? { usage } : {},
613
630
  ...finishReason !== void 0 ? { finishReason } : {},
@@ -621,6 +638,7 @@ var require_dist = __commonJS({
621
638
  }
622
639
  const response = await model.invoke(messages);
623
640
  (0, import_core22.throwIfExecutionAborted)(abortSignal);
641
+ if (response.content) args.onTextChunk?.(response.content);
624
642
  if (emit) {
625
643
  emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
626
644
  ...commonMeta
@@ -637,6 +655,7 @@ var require_dist = __commonJS({
637
655
  }
638
656
  const result = {
639
657
  text: response.content,
658
+ ...response.continuation ? { continuation: response.continuation } : {},
640
659
  ...response.raw !== void 0 ? { raw: response.raw } : {},
641
660
  ...response.usage !== void 0 ? { usage: response.usage } : {},
642
661
  ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
@@ -668,472 +687,184 @@ var require_dist = __commonJS({
668
687
  );
669
688
  (0, import_core32.throwIfExecutionAborted)(abortSignal);
670
689
  ctx.node.consumeExecution?.("maxModelPasses");
671
- return runReasonEngine({
672
- model: args.model,
673
- input: inputOverride ?? args.input,
674
- system: args.system,
675
- temperature: args.temperature,
676
- maxOutputTokens: args.maxOutputTokens,
677
- stopSequences: args.stopSequences,
678
- abortSignal,
679
- reasoning: args.reasoning,
680
- responseFormat: args.responseFormat,
681
- providerOptions: args.providerOptions,
682
- tools: args.tools,
683
- messages: args.messages,
684
- defaultTemperature: ctx.node.config?.model?.temperature,
685
- stream: args.stream,
686
- emit: args.emit,
687
- onTextChunk: args.onTextChunk,
688
- telemetry: args.telemetry,
689
- nodeId: ctx.node.graph.node,
690
- emitEvent: ctx.node.emit,
691
- reasonTrace: ctx.reasonTrace,
692
- id: meta.id,
693
- opId: meta.opId
694
- });
695
- }
696
- var tryJsonParse = (value) => {
697
690
  try {
698
- return { ok: true, value: JSON.parse(value) };
691
+ return await runReasonEngine({
692
+ model: args.model,
693
+ input: inputOverride ?? args.input,
694
+ system: args.system,
695
+ temperature: args.temperature,
696
+ maxOutputTokens: args.maxOutputTokens,
697
+ stopSequences: args.stopSequences,
698
+ abortSignal,
699
+ reasoning: args.reasoning,
700
+ responseFormat: args.responseFormat,
701
+ providerOptions: args.providerOptions,
702
+ tools: args.tools,
703
+ messages: args.messages,
704
+ defaultTemperature: ctx.node.config?.model?.temperature,
705
+ stream: args.stream,
706
+ emit: args.emit,
707
+ onTextChunk: args.onTextChunk,
708
+ telemetry: args.telemetry,
709
+ nodeId: ctx.node.graph.node,
710
+ emitEvent: ctx.node.emit,
711
+ reasonTrace: ctx.reasonTrace,
712
+ id: meta.id,
713
+ opId: meta.opId
714
+ });
699
715
  } catch (error) {
700
- return {
701
- ok: false,
702
- error: error instanceof Error ? error.message : String(error)
703
- };
716
+ const usage = error?.usage;
717
+ if (usage) accumulateTokenUsage(usage);
718
+ throw error;
704
719
  }
705
- };
706
- var extractJsonCodeBlock = (value) => {
707
- const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
708
- if (!match || typeof match[1] !== "string") return null;
709
- const inner = match[1].trim();
710
- return inner.length > 0 ? inner : null;
711
- };
712
- var tryStringify = (value) => {
713
- if (typeof value === "string") return value;
720
+ }
721
+ var import_zod22 = require("zod");
722
+ function inferOutputFormat(schema, explicit) {
723
+ if (explicit) return { responseFormat: explicit };
724
+ if (!schema) return {};
714
725
  try {
715
- return JSON.stringify(value, null, 2);
726
+ const json = (0, import_zod22.toJSONSchema)(schema);
727
+ const clean = (value) => {
728
+ if (Array.isArray(value)) return value.map(clean);
729
+ if (!value || typeof value !== "object") return value;
730
+ const object = value;
731
+ if ([
732
+ "allOf",
733
+ "not",
734
+ "dependentRequired",
735
+ "dependentSchemas",
736
+ "if",
737
+ "then",
738
+ "else",
739
+ "prefixItems"
740
+ ].some((key) => key in object))
741
+ throw new Error(
742
+ "Schema composition requires an explicit provider schema"
743
+ );
744
+ if (object.type === "object") {
745
+ if (object.additionalProperties !== false)
746
+ throw new Error(
747
+ "Dynamic properties require an explicit provider schema"
748
+ );
749
+ const properties = object.properties;
750
+ const required = object.required;
751
+ if (properties && Object.keys(properties).some((key) => !required?.includes(key))) {
752
+ throw new Error(
753
+ "Optional properties require an explicit provider schema"
754
+ );
755
+ }
756
+ }
757
+ return Object.fromEntries(
758
+ Object.entries(object).filter(([key]) => key !== "$schema" && key !== "default").map(([key, child]) => {
759
+ if ((key === "properties" || key === "$defs" || key === "definitions") && child && typeof child === "object")
760
+ return [
761
+ key,
762
+ Object.fromEntries(
763
+ Object.entries(child).map(([name, definition]) => [
764
+ name,
765
+ clean(definition)
766
+ ])
767
+ )
768
+ ];
769
+ return [
770
+ key,
771
+ key === "enum" || key === "const" ? child : clean(child)
772
+ ];
773
+ })
774
+ );
775
+ };
776
+ if (json.type !== "object")
777
+ throw new Error("The provider schema must have an object root");
778
+ return { responseFormat: { type: "json", schema: clean(json) } };
716
779
  } catch {
717
- return String(value);
780
+ return {
781
+ responseFormat: { type: "json" },
782
+ warnings: [
783
+ {
784
+ type: "compatibility",
785
+ feature: "outputSchema",
786
+ details: "This validator cannot be inferred as a strict provider JSON schema. Kortyx still validates the final output locally. Supply responseFormat.schema to enforce a wire schema."
787
+ }
788
+ ]
789
+ };
718
790
  }
719
- };
720
- var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
721
- var resolveOutputCandidate = (text) => {
722
- const trimmed = text.trim();
723
- if (!trimmed) return text;
724
- const direct = tryJsonParse(trimmed);
725
- if (direct.ok) return direct.value;
726
- const block = extractJsonCodeBlock(trimmed);
727
- if (block) {
728
- const parsedBlock = tryJsonParse(block);
729
- if (parsedBlock.ok) return parsedBlock.value;
791
+ }
792
+ var resolveAppendFieldPaths = (cfg) => {
793
+ const fields = cfg?.fields;
794
+ if (!fields) return [];
795
+ const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
796
+ for (const path of appendPaths) {
797
+ assertStructuredPath(path, "useReason structured append");
730
798
  }
731
- return text;
732
- };
733
- var looksLikeStructuredJsonAttempt = (value) => {
734
- const trimmed = value.trim();
735
- if (!trimmed) return false;
736
- return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
799
+ return appendPaths;
737
800
  };
738
- var isLikelyTruncatedStructuredOutput = (text) => {
739
- const trimmed = text.trim();
740
- if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
741
- const direct = tryJsonParse(trimmed);
742
- if (!direct.ok && direct.error) return true;
743
- const block = extractJsonCodeBlock(trimmed);
744
- if (!block) return false;
745
- const parsedBlock = tryJsonParse(block);
746
- return !parsedBlock.ok && Boolean(parsedBlock.error);
801
+ var resolveSetFieldPaths = (cfg) => {
802
+ const fields = cfg?.fields;
803
+ if (!fields) return [];
804
+ const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
805
+ for (const path of setPaths) {
806
+ assertStructuredPath(path, "useReason structured set");
807
+ }
808
+ return setPaths;
747
809
  };
748
- var createTruncatedStructuredOutputError = (label) => new Error(
749
- `${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
750
- );
751
- var createInvalidStructuredOutputError = (label) => new Error(
752
- `${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. This often happens when output is truncated or the provider does not follow JSON mode. Increase maxOutputTokens or simplify the requested output.`
753
- );
754
- var parseReasonOutputWithSchema = (args) => {
755
- const candidate = resolveOutputCandidate(args.text);
756
- try {
757
- return parseWithSchema(args.schema, candidate, args.label);
758
- } catch (error) {
759
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
760
- throw createTruncatedStructuredOutputError(args.label);
761
- }
762
- if (typeof candidate === "string" && candidate.trim().length > 0) {
763
- throw createInvalidStructuredOutputError(args.label);
764
- }
765
- throw error;
810
+ var resolveTextDeltaFieldPaths = (cfg) => {
811
+ const fields = cfg?.fields;
812
+ if (!fields) return [];
813
+ const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
814
+ for (const path of textPaths) {
815
+ assertStructuredPath(path, "useReason structured text-delta");
766
816
  }
817
+ return textPaths;
767
818
  };
768
- var parseInterruptFirstPassResult = (args) => {
769
- const candidate = resolveOutputCandidate(args.text);
770
- if (!isRecord22(candidate)) {
771
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
772
- throw createTruncatedStructuredOutputError("useReason output");
819
+ var extractCompletedStringValueAt = (args) => {
820
+ let value = "";
821
+ for (let i = args.start; i < args.text.length; i += 1) {
822
+ const ch = args.text[i];
823
+ if (ch === void 0) break;
824
+ if (ch === '"') {
825
+ return {
826
+ complete: true,
827
+ value
828
+ };
773
829
  }
774
- if (typeof candidate === "string" && candidate.trim().length > 0) {
775
- throw createInvalidStructuredOutputError("useReason output");
830
+ if (ch !== "\\") {
831
+ value += ch;
832
+ continue;
776
833
  }
777
- throw new Error(
778
- "useReason first pass with interrupt must return a JSON object."
779
- );
780
- }
781
- const mode = args.mode ?? "required";
782
- let interruptRequired = true;
783
- if (mode === "optional") {
784
- if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
785
- throw new Error(
786
- 'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
787
- );
834
+ const next = args.text[i + 1];
835
+ if (!next) {
836
+ return {
837
+ complete: false
838
+ };
788
839
  }
789
- interruptRequired = candidate.decision === "interrupt";
790
- if (!interruptRequired) {
791
- const hasUnexpectedRequest = [
792
- candidate.interruptRequest,
793
- candidate.request,
794
- candidate.interrupt
795
- ].some((value) => value !== null && value !== void 0);
796
- if (hasUnexpectedRequest) {
797
- throw new Error(
798
- 'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
799
- );
800
- }
840
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
841
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
842
+ i += 1;
843
+ continue;
801
844
  }
802
- }
803
- let request;
804
- if (interruptRequired) {
805
- const requestCandidates = [
806
- candidate.interruptRequest,
807
- candidate.request,
808
- candidate.interrupt,
809
- mode === "required" ? candidate : void 0
810
- ];
811
- const requestPayload = requestCandidates.find(
812
- (value) => value !== null && value !== void 0
813
- );
814
- request = parseWithSchema(
815
- args.requestSchema,
816
- requestPayload,
817
- "useReason interrupt.request"
818
- );
819
- }
820
- let output;
821
- if (args.outputSchema) {
822
- const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
823
- try {
824
- output = parseWithSchema(
825
- args.outputSchema,
826
- outputPayload,
827
- "useReason output"
828
- );
829
- } catch (error) {
830
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
831
- throw createTruncatedStructuredOutputError("useReason output");
832
- }
833
- if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
834
- throw createInvalidStructuredOutputError("useReason output");
845
+ if (next === "u") {
846
+ const hex = args.text.slice(i + 2, i + 6);
847
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
848
+ return {
849
+ complete: false
850
+ };
835
851
  }
836
- throw error;
852
+ value += String.fromCharCode(Number.parseInt(hex, 16));
853
+ i += 5;
854
+ continue;
837
855
  }
838
- }
839
- const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
840
- if (!args.outputSchema && !explicitDraft) {
841
- throw new Error(
842
- "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
843
- );
844
- }
845
- const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
846
- if (!interruptRequired) {
847
856
  return {
848
- draftText,
849
- interruptRequired: false,
850
- ...output !== void 0 ? { output } : {}
857
+ complete: false
851
858
  };
852
859
  }
853
860
  return {
854
- draftText,
855
- interruptRequired: true,
856
- request,
857
- ...output !== void 0 ? { output } : {}
861
+ complete: false
858
862
  };
859
863
  };
860
- var import_zod22 = require("zod");
861
- var tryStringify2 = (value) => {
862
- if (typeof value === "string") return value;
863
- try {
864
- return JSON.stringify(value, null, 2);
865
- } catch {
866
- return String(value);
867
- }
868
- };
869
- var tryGetSchemaHint = (schema) => {
870
- try {
871
- const hint = (0, import_zod22.toJSONSchema)(schema);
872
- return JSON.stringify(hint, null, 2);
873
- } catch {
874
- return void 0;
875
- }
876
- };
877
- var withSchemaHint = (input, label, schema) => {
878
- const hint = tryGetSchemaHint(schema);
879
- if (!hint) return input;
880
- return `${input}
881
-
882
- ${label}:
883
- ${hint}`;
884
- };
885
- var defaultInterruptFirstPassInput = (args) => {
886
- const mode = args.mode ?? "required";
887
- const base = mode === "optional" ? args.outputSchema ? [
888
- args.input,
889
- "Output rules:",
890
- "- Return JSON only. No markdown fences.",
891
- "- Return an object with keys: decision, output, interruptRequest, draftText.",
892
- '- decision must be either "continue" or "interrupt".',
893
- '- Use decision "continue" when you can produce the final output without asking the user.',
894
- '- Use decision "interrupt" only when user input is needed before finalizing.',
895
- "- output must match the expected output JSON schema.",
896
- '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
897
- '- When decision is "continue", interruptRequest must be null.',
898
- "- draftText should be a concise plain-language summary of output."
899
- ].join("\n\n") : [
900
- args.input,
901
- "Output rules:",
902
- "- Return JSON only. No markdown fences.",
903
- "- Return an object with keys: decision, draftText, interruptRequest.",
904
- '- decision must be either "continue" or "interrupt".',
905
- '- Use decision "continue" when you can produce the final answer without asking the user.',
906
- '- Use decision "interrupt" only when user input is needed before finalizing.',
907
- "- draftText should be the current answer text.",
908
- '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
909
- '- When decision is "continue", interruptRequest must be null.'
910
- ].join("\n\n") : args.outputSchema ? [
911
- args.input,
912
- "Output rules:",
913
- "- Return JSON only. No markdown fences.",
914
- "- Return an object with keys: output, interruptRequest, draftText.",
915
- "- output must match the expected output JSON schema.",
916
- "- interruptRequest must match the expected interrupt request JSON schema.",
917
- "- interruptRequest must be a non-null object.",
918
- "- draftText should be a concise plain-language summary of output.",
919
- "- If uncertain, still return a valid interruptRequest with safe default options."
920
- ].join("\n\n") : [
921
- args.input,
922
- "Output rules:",
923
- "- Return JSON only. No markdown fences.",
924
- "- Return an object with keys: draftText, interruptRequest.",
925
- "- draftText should be the current draft answer text.",
926
- "- interruptRequest must match the expected interrupt request JSON schema.",
927
- "- interruptRequest must be a non-null object.",
928
- "- If uncertain, still return a valid interruptRequest with safe default options."
929
- ].join("\n\n");
930
- const withInterruptSchema = withSchemaHint(
931
- base,
932
- "Expected interrupt request JSON schema (for `interruptRequest`)",
933
- args.requestSchema
934
- );
935
- if (!args.outputSchema) return withInterruptSchema;
936
- const withOutputSchema = withSchemaHint(
937
- withInterruptSchema,
938
- "Expected output JSON schema (for `output`)",
939
- args.outputSchema
940
- );
941
- return [
942
- withOutputSchema,
943
- "JSON template:",
944
- mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
945
- ].join("\n\n");
946
- };
947
- var defaultContinuationInput = (args) => [
948
- "You are continuing a paused reasoning task after user input.",
949
- "Update the previous result using the user response and return the final answer only.",
950
- "If the previous output was JSON, return JSON in the same schema.",
951
- "",
952
- `Original request:
953
- ${args.input}`,
954
- `Draft answer text:
955
- ${args.draftText}`,
956
- `Draft output object:
957
- ${tryStringify2(args.draftOutput ?? args.draftText)}`,
958
- `Interrupt request shown to user:
959
- ${tryStringify2(args.request)}`,
960
- `User response:
961
- ${tryStringify2(args.response)}`
962
- ].join("\n\n");
963
- var withOutputGuardrails = (input, schema) => {
964
- const base = [
965
- input,
966
- "Output rules:",
967
- "- Return only JSON, no markdown fences.",
968
- "- Ensure the response conforms to the expected output schema.",
969
- "- Do not include explanatory text outside the JSON object."
970
- ].join("\n\n");
971
- return withSchemaHint(base, "Expected output JSON schema", schema);
972
- };
973
- var withStructuredStreamHints = (input, args) => {
974
- const rules = [];
975
- for (const path of args.setFieldPaths ?? []) {
976
- rules.push(
977
- `- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
978
- );
979
- }
980
- for (const path of args.appendFieldPaths ?? []) {
981
- rules.push(
982
- `- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
983
- );
984
- rules.push(
985
- `- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
986
- );
987
- }
988
- for (const path of args.textDeltaFieldPaths ?? []) {
989
- rules.push(
990
- `- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
991
- );
992
- rules.push(
993
- `- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
994
- );
995
- }
996
- if (rules.length === 0) return input;
997
- return [
998
- input,
999
- "Streaming rules:",
1000
- ...rules,
1001
- "- Keep the overall response as valid JSON."
1002
- ].join("\n\n");
1003
- };
1004
- var mergeUsage = (left, right) => {
1005
- if (!left) return right;
1006
- if (!right) return left;
1007
- const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
1008
- const raw = left.raw || right.raw ? {
1009
- ...left.raw ?? {},
1010
- ...right.raw ?? {}
1011
- } : void 0;
1012
- const input = sum(left.input, right.input);
1013
- const output = sum(left.output, right.output);
1014
- const total = sum(left.total, right.total);
1015
- const reasoning = sum(left.reasoning, right.reasoning);
1016
- const cacheRead = sum(left.cacheRead, right.cacheRead);
1017
- const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
1018
- const merged = {};
1019
- if (input !== void 0) merged.input = input;
1020
- if (output !== void 0) merged.output = output;
1021
- if (total !== void 0) merged.total = total;
1022
- if (reasoning !== void 0) merged.reasoning = reasoning;
1023
- if (cacheRead !== void 0) merged.cacheRead = cacheRead;
1024
- if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
1025
- if (raw) merged.raw = raw;
1026
- return merged;
1027
- };
1028
- var mergeWarnings = (left, right) => {
1029
- if (!left?.length) return right;
1030
- if (!right?.length) return left;
1031
- const seen = /* @__PURE__ */ new Set();
1032
- const merged = [];
1033
- for (const warning of [...left, ...right]) {
1034
- const key = JSON.stringify(warning);
1035
- if (seen.has(key)) continue;
1036
- seen.add(key);
1037
- merged.push(warning);
1038
- }
1039
- return merged;
1040
- };
1041
- var mergeProviderMetadata = (left, right) => {
1042
- if (!left) return right;
1043
- if (!right) return left;
1044
- return {
1045
- ...left,
1046
- ...right
1047
- };
1048
- };
1049
- var emitReasonStructuredOutput = (args) => {
1050
- if (!args.emit || !shouldEmitStructured(args.structured)) return;
1051
- emitStructuredData({
1052
- dataType: args.structured?.dataType ?? "reason-output",
1053
- data: args.output,
1054
- kind: "final",
1055
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1056
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1057
- ...args.id ? { id: args.id } : {},
1058
- streamId: args.opId
1059
- });
1060
- };
1061
- var resolveAppendFieldPaths = (cfg) => {
1062
- const fields = cfg?.fields;
1063
- if (!fields) return [];
1064
- const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
1065
- for (const path of appendPaths) {
1066
- assertStructuredPath(path, "useReason structured append");
1067
- }
1068
- return appendPaths;
1069
- };
1070
- var resolveSetFieldPaths = (cfg) => {
1071
- const fields = cfg?.fields;
1072
- if (!fields) return [];
1073
- const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
1074
- for (const path of setPaths) {
1075
- assertStructuredPath(path, "useReason structured set");
1076
- }
1077
- return setPaths;
1078
- };
1079
- var resolveTextDeltaFieldPaths = (cfg) => {
1080
- const fields = cfg?.fields;
1081
- if (!fields) return [];
1082
- const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
1083
- for (const path of textPaths) {
1084
- assertStructuredPath(path, "useReason structured text-delta");
1085
- }
1086
- return textPaths;
1087
- };
1088
- var extractCompletedStringValueAt = (args) => {
1089
- let value = "";
1090
- for (let i = args.start; i < args.text.length; i += 1) {
1091
- const ch = args.text[i];
1092
- if (ch === void 0) break;
1093
- if (ch === '"') {
1094
- return {
1095
- complete: true,
1096
- value
1097
- };
1098
- }
1099
- if (ch !== "\\") {
1100
- value += ch;
1101
- continue;
1102
- }
1103
- const next = args.text[i + 1];
1104
- if (!next) {
1105
- return {
1106
- complete: false
1107
- };
1108
- }
1109
- if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1110
- value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1111
- i += 1;
1112
- continue;
1113
- }
1114
- if (next === "u") {
1115
- const hex = args.text.slice(i + 2, i + 6);
1116
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1117
- return {
1118
- complete: false
1119
- };
1120
- }
1121
- value += String.fromCharCode(Number.parseInt(hex, 16));
1122
- i += 5;
1123
- continue;
1124
- }
1125
- return {
1126
- complete: false
1127
- };
1128
- }
1129
- return {
1130
- complete: false
1131
- };
1132
- };
1133
- var skipWhitespace = (text, start) => {
1134
- let i = start;
1135
- while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
1136
- return i;
864
+ var skipWhitespace = (text, start) => {
865
+ let i = start;
866
+ while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
867
+ return i;
1137
868
  };
1138
869
  var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
1139
870
  var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
@@ -1517,103 +1248,552 @@ ${tryStringify2(args.response)}`
1517
1248
  }
1518
1249
  return items;
1519
1250
  };
1520
- var extractCompletedArrayItemGroups = (args) => {
1521
- return locateValueStarts(args).flatMap((match) => {
1522
- const items = extractCompletedArrayItems({
1523
- text: args.text,
1524
- path: match.path
1525
- });
1526
- return items.length > 0 ? [
1527
- {
1528
- path: match.path,
1529
- items
1530
- }
1531
- ] : [];
1532
- });
1251
+ var extractCompletedArrayItemGroups = (args) => {
1252
+ return locateValueStarts(args).flatMap((match) => {
1253
+ const items = extractCompletedArrayItems({
1254
+ text: args.text,
1255
+ path: match.path
1256
+ });
1257
+ return items.length > 0 ? [
1258
+ {
1259
+ path: match.path,
1260
+ items
1261
+ }
1262
+ ] : [];
1263
+ });
1264
+ };
1265
+ var extractStreamingStringValue = (args) => {
1266
+ const located = locateValueStart(args);
1267
+ if (!located.found || located.start === void 0) {
1268
+ return {
1269
+ found: located.found,
1270
+ complete: false,
1271
+ value: ""
1272
+ };
1273
+ }
1274
+ const stringStart = skipWhitespace(args.text, located.start);
1275
+ if (args.text[stringStart] !== '"') {
1276
+ return {
1277
+ found: true,
1278
+ complete: false,
1279
+ value: ""
1280
+ };
1281
+ }
1282
+ let value = "";
1283
+ for (let i = stringStart + 1; i < args.text.length; i += 1) {
1284
+ const ch = args.text[i];
1285
+ if (ch === void 0) break;
1286
+ if (ch === '"') {
1287
+ return {
1288
+ found: true,
1289
+ complete: true,
1290
+ value
1291
+ };
1292
+ }
1293
+ if (ch !== "\\") {
1294
+ value += ch;
1295
+ continue;
1296
+ }
1297
+ const next = args.text[i + 1];
1298
+ if (!next) {
1299
+ return {
1300
+ found: true,
1301
+ complete: false,
1302
+ value
1303
+ };
1304
+ }
1305
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1306
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1307
+ i += 1;
1308
+ continue;
1309
+ }
1310
+ if (next === "u") {
1311
+ const hex = args.text.slice(i + 2, i + 6);
1312
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1313
+ return {
1314
+ found: true,
1315
+ complete: false,
1316
+ value
1317
+ };
1318
+ }
1319
+ value += String.fromCharCode(Number.parseInt(hex, 16));
1320
+ i += 5;
1321
+ continue;
1322
+ }
1323
+ return {
1324
+ found: true,
1325
+ complete: false,
1326
+ value
1327
+ };
1328
+ }
1329
+ return {
1330
+ found: true,
1331
+ complete: false,
1332
+ value
1333
+ };
1334
+ };
1335
+ var extractStreamingStringValues = (args) => {
1336
+ return locateValueStarts(args).flatMap((match) => {
1337
+ const current = extractStreamingStringValue({
1338
+ text: args.text,
1339
+ path: match.path
1340
+ });
1341
+ return current.found ? [
1342
+ {
1343
+ path: match.path,
1344
+ complete: current.complete,
1345
+ value: current.value
1346
+ }
1347
+ ] : [];
1348
+ });
1349
+ };
1350
+ function createStructuredOutputStreamer(args, id, opId) {
1351
+ let firstText = "";
1352
+ const setFieldPaths = resolveSetFieldPaths(args.structured);
1353
+ const appendFieldPaths = resolveAppendFieldPaths(args.structured);
1354
+ const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
1355
+ const emittedSetValues = /* @__PURE__ */ new Map();
1356
+ const emittedAppendCounts = /* @__PURE__ */ new Map();
1357
+ const emittedTextValues = /* @__PURE__ */ new Map();
1358
+ return (delta) => {
1359
+ firstText += delta;
1360
+ for (const fieldPath of setFieldPaths) {
1361
+ const values = extractCompletedFieldValues({
1362
+ text: firstText,
1363
+ path: fieldPath
1364
+ });
1365
+ for (const { path, value } of values) {
1366
+ const nextSerialized = JSON.stringify(value);
1367
+ if (emittedSetValues.get(path) === nextSerialized) continue;
1368
+ emittedSetValues.set(path, nextSerialized);
1369
+ emitStructuredData({
1370
+ kind: "set",
1371
+ path,
1372
+ value,
1373
+ dataType: args.structured?.dataType ?? "reason-output",
1374
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1375
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1376
+ ...id ? { id } : {},
1377
+ streamId: opId
1378
+ });
1379
+ }
1380
+ }
1381
+ for (const fieldPath of appendFieldPaths) {
1382
+ const groups = extractCompletedArrayItemGroups({
1383
+ text: firstText,
1384
+ path: fieldPath
1385
+ });
1386
+ for (const { path, items } of groups) {
1387
+ const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
1388
+ if (items.length <= emittedAppendCount) continue;
1389
+ const nextItems = items.slice(emittedAppendCount);
1390
+ emittedAppendCounts.set(path, items.length);
1391
+ emitStructuredData({
1392
+ kind: "append",
1393
+ path,
1394
+ items: nextItems,
1395
+ dataType: args.structured?.dataType ?? "reason-output",
1396
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1397
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1398
+ ...id ? { id } : {},
1399
+ streamId: opId
1400
+ });
1401
+ }
1402
+ }
1403
+ for (const fieldPath of textDeltaFieldPaths) {
1404
+ const values = extractStreamingStringValues({
1405
+ text: firstText,
1406
+ path: fieldPath
1407
+ });
1408
+ for (const { path, value } of values) {
1409
+ const emittedTextValue = emittedTextValues.get(path) ?? "";
1410
+ if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
1411
+ continue;
1412
+ }
1413
+ const nextDelta = value.slice(emittedTextValue.length);
1414
+ emittedTextValues.set(path, value);
1415
+ if (nextDelta.length === 0) continue;
1416
+ emitStructuredData({
1417
+ kind: "text-delta",
1418
+ path,
1419
+ delta: nextDelta,
1420
+ dataType: args.structured?.dataType ?? "reason-output",
1421
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1422
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1423
+ ...id ? { id } : {},
1424
+ streamId: opId
1425
+ });
1426
+ }
1427
+ }
1428
+ };
1429
+ }
1430
+ var tryJsonParse = (value) => {
1431
+ try {
1432
+ return { ok: true, value: JSON.parse(value) };
1433
+ } catch (error) {
1434
+ return {
1435
+ ok: false,
1436
+ error: error instanceof Error ? error.message : String(error)
1437
+ };
1438
+ }
1439
+ };
1440
+ var extractJsonCodeBlock = (value) => {
1441
+ const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
1442
+ if (!match || typeof match[1] !== "string") return null;
1443
+ const inner = match[1].trim();
1444
+ return inner.length > 0 ? inner : null;
1445
+ };
1446
+ var tryStringify = (value) => {
1447
+ if (typeof value === "string") return value;
1448
+ try {
1449
+ return JSON.stringify(value, null, 2);
1450
+ } catch {
1451
+ return String(value);
1452
+ }
1453
+ };
1454
+ var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1455
+ var resolveOutputCandidate = (text) => {
1456
+ const trimmed = text.trim();
1457
+ if (!trimmed) return text;
1458
+ const direct = tryJsonParse(trimmed);
1459
+ if (direct.ok) return direct.value;
1460
+ const block = extractJsonCodeBlock(trimmed);
1461
+ if (block) {
1462
+ const parsedBlock = tryJsonParse(block);
1463
+ if (parsedBlock.ok) return parsedBlock.value;
1464
+ }
1465
+ return text;
1466
+ };
1467
+ var looksLikeStructuredJsonAttempt = (value) => {
1468
+ const trimmed = value.trim();
1469
+ if (!trimmed) return false;
1470
+ return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
1471
+ };
1472
+ var isLikelyTruncatedStructuredOutput = (text) => {
1473
+ const trimmed = text.trim();
1474
+ if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
1475
+ const direct = tryJsonParse(trimmed);
1476
+ if (!direct.ok && direct.error) return true;
1477
+ const block = extractJsonCodeBlock(trimmed);
1478
+ if (!block) return false;
1479
+ const parsedBlock = tryJsonParse(block);
1480
+ return !parsedBlock.ok && Boolean(parsedBlock.error);
1481
+ };
1482
+ var createTruncatedStructuredOutputError = (label) => new Error(
1483
+ `${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
1484
+ );
1485
+ var createInvalidStructuredOutputError = (label) => new Error(
1486
+ `${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. This often happens when output is truncated or the provider does not follow JSON mode. Increase maxOutputTokens or simplify the requested output.`
1487
+ );
1488
+ var parseReasonOutputWithSchema = (args) => {
1489
+ const candidate = resolveOutputCandidate(args.text);
1490
+ try {
1491
+ return parseWithSchema(args.schema, candidate, args.label);
1492
+ } catch (error) {
1493
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1494
+ throw createTruncatedStructuredOutputError(args.label);
1495
+ }
1496
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
1497
+ throw createInvalidStructuredOutputError(args.label);
1498
+ }
1499
+ throw error;
1500
+ }
1501
+ };
1502
+ var parseInterruptFirstPassResult = (args) => {
1503
+ const candidate = resolveOutputCandidate(args.text);
1504
+ if (!isRecord22(candidate)) {
1505
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1506
+ throw createTruncatedStructuredOutputError("useReason output");
1507
+ }
1508
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
1509
+ throw createInvalidStructuredOutputError("useReason output");
1510
+ }
1511
+ throw new Error(
1512
+ "useReason first pass with interrupt must return a JSON object."
1513
+ );
1514
+ }
1515
+ const mode = args.mode ?? "required";
1516
+ let interruptRequired = true;
1517
+ if (mode === "optional") {
1518
+ if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
1519
+ throw new Error(
1520
+ 'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
1521
+ );
1522
+ }
1523
+ interruptRequired = candidate.decision === "interrupt";
1524
+ if (!interruptRequired) {
1525
+ const hasUnexpectedRequest = [
1526
+ candidate.interruptRequest,
1527
+ candidate.request,
1528
+ candidate.interrupt
1529
+ ].some((value) => value !== null && value !== void 0);
1530
+ if (hasUnexpectedRequest) {
1531
+ throw new Error(
1532
+ 'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
1533
+ );
1534
+ }
1535
+ }
1536
+ }
1537
+ let request;
1538
+ if (interruptRequired) {
1539
+ const requestCandidates = [
1540
+ candidate.interruptRequest,
1541
+ candidate.request,
1542
+ candidate.interrupt,
1543
+ mode === "required" ? candidate : void 0
1544
+ ];
1545
+ const requestPayload = requestCandidates.find(
1546
+ (value) => value !== null && value !== void 0
1547
+ );
1548
+ request = parseWithSchema(
1549
+ args.requestSchema,
1550
+ requestPayload,
1551
+ "useReason interrupt.request"
1552
+ );
1553
+ }
1554
+ let output;
1555
+ if (args.outputSchema) {
1556
+ const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
1557
+ try {
1558
+ output = parseWithSchema(
1559
+ args.outputSchema,
1560
+ outputPayload,
1561
+ "useReason output"
1562
+ );
1563
+ } catch (error) {
1564
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1565
+ throw createTruncatedStructuredOutputError("useReason output");
1566
+ }
1567
+ if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
1568
+ throw createInvalidStructuredOutputError("useReason output");
1569
+ }
1570
+ throw error;
1571
+ }
1572
+ }
1573
+ const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
1574
+ if (!args.outputSchema && !explicitDraft) {
1575
+ throw new Error(
1576
+ "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
1577
+ );
1578
+ }
1579
+ const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
1580
+ if (!interruptRequired) {
1581
+ return {
1582
+ draftText,
1583
+ interruptRequired: false,
1584
+ ...output !== void 0 ? { output } : {}
1585
+ };
1586
+ }
1587
+ return {
1588
+ draftText,
1589
+ interruptRequired: true,
1590
+ request,
1591
+ ...output !== void 0 ? { output } : {}
1592
+ };
1593
+ };
1594
+ var import_zod32 = require("zod");
1595
+ var tryStringify2 = (value) => {
1596
+ if (typeof value === "string") return value;
1597
+ try {
1598
+ return JSON.stringify(value, null, 2);
1599
+ } catch {
1600
+ return String(value);
1601
+ }
1602
+ };
1603
+ var tryGetSchemaHint = (schema) => {
1604
+ try {
1605
+ const hint = (0, import_zod32.toJSONSchema)(schema);
1606
+ return JSON.stringify(hint, null, 2);
1607
+ } catch {
1608
+ return void 0;
1609
+ }
1610
+ };
1611
+ var withSchemaHint = (input, label, schema) => {
1612
+ const hint = tryGetSchemaHint(schema);
1613
+ if (!hint) return input;
1614
+ return `${input}
1615
+
1616
+ ${label}:
1617
+ ${hint}`;
1618
+ };
1619
+ var defaultInterruptFirstPassInput = (args) => {
1620
+ const mode = args.mode ?? "required";
1621
+ const base = mode === "optional" ? args.outputSchema ? [
1622
+ args.input,
1623
+ "Output rules:",
1624
+ "- Return JSON only. No markdown fences.",
1625
+ "- Return an object with keys: decision, output, interruptRequest, draftText.",
1626
+ '- decision must be either "continue" or "interrupt".',
1627
+ '- Use decision "continue" when you can produce the final output without asking the user.',
1628
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
1629
+ "- output must match the expected output JSON schema.",
1630
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
1631
+ '- When decision is "continue", interruptRequest must be null.',
1632
+ "- draftText should be a concise plain-language summary of output."
1633
+ ].join("\n\n") : [
1634
+ args.input,
1635
+ "Output rules:",
1636
+ "- Return JSON only. No markdown fences.",
1637
+ "- Return an object with keys: decision, draftText, interruptRequest.",
1638
+ '- decision must be either "continue" or "interrupt".',
1639
+ '- Use decision "continue" when you can produce the final answer without asking the user.',
1640
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
1641
+ "- draftText should be the current answer text.",
1642
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
1643
+ '- When decision is "continue", interruptRequest must be null.'
1644
+ ].join("\n\n") : args.outputSchema ? [
1645
+ args.input,
1646
+ "Output rules:",
1647
+ "- Return JSON only. No markdown fences.",
1648
+ "- Return an object with keys: output, interruptRequest, draftText.",
1649
+ "- output must match the expected output JSON schema.",
1650
+ "- interruptRequest must match the expected interrupt request JSON schema.",
1651
+ "- interruptRequest must be a non-null object.",
1652
+ "- draftText should be a concise plain-language summary of output.",
1653
+ "- If uncertain, still return a valid interruptRequest with safe default options."
1654
+ ].join("\n\n") : [
1655
+ args.input,
1656
+ "Output rules:",
1657
+ "- Return JSON only. No markdown fences.",
1658
+ "- Return an object with keys: draftText, interruptRequest.",
1659
+ "- draftText should be the current draft answer text.",
1660
+ "- interruptRequest must match the expected interrupt request JSON schema.",
1661
+ "- interruptRequest must be a non-null object.",
1662
+ "- If uncertain, still return a valid interruptRequest with safe default options."
1663
+ ].join("\n\n");
1664
+ const withInterruptSchema = withSchemaHint(
1665
+ base,
1666
+ "Expected interrupt request JSON schema (for `interruptRequest`)",
1667
+ args.requestSchema
1668
+ );
1669
+ if (!args.outputSchema) return withInterruptSchema;
1670
+ const withOutputSchema = withSchemaHint(
1671
+ withInterruptSchema,
1672
+ "Expected output JSON schema (for `output`)",
1673
+ args.outputSchema
1674
+ );
1675
+ return [
1676
+ withOutputSchema,
1677
+ "JSON template:",
1678
+ mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
1679
+ ].join("\n\n");
1680
+ };
1681
+ var defaultContinuationInput = (args) => [
1682
+ "You are continuing a paused reasoning task after user input.",
1683
+ "Update the previous result using the user response and return the final answer only.",
1684
+ "If the previous output was JSON, return JSON in the same schema.",
1685
+ "",
1686
+ `Original request:
1687
+ ${args.input}`,
1688
+ `Draft answer text:
1689
+ ${args.draftText}`,
1690
+ `Draft output object:
1691
+ ${tryStringify2(args.draftOutput ?? args.draftText)}`,
1692
+ `Interrupt request shown to user:
1693
+ ${tryStringify2(args.request)}`,
1694
+ `User response:
1695
+ ${tryStringify2(args.response)}`
1696
+ ].join("\n\n");
1697
+ var withOutputGuardrails = (input, schema) => {
1698
+ const base = [
1699
+ input,
1700
+ "Output rules:",
1701
+ "- Return only JSON, no markdown fences.",
1702
+ "- Ensure the response conforms to the expected output schema.",
1703
+ "- Do not include explanatory text outside the JSON object."
1704
+ ].join("\n\n");
1705
+ return withSchemaHint(base, "Expected output JSON schema", schema);
1533
1706
  };
1534
- var extractStreamingStringValue = (args) => {
1535
- const located = locateValueStart(args);
1536
- if (!located.found || located.start === void 0) {
1537
- return {
1538
- found: located.found,
1539
- complete: false,
1540
- value: ""
1541
- };
1707
+ var withStructuredStreamHints = (input, args) => {
1708
+ const rules = [];
1709
+ for (const path of args.setFieldPaths ?? []) {
1710
+ rules.push(
1711
+ `- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
1712
+ );
1542
1713
  }
1543
- const stringStart = skipWhitespace(args.text, located.start);
1544
- if (args.text[stringStart] !== '"') {
1545
- return {
1546
- found: true,
1547
- complete: false,
1548
- value: ""
1549
- };
1714
+ for (const path of args.appendFieldPaths ?? []) {
1715
+ rules.push(
1716
+ `- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
1717
+ );
1718
+ rules.push(
1719
+ `- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
1720
+ );
1550
1721
  }
1551
- let value = "";
1552
- for (let i = stringStart + 1; i < args.text.length; i += 1) {
1553
- const ch = args.text[i];
1554
- if (ch === void 0) break;
1555
- if (ch === '"') {
1556
- return {
1557
- found: true,
1558
- complete: true,
1559
- value
1560
- };
1561
- }
1562
- if (ch !== "\\") {
1563
- value += ch;
1564
- continue;
1565
- }
1566
- const next = args.text[i + 1];
1567
- if (!next) {
1568
- return {
1569
- found: true,
1570
- complete: false,
1571
- value
1572
- };
1573
- }
1574
- if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1575
- value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1576
- i += 1;
1577
- continue;
1578
- }
1579
- if (next === "u") {
1580
- const hex = args.text.slice(i + 2, i + 6);
1581
- if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1582
- return {
1583
- found: true,
1584
- complete: false,
1585
- value
1586
- };
1587
- }
1588
- value += String.fromCharCode(Number.parseInt(hex, 16));
1589
- i += 5;
1590
- continue;
1591
- }
1592
- return {
1593
- found: true,
1594
- complete: false,
1595
- value
1596
- };
1722
+ for (const path of args.textDeltaFieldPaths ?? []) {
1723
+ rules.push(
1724
+ `- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
1725
+ );
1726
+ rules.push(
1727
+ `- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
1728
+ );
1729
+ }
1730
+ if (rules.length === 0) return input;
1731
+ return [
1732
+ input,
1733
+ "Streaming rules:",
1734
+ ...rules,
1735
+ "- Keep the overall response as valid JSON."
1736
+ ].join("\n\n");
1737
+ };
1738
+ var mergeUsage = (left, right) => {
1739
+ if (!left) return right;
1740
+ if (!right) return left;
1741
+ const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
1742
+ const raw = left.raw || right.raw ? {
1743
+ ...left.raw ?? {},
1744
+ ...right.raw ?? {}
1745
+ } : void 0;
1746
+ const input = sum(left.input, right.input);
1747
+ const output = sum(left.output, right.output);
1748
+ const total = sum(left.total, right.total);
1749
+ const reasoning = sum(left.reasoning, right.reasoning);
1750
+ const cacheRead = sum(left.cacheRead, right.cacheRead);
1751
+ const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
1752
+ const merged = {
1753
+ ...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
1754
+ ...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
1755
+ ...right.inputIncludesCacheRead !== void 0 ? { inputIncludesCacheRead: right.inputIncludesCacheRead } : {}
1756
+ };
1757
+ if (input !== void 0) merged.input = input;
1758
+ if (output !== void 0) merged.output = output;
1759
+ if (total !== void 0) merged.total = total;
1760
+ if (reasoning !== void 0) merged.reasoning = reasoning;
1761
+ if (cacheRead !== void 0) merged.cacheRead = cacheRead;
1762
+ if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
1763
+ if (raw) merged.raw = raw;
1764
+ return merged;
1765
+ };
1766
+ var mergeWarnings = (left, right) => {
1767
+ if (!left?.length) return right;
1768
+ if (!right?.length) return left;
1769
+ const seen = /* @__PURE__ */ new Set();
1770
+ const merged = [];
1771
+ for (const warning of [...left, ...right]) {
1772
+ const key = JSON.stringify(warning);
1773
+ if (seen.has(key)) continue;
1774
+ seen.add(key);
1775
+ merged.push(warning);
1597
1776
  }
1777
+ return merged;
1778
+ };
1779
+ var mergeProviderMetadata = (left, right) => {
1780
+ if (!left) return right;
1781
+ if (!right) return left;
1598
1782
  return {
1599
- found: true,
1600
- complete: false,
1601
- value
1783
+ ...left,
1784
+ ...right
1602
1785
  };
1603
1786
  };
1604
- var extractStreamingStringValues = (args) => {
1605
- return locateValueStarts(args).flatMap((match) => {
1606
- const current = extractStreamingStringValue({
1607
- text: args.text,
1608
- path: match.path
1609
- });
1610
- return current.found ? [
1611
- {
1612
- path: match.path,
1613
- complete: current.complete,
1614
- value: current.value
1615
- }
1616
- ] : [];
1787
+ var emitReasonStructuredOutput = (args) => {
1788
+ if (!args.emit || !shouldEmitStructured(args.structured)) return;
1789
+ emitStructuredData({
1790
+ dataType: args.structured?.dataType ?? "reason-output",
1791
+ data: args.output,
1792
+ kind: "final",
1793
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1794
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1795
+ ...args.id ? { id: args.id } : {},
1796
+ streamId: args.opId
1617
1797
  });
1618
1798
  };
1619
1799
  var import_core42 = require("@kortyx/core");
@@ -1668,13 +1848,16 @@ ${tryStringify2(args.response)}`
1668
1848
  { role: "user", content: String(args.input ?? "") }
1669
1849
  ];
1670
1850
  var runReasonToolLoop = async (args) => {
1671
- const { useReasonArgs, id, opId, traceSpan } = args;
1851
+ const { useReasonArgs, id, traceSpan, checkpointKey } = args;
1672
1852
  const ctx = getHookContext();
1673
1853
  const abortSignal = (0, import_core42.combineAbortSignals)(
1674
1854
  ctx.node.abortSignal,
1675
1855
  useReasonArgs.abortSignal,
1676
1856
  useReasonArgs.model.options?.abortSignal
1677
1857
  );
1858
+ const saved = ctx.currentNodeState.byKey[checkpointKey];
1859
+ const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
1860
+ const opId = checkpoint?.opId ?? args.opId;
1678
1861
  const tools = useReasonArgs.tools ?? [];
1679
1862
  const toolByName = /* @__PURE__ */ new Map();
1680
1863
  let validationCompleted = false;
@@ -1700,20 +1883,43 @@ ${tryStringify2(args.response)}`
1700
1883
  }
1701
1884
  const toolDefinitions = toToolDefinitions(tools);
1702
1885
  const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
1703
- const messages = createInitialMessages({
1886
+ const messages = checkpoint?.messages ?? createInitialMessages({
1704
1887
  system: useReasonArgs.system,
1705
- input: useReasonArgs.input
1888
+ input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
1706
1889
  });
1707
- const steps = [];
1708
- const allToolCalls = [];
1709
- const allToolResults = [];
1890
+ const steps = checkpoint?.steps ?? [];
1891
+ const allToolCalls = steps.flatMap(
1892
+ (step) => step.toolCalls
1893
+ );
1894
+ const allToolResults = steps.flatMap(
1895
+ (step) => step.toolResults
1896
+ );
1710
1897
  let finalText = "";
1711
1898
  let finalRaw;
1712
1899
  let finalOutput;
1713
- let aggregatedUsage;
1900
+ let aggregatedUsage = steps.reduce(
1901
+ (usage, step) => mergeUsage(usage, step.usage),
1902
+ void 0
1903
+ );
1714
1904
  let finalFinishReason;
1715
1905
  let aggregatedProviderMetadata;
1716
- let aggregatedWarnings;
1906
+ let aggregatedWarnings = steps.reduce(
1907
+ (warnings, step) => mergeWarnings(warnings, step.warnings),
1908
+ args.initialWarnings
1909
+ );
1910
+ let pending = checkpoint?.pending;
1911
+ const approvedCalls = checkpoint?.approvedCalls ?? [];
1912
+ const save = () => {
1913
+ ctx.currentNodeState.byKey[checkpointKey] = {
1914
+ status: "tool_loop",
1915
+ opId,
1916
+ messages,
1917
+ steps,
1918
+ approvedCalls,
1919
+ ...pending ? { pending } : {}
1920
+ };
1921
+ ctx.stateDirty = true;
1922
+ };
1717
1923
  const emitToolEvent = (event, payload) => {
1718
1924
  ctx.node.emit(event, {
1719
1925
  ...payload,
@@ -1723,26 +1929,43 @@ ${tryStringify2(args.response)}`
1723
1929
  });
1724
1930
  };
1725
1931
  try {
1726
- for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
1932
+ for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
1727
1933
  (0, import_core42.throwIfExecutionAborted)(abortSignal);
1728
1934
  traceSpan?.addEvent?.("useReason.tool-step.start", {
1729
1935
  stepIndex,
1730
1936
  toolCount: toolDefinitions.length
1731
1937
  });
1732
- const step = await reasonEngine(
1938
+ const reused = Boolean(pending);
1939
+ const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
1940
+ const emit = useReasonArgs.emit ?? true;
1941
+ let textStarted = false;
1942
+ const structuredChunk = useReasonArgs.outputSchema && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
1943
+ const step = pending ?? await reasonEngine(
1733
1944
  {
1734
1945
  ...useReasonArgs,
1735
1946
  tools: toolDefinitions,
1736
1947
  messages,
1737
1948
  emit: false,
1738
- stream: false
1949
+ stream,
1950
+ onTextChunk: (delta) => {
1951
+ if (structuredChunk) structuredChunk(delta);
1952
+ if (!emit || useReasonArgs.outputSchema || !delta) return;
1953
+ if (!textStarted) {
1954
+ emitToolEvent("text-start", {});
1955
+ textStarted = true;
1956
+ }
1957
+ emitToolEvent("text-delta", { delta });
1958
+ }
1739
1959
  },
1740
1960
  { ...id ? { id } : {}, opId }
1741
1961
  );
1962
+ if (textStarted) emitToolEvent("text-end", {});
1742
1963
  finalText = step.text;
1743
1964
  finalRaw = step.raw;
1744
- aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
1745
- accumulateTokenUsage(step.usage);
1965
+ if (!reused) {
1966
+ aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
1967
+ accumulateTokenUsage(step.usage);
1968
+ }
1746
1969
  finalFinishReason = step.finishReason;
1747
1970
  aggregatedProviderMetadata = mergeProviderMetadata(
1748
1971
  aggregatedProviderMetadata,
@@ -1750,32 +1973,35 @@ ${tryStringify2(args.response)}`
1750
1973
  );
1751
1974
  aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
1752
1975
  const toolCalls = step.toolCalls ?? [];
1753
- const toolResults = [];
1754
- steps.push({
1755
- stepIndex,
1756
- text: step.text,
1757
- toolCalls,
1758
- toolResults,
1759
- ...step.usage ? { usage: step.usage } : {},
1760
- ...step.finishReason ? { finishReason: step.finishReason } : {},
1761
- ...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
1762
- ...step.warnings ? { warnings: step.warnings } : {}
1763
- });
1976
+ const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
1977
+ if (!reused)
1978
+ steps.push({
1979
+ stepIndex,
1980
+ text: step.text,
1981
+ toolCalls,
1982
+ toolResults,
1983
+ ...step.usage ? { usage: step.usage } : {},
1984
+ ...step.finishReason ? { finishReason: step.finishReason } : {},
1985
+ ...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
1986
+ ...step.warnings ? { warnings: step.warnings } : {}
1987
+ });
1764
1988
  if (toolCalls.length === 0) {
1765
- if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
1766
- emitToolEvent("text-start", {});
1767
- emitToolEvent("text-delta", { delta: step.text });
1768
- emitToolEvent("text-end", {});
1769
- }
1770
1989
  break;
1771
1990
  }
1772
- allToolCalls.push(...toolCalls);
1773
- messages.push({
1774
- role: "assistant",
1775
- content: step.text,
1776
- toolCalls
1777
- });
1991
+ if (!reused) {
1992
+ allToolCalls.push(...toolCalls);
1993
+ messages.push({
1994
+ role: "assistant",
1995
+ content: step.text,
1996
+ toolCalls,
1997
+ ...step.continuation ? { continuation: step.continuation } : {}
1998
+ });
1999
+ }
2000
+ pending = step;
2001
+ save();
1778
2002
  for (const toolCall of toolCalls) {
2003
+ if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
2004
+ continue;
1779
2005
  (0, import_core42.throwIfExecutionAborted)(abortSignal);
1780
2006
  const tool = toolByName.get(toolCall.name);
1781
2007
  if (!tool) {
@@ -1805,7 +2031,7 @@ ${tryStringify2(args.response)}`
1805
2031
  input: toolCall.input
1806
2032
  });
1807
2033
  }
1808
- if (shouldApproveTool) {
2034
+ if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
1809
2035
  const approval = await awaitInterruptInternal({
1810
2036
  id: `tool:${toolCall.id}`,
1811
2037
  request: {
@@ -1840,8 +2066,18 @@ ${tryStringify2(args.response)}`
1840
2066
  isError: true
1841
2067
  });
1842
2068
  }
2069
+ messages.push({
2070
+ role: "tool",
2071
+ content: denied.content,
2072
+ toolCallId: denied.toolCallId,
2073
+ name: denied.name,
2074
+ isError: true
2075
+ });
2076
+ save();
1843
2077
  continue;
1844
2078
  }
2079
+ approvedCalls.push(toolCall.id);
2080
+ save();
1845
2081
  }
1846
2082
  try {
1847
2083
  (0, import_core42.throwIfExecutionAborted)(abortSignal);
@@ -1895,25 +2131,19 @@ ${tryStringify2(args.response)}`
1895
2131
  message: result2.content
1896
2132
  });
1897
2133
  }
2134
+ const completed = toolResults.at(-1);
2135
+ if (completed)
2136
+ messages.push({
2137
+ role: "tool",
2138
+ content: completed.content,
2139
+ toolCallId: completed.toolCallId,
2140
+ name: completed.name,
2141
+ ...completed.isError !== void 0 ? { isError: completed.isError } : {}
2142
+ });
2143
+ save();
1898
2144
  }
1899
- const currentStep = steps[steps.length - 1];
1900
- if (currentStep) {
1901
- steps[steps.length - 1] = {
1902
- ...currentStep,
1903
- toolResults
1904
- };
1905
- }
1906
- for (const result2 of toolResults) {
1907
- messages.push({
1908
- role: "tool",
1909
- content: result2.content,
1910
- toolCallId: result2.toolCallId,
1911
- name: result2.name,
1912
- ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
1913
- ...result2.isError !== void 0 ? { isError: result2.isError } : {},
1914
- ...result2.raw !== void 0 ? { raw: result2.raw } : {}
1915
- });
1916
- }
2145
+ pending = void 0;
2146
+ save();
1917
2147
  }
1918
2148
  if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
1919
2149
  throw new Error(
@@ -1967,6 +2197,8 @@ ${tryStringify2(args.response)}`
1967
2197
  output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
1968
2198
  }
1969
2199
  });
2200
+ ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
2201
+ ctx.stateDirty = true;
1970
2202
  return result;
1971
2203
  } catch (error) {
1972
2204
  traceSpan?.fail?.(error, {
@@ -2000,7 +2232,15 @@ ${tryStringify2(args.response)}`
2000
2232
  args.model.options?.abortSignal
2001
2233
  );
2002
2234
  (0, import_core52.throwIfExecutionAborted)(abortSignal);
2003
- args = { ...args, abortSignal };
2235
+ const inferred = inferOutputFormat(
2236
+ args.outputSchema,
2237
+ args.responseFormat ?? args.model.options?.responseFormat
2238
+ );
2239
+ args = {
2240
+ ...args,
2241
+ abortSignal,
2242
+ ...!args.interrupt && inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {}
2243
+ };
2004
2244
  const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
2005
2245
  const opId = createRuntimeId2();
2006
2246
  const checkpointKey = resolveReasonCheckpointKey({
@@ -2044,6 +2284,8 @@ ${tryStringify2(args.response)}`
2044
2284
  if (args.tools?.length) {
2045
2285
  return runReasonToolLoop({
2046
2286
  useReasonArgs: args,
2287
+ checkpointKey,
2288
+ initialWarnings: inferred.warnings,
2047
2289
  ...id ? { id } : {},
2048
2290
  opId,
2049
2291
  ...traceSpan ? { traceSpan } : {}
@@ -2067,10 +2309,7 @@ ${tryStringify2(args.response)}`
2067
2309
  let aggregatedUsage;
2068
2310
  let finalFinishReason;
2069
2311
  let aggregatedProviderMetadata;
2070
- let aggregatedWarnings;
2071
- const emittedSetValues = /* @__PURE__ */ new Map();
2072
- const emittedAppendCounts = /* @__PURE__ */ new Map();
2073
- const emittedTextValues = /* @__PURE__ */ new Map();
2312
+ let aggregatedWarnings = inferred.warnings;
2074
2313
  if (existingCheckpoint) {
2075
2314
  firstText = existingCheckpoint.firstText;
2076
2315
  firstRaw = existingCheckpoint.firstRaw;
@@ -2092,78 +2331,7 @@ ${tryStringify2(args.response)}`
2092
2331
  emit: suppressTextStream ? false : args.emit,
2093
2332
  stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
2094
2333
  ...useStructuredIncrementalStreaming ? {
2095
- onTextChunk: (delta) => {
2096
- firstText += delta;
2097
- finalText = firstText;
2098
- for (const fieldPath of setFieldPaths) {
2099
- const values = extractCompletedFieldValues({
2100
- text: firstText,
2101
- path: fieldPath
2102
- });
2103
- for (const { path, value } of values) {
2104
- const nextSerialized = JSON.stringify(value);
2105
- if (emittedSetValues.get(path) === nextSerialized) continue;
2106
- emittedSetValues.set(path, nextSerialized);
2107
- emitStructuredData({
2108
- kind: "set",
2109
- path,
2110
- value,
2111
- dataType: args.structured?.dataType ?? "reason-output",
2112
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2113
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2114
- ...id ? { id } : {},
2115
- streamId: opId
2116
- });
2117
- }
2118
- }
2119
- for (const fieldPath of appendFieldPaths) {
2120
- const groups = extractCompletedArrayItemGroups({
2121
- text: firstText,
2122
- path: fieldPath
2123
- });
2124
- for (const { path, items } of groups) {
2125
- const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
2126
- if (items.length <= emittedAppendCount) continue;
2127
- const nextItems = items.slice(emittedAppendCount);
2128
- emittedAppendCounts.set(path, items.length);
2129
- emitStructuredData({
2130
- kind: "append",
2131
- path,
2132
- items: nextItems,
2133
- dataType: args.structured?.dataType ?? "reason-output",
2134
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2135
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2136
- ...id ? { id } : {},
2137
- streamId: opId
2138
- });
2139
- }
2140
- }
2141
- for (const fieldPath of textDeltaFieldPaths) {
2142
- const values = extractStreamingStringValues({
2143
- text: firstText,
2144
- path: fieldPath
2145
- });
2146
- for (const { path, value } of values) {
2147
- const emittedTextValue = emittedTextValues.get(path) ?? "";
2148
- if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
2149
- continue;
2150
- }
2151
- const nextDelta = value.slice(emittedTextValue.length);
2152
- emittedTextValues.set(path, value);
2153
- if (nextDelta.length === 0) continue;
2154
- emitStructuredData({
2155
- kind: "text-delta",
2156
- path,
2157
- delta: nextDelta,
2158
- dataType: args.structured?.dataType ?? "reason-output",
2159
- ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2160
- ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2161
- ...id ? { id } : {},
2162
- streamId: opId
2163
- });
2164
- }
2165
- }
2166
- }
2334
+ onTextChunk: createStructuredOutputStreamer(args, id, opId)
2167
2335
  } : {}
2168
2336
  },
2169
2337
  reasonMeta,
@@ -2283,6 +2451,7 @@ ${tryStringify2(args.response)}`
2283
2451
  const second = await reasonEngine(
2284
2452
  {
2285
2453
  ...args,
2454
+ ...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
2286
2455
  emit: suppressTextStream ? false : args.emit,
2287
2456
  stream: suppressTextStream ? false : args.stream
2288
2457
  },
@@ -4629,7 +4798,7 @@ var responseSchema = import_zod3.z.discriminatedUnion("type", [
4629
4798
  import_zod3.z.object({ type: import_zod3.z.literal("cancel") }).strict()
4630
4799
  ]);
4631
4800
  function resultFromOutcome(outcome, runId, sessionId) {
4632
- const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
4801
+ const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.error?.__kortyxHookStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
4633
4802
  const info = {
4634
4803
  runId,
4635
4804
  sessionId,