@kortyx/agent 0.27.0 → 0.28.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/CHANGELOG.md +17 -0
- package/dist/index.js +486 -41
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -526,6 +526,7 @@ var require_dist = __commonJS({
|
|
|
526
526
|
completeResponse: () => completeResponse,
|
|
527
527
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
528
528
|
defineInterruptContract: () => defineInterruptContract,
|
|
529
|
+
defineOutputContract: () => defineOutputContract,
|
|
529
530
|
emitWorkflowCall: () => emitWorkflowCall2,
|
|
530
531
|
parallel: () => parallel,
|
|
531
532
|
reportError: () => reportError,
|
|
@@ -926,6 +927,112 @@ var require_dist = __commonJS({
|
|
|
926
927
|
return normalized;
|
|
927
928
|
};
|
|
928
929
|
var isLegacyInterruptContracts = (config) => Boolean(config && legacyConfigs.has(config));
|
|
930
|
+
var import_zod22 = require("zod");
|
|
931
|
+
var parseStructuredPath = (path, source) => {
|
|
932
|
+
if (path.length === 0) {
|
|
933
|
+
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
934
|
+
}
|
|
935
|
+
const parts = path.split(".");
|
|
936
|
+
if (parts.some((part) => part.length === 0)) {
|
|
937
|
+
throw new Error(
|
|
938
|
+
`${source} path "${path}" must not contain empty segments.`
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
return parts;
|
|
942
|
+
};
|
|
943
|
+
var assertStructuredPath = (path, source) => {
|
|
944
|
+
parseStructuredPath(path, source);
|
|
945
|
+
};
|
|
946
|
+
var EMIT_PREFIX = "kortyx_emit__";
|
|
947
|
+
var RETURN_PREFIX = "kortyx_return__";
|
|
948
|
+
var STREAM_EMIT_PREFIX = "kortyx_stream_emit__";
|
|
949
|
+
var STREAM_RETURN_PREFIX = "kortyx_stream_return__";
|
|
950
|
+
var legacyOutputWarningEmitted = false;
|
|
951
|
+
var warnLegacyReasonOutput = () => {
|
|
952
|
+
if (legacyOutputWarningEmitted) return;
|
|
953
|
+
legacyOutputWarningEmitted = true;
|
|
954
|
+
process.emitWarning(
|
|
955
|
+
"useReason({ outputSchema, structured }) and result.output are deprecated and will be removed in the next major release. Define reusable contracts with defineOutputContract() and pass them through useReason({ outputs }). See docs/internal/next-major-removals.md.",
|
|
956
|
+
{ code: "KORTYX_USE_REASON_OUTPUT_DEPRECATED", type: "DeprecationWarning" }
|
|
957
|
+
);
|
|
958
|
+
};
|
|
959
|
+
var defineOutputContract = (contract) => {
|
|
960
|
+
if (!contract || typeof contract !== "object")
|
|
961
|
+
throw new Error("defineOutputContract requires a contract object.");
|
|
962
|
+
for (const [field, value] of [
|
|
963
|
+
["description", contract.description],
|
|
964
|
+
["schemaId", contract.schemaId],
|
|
965
|
+
["schemaVersion", contract.schemaVersion]
|
|
966
|
+
])
|
|
967
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
968
|
+
throw new Error(`Output contract ${field} must be a non-empty string.`);
|
|
969
|
+
if (typeof contract.schema?.safeParse !== "function")
|
|
970
|
+
throw new Error("Output contract schema must provide safeParse(value).");
|
|
971
|
+
if (contract.stream) {
|
|
972
|
+
if (!contract.stream.fields || Object.keys(contract.stream.fields).length === 0)
|
|
973
|
+
throw new Error("Output contract stream.fields must not be empty.");
|
|
974
|
+
for (const [path, mode] of Object.entries(contract.stream.fields)) {
|
|
975
|
+
assertStructuredPath(path, "Output contract stream.fields");
|
|
976
|
+
if (!["set", "append", "text-delta"].includes(mode))
|
|
977
|
+
throw new Error(`Invalid output contract stream mode for "${path}".`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return contract;
|
|
981
|
+
};
|
|
982
|
+
var outputControlToolName = (kind, name) => `${kind === "emit" ? EMIT_PREFIX : kind === "return" ? RETURN_PREFIX : kind === "stream-emit" ? STREAM_EMIT_PREFIX : STREAM_RETURN_PREFIX}${name}`;
|
|
983
|
+
var isOutputControlToolName = (name) => name.startsWith(EMIT_PREFIX) || name.startsWith(RETURN_PREFIX) || name.startsWith(STREAM_EMIT_PREFIX) || name.startsWith(STREAM_RETURN_PREFIX);
|
|
984
|
+
var normalizeReasonOutputs = (config, hasLegacyOutput) => {
|
|
985
|
+
if (!config) return void 0;
|
|
986
|
+
if (hasLegacyOutput)
|
|
987
|
+
throw new Error(
|
|
988
|
+
"useReason cannot combine `outputs` with deprecated `outputSchema` or `structured`."
|
|
989
|
+
);
|
|
990
|
+
const entries = [
|
|
991
|
+
...Object.entries(config.emit ?? {}).map(([name, contract]) => ({
|
|
992
|
+
name,
|
|
993
|
+
contract,
|
|
994
|
+
kind: "emit"
|
|
995
|
+
})),
|
|
996
|
+
...Object.entries(config.return ?? {}).map(([name, contract]) => ({
|
|
997
|
+
name,
|
|
998
|
+
contract,
|
|
999
|
+
kind: "return"
|
|
1000
|
+
}))
|
|
1001
|
+
];
|
|
1002
|
+
if (entries.length === 0)
|
|
1003
|
+
throw new Error(
|
|
1004
|
+
"useReason outputs must include at least one emit or return contract."
|
|
1005
|
+
);
|
|
1006
|
+
for (const { name, contract, kind } of entries) {
|
|
1007
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(name) || outputControlToolName(kind, name).length > 64)
|
|
1008
|
+
throw new Error(`Invalid useReason output contract name "${name}".`);
|
|
1009
|
+
defineOutputContract(contract);
|
|
1010
|
+
if (contract.schema["~kortyx"]?.nativeOutput === true)
|
|
1011
|
+
throw new Error(
|
|
1012
|
+
`useReason output contract "${name}" uses a provider-native output schema that cannot be selected through tools. Continue using outputSchema for this provider until a non-tool contract path is available.`
|
|
1013
|
+
);
|
|
1014
|
+
if (contract.stream && outputControlToolName(
|
|
1015
|
+
kind === "emit" ? "stream-emit" : "stream-return",
|
|
1016
|
+
name
|
|
1017
|
+
).length > 64)
|
|
1018
|
+
throw new Error(
|
|
1019
|
+
`Invalid useReason output contract name "${name}" for streaming tools.`
|
|
1020
|
+
);
|
|
1021
|
+
try {
|
|
1022
|
+
const json = (0, import_zod22.toJSONSchema)(contract.schema);
|
|
1023
|
+
if (json.type !== "object")
|
|
1024
|
+
throw new Error("The tool input schema must have an object root.");
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
throw new Error(
|
|
1027
|
+
`useReason output contract "${name}" schema must support JSON Schema conversion.`,
|
|
1028
|
+
{ cause: error }
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (config.maxEmissions !== void 0 && (!Number.isInteger(config.maxEmissions) || config.maxEmissions < 1))
|
|
1033
|
+
throw new Error("useReason outputs.maxEmissions must be at least 1.");
|
|
1034
|
+
return config;
|
|
1035
|
+
};
|
|
929
1036
|
var import_core62 = require("@kortyx/core");
|
|
930
1037
|
function safeSpan(span) {
|
|
931
1038
|
const guard = (fn) => (...args) => {
|
|
@@ -1006,21 +1113,6 @@ var require_dist = __commonJS({
|
|
|
1006
1113
|
return execution ?? invoke(safeSpan(void 0));
|
|
1007
1114
|
}
|
|
1008
1115
|
var import_node_crypto4 = require("crypto");
|
|
1009
|
-
var parseStructuredPath = (path, source) => {
|
|
1010
|
-
if (path.length === 0) {
|
|
1011
|
-
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
1012
|
-
}
|
|
1013
|
-
const parts = path.split(".");
|
|
1014
|
-
if (parts.some((part) => part.length === 0)) {
|
|
1015
|
-
throw new Error(
|
|
1016
|
-
`${source} path "${path}" must not contain empty segments.`
|
|
1017
|
-
);
|
|
1018
|
-
}
|
|
1019
|
-
return parts;
|
|
1020
|
-
};
|
|
1021
|
-
var assertStructuredPath = (path, source) => {
|
|
1022
|
-
parseStructuredPath(path, source);
|
|
1023
|
-
};
|
|
1024
1116
|
var createStructuredStreamId = () => {
|
|
1025
1117
|
try {
|
|
1026
1118
|
return (0, import_node_crypto4.randomUUID)();
|
|
@@ -1816,12 +1908,12 @@ var require_dist = __commonJS({
|
|
|
1816
1908
|
throw error;
|
|
1817
1909
|
}
|
|
1818
1910
|
}
|
|
1819
|
-
var
|
|
1911
|
+
var import_zod32 = require("zod");
|
|
1820
1912
|
function inferOutputFormat(schema, explicit) {
|
|
1821
1913
|
if (explicit) return { responseFormat: explicit };
|
|
1822
1914
|
if (!schema) return {};
|
|
1823
1915
|
try {
|
|
1824
|
-
const json = (0,
|
|
1916
|
+
const json = (0, import_zod32.toJSONSchema)(schema);
|
|
1825
1917
|
const clean = (value) => {
|
|
1826
1918
|
if (Array.isArray(value)) return value.map(clean);
|
|
1827
1919
|
if (!value || typeof value !== "object") return value;
|
|
@@ -2570,7 +2662,7 @@ var require_dist = __commonJS({
|
|
|
2570
2662
|
);
|
|
2571
2663
|
var createInvalidStructuredOutputError = (label, cause) => new import_errors52.ValidationError(
|
|
2572
2664
|
"INVALID_MODEL_JSON",
|
|
2573
|
-
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check outputSchema and provider structured-output settings.`,
|
|
2665
|
+
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check the output contract or outputSchema and provider structured-output settings.`,
|
|
2574
2666
|
cause
|
|
2575
2667
|
);
|
|
2576
2668
|
var parseReasonOutputWithSchema = (args) => {
|
|
@@ -2693,7 +2785,7 @@ var require_dist = __commonJS({
|
|
|
2693
2785
|
throw error;
|
|
2694
2786
|
}
|
|
2695
2787
|
};
|
|
2696
|
-
var
|
|
2788
|
+
var import_zod42 = require("zod");
|
|
2697
2789
|
var tryStringify2 = (value) => {
|
|
2698
2790
|
if (typeof value === "string") return value;
|
|
2699
2791
|
try {
|
|
@@ -2704,7 +2796,7 @@ var require_dist = __commonJS({
|
|
|
2704
2796
|
};
|
|
2705
2797
|
var tryGetSchemaHint = (schema) => {
|
|
2706
2798
|
try {
|
|
2707
|
-
const hint = (0,
|
|
2799
|
+
const hint = (0, import_zod42.toJSONSchema)(schema);
|
|
2708
2800
|
return JSON.stringify(hint, null, 2);
|
|
2709
2801
|
} catch {
|
|
2710
2802
|
return void 0;
|
|
@@ -2902,7 +2994,7 @@ ${tryStringify2(args.response)}`
|
|
|
2902
2994
|
};
|
|
2903
2995
|
var import_core52 = require("@kortyx/core");
|
|
2904
2996
|
var import_errors62 = require("@kortyx/core/errors");
|
|
2905
|
-
var
|
|
2997
|
+
var import_zod52 = require("zod");
|
|
2906
2998
|
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
2907
2999
|
name: tool.name,
|
|
2908
3000
|
...tool.title ? { title: tool.title } : {},
|
|
@@ -2938,10 +3030,14 @@ ${tryStringify2(args.response)}`
|
|
|
2938
3030
|
};
|
|
2939
3031
|
};
|
|
2940
3032
|
var createInitialMessages = (args) => [
|
|
2941
|
-
...typeof args.system === "string" || args.interruptInstructions ? [
|
|
3033
|
+
...typeof args.system === "string" || args.interruptInstructions || args.outputInstructions ? [
|
|
2942
3034
|
{
|
|
2943
3035
|
role: "system",
|
|
2944
|
-
content: [
|
|
3036
|
+
content: [
|
|
3037
|
+
args.system,
|
|
3038
|
+
args.interruptInstructions,
|
|
3039
|
+
args.outputInstructions
|
|
3040
|
+
].filter((value) => Boolean(value)).join("\n\n")
|
|
2945
3041
|
}
|
|
2946
3042
|
] : [],
|
|
2947
3043
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -2963,12 +3059,13 @@ ${tryStringify2(args.response)}`
|
|
|
2963
3059
|
interrupts: useReasonArgs.interrupts
|
|
2964
3060
|
});
|
|
2965
3061
|
const interruptByToolName = /* @__PURE__ */ new Map();
|
|
3062
|
+
const outputByToolName = /* @__PURE__ */ new Map();
|
|
2966
3063
|
const toolByName = /* @__PURE__ */ new Map();
|
|
2967
3064
|
let separateOutput = false;
|
|
2968
3065
|
for (const tool of tools2) {
|
|
2969
|
-
if (isInterruptControlToolName(tool.name))
|
|
3066
|
+
if (isInterruptControlToolName(tool.name) || isOutputControlToolName(tool.name))
|
|
2970
3067
|
throw new Error(
|
|
2971
|
-
`useReason tool name "${tool.name}" uses
|
|
3068
|
+
`useReason tool name "${tool.name}" uses a reserved control-tool namespace.`
|
|
2972
3069
|
);
|
|
2973
3070
|
if (toolByName.has(tool.name)) {
|
|
2974
3071
|
throw new Error(`useReason received duplicate tool name "${tool.name}".`);
|
|
@@ -2997,7 +3094,7 @@ ${tryStringify2(args.response)}`
|
|
|
2997
3094
|
);
|
|
2998
3095
|
let inputSchema;
|
|
2999
3096
|
try {
|
|
3000
|
-
inputSchema = (0,
|
|
3097
|
+
inputSchema = (0, import_zod52.toJSONSchema)(contract.requestSchema);
|
|
3001
3098
|
} catch (error) {
|
|
3002
3099
|
throw new Error(
|
|
3003
3100
|
`useReason interrupt contract "${name}" requestSchema must support JSON Schema conversion.`,
|
|
@@ -3021,9 +3118,66 @@ ${tryStringify2(args.response)}`
|
|
|
3021
3118
|
}
|
|
3022
3119
|
});
|
|
3023
3120
|
}
|
|
3121
|
+
const outputDefinitions = [];
|
|
3122
|
+
for (const kind of ["emit", "return"]) {
|
|
3123
|
+
for (const [name, contract] of Object.entries(
|
|
3124
|
+
useReasonArgs.outputs?.[kind] ?? {}
|
|
3125
|
+
)) {
|
|
3126
|
+
if (contract.stream) {
|
|
3127
|
+
const streamName = outputControlToolName(
|
|
3128
|
+
kind === "emit" ? "stream-emit" : "stream-return",
|
|
3129
|
+
name
|
|
3130
|
+
);
|
|
3131
|
+
outputByToolName.set(streamName, {
|
|
3132
|
+
name,
|
|
3133
|
+
kind,
|
|
3134
|
+
contract,
|
|
3135
|
+
stream: true
|
|
3136
|
+
});
|
|
3137
|
+
outputDefinitions.push({
|
|
3138
|
+
name: streamName,
|
|
3139
|
+
title: `Stream structured output: ${name}`,
|
|
3140
|
+
description: `${contract.description} Call this to start realtime generation of this structured value. The value is generated in a separate streamed model pass, validated, and ${kind === "emit" ? "returned as a tool result so you can continue" : "used to finish this operation"}.`,
|
|
3141
|
+
inputSchema: {
|
|
3142
|
+
type: "object",
|
|
3143
|
+
properties: {
|
|
3144
|
+
instruction: {
|
|
3145
|
+
type: "string",
|
|
3146
|
+
description: "What the streamed output should contain."
|
|
3147
|
+
}
|
|
3148
|
+
},
|
|
3149
|
+
required: ["instruction"],
|
|
3150
|
+
additionalProperties: false
|
|
3151
|
+
},
|
|
3152
|
+
metadata: {
|
|
3153
|
+
kortyxControl: kind === "emit" ? "output.stream.emit" : "output.stream.return",
|
|
3154
|
+
contract: name,
|
|
3155
|
+
schemaId: contract.schemaId,
|
|
3156
|
+
schemaVersion: contract.schemaVersion
|
|
3157
|
+
}
|
|
3158
|
+
});
|
|
3159
|
+
} else {
|
|
3160
|
+
const toolName = outputControlToolName(kind, name);
|
|
3161
|
+
outputByToolName.set(toolName, { name, kind, contract, stream: false });
|
|
3162
|
+
outputDefinitions.push({
|
|
3163
|
+
name: toolName,
|
|
3164
|
+
title: `${kind === "emit" ? "Emit" : "Return"} structured output: ${name}`,
|
|
3165
|
+
description: `${contract.description} ${kind === "emit" ? "Emit this structured value and continue reasoning." : "Return this structured value and finish reasoning."}`,
|
|
3166
|
+
inputSchema: (0, import_zod52.toJSONSchema)(contract.schema),
|
|
3167
|
+
metadata: {
|
|
3168
|
+
kortyxControl: kind === "emit" ? "output.emit" : "output.return",
|
|
3169
|
+
contract: name,
|
|
3170
|
+
schemaId: contract.schemaId,
|
|
3171
|
+
schemaVersion: contract.schemaVersion
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3024
3177
|
const toolDefinitions = [
|
|
3025
3178
|
...toToolDefinitions(tools2),
|
|
3026
|
-
...interruptDefinitions
|
|
3179
|
+
...interruptDefinitions,
|
|
3180
|
+
...outputDefinitions
|
|
3027
3181
|
];
|
|
3028
3182
|
let finalizing = checkpoint?.finalizing ?? false;
|
|
3029
3183
|
let completed = false;
|
|
@@ -3038,11 +3192,27 @@ ${tryStringify2(args.response)}`
|
|
|
3038
3192
|
"- Call at most one human-input tool in a model turn, and do not combine it with other tool calls in that turn.",
|
|
3039
3193
|
interruptConfig.mode === "required" ? "- You must call at least one human-input tool before returning the final answer." : "- Human input is optional; call a human-input tool only when the request cannot be completed safely without it."
|
|
3040
3194
|
].join("\n")
|
|
3195
|
+
} : {},
|
|
3196
|
+
...useReasonArgs.outputs ? {
|
|
3197
|
+
outputInstructions: [
|
|
3198
|
+
"Structured output rules:",
|
|
3199
|
+
"- The kortyx_emit__* tools publish validated structured output and return an acknowledgement. You may continue with text or more tools afterward.",
|
|
3200
|
+
"- The kortyx_return__* tools publish a validated result and end this reasoning operation. If return tools are available, call exactly one before finishing.",
|
|
3201
|
+
"- For contracts with realtime streaming, use kortyx_stream_emit__* or kortyx_stream_return__*. These start a separate streamed JSON generation; its fields become visible as tokens arrive.",
|
|
3202
|
+
"- Call a structured output control tool alone in its model turn."
|
|
3203
|
+
].join("\n")
|
|
3041
3204
|
} : {}
|
|
3042
3205
|
});
|
|
3043
3206
|
const steps = checkpoint?.steps ?? [];
|
|
3044
|
-
const allToolCalls = steps.flatMap((step) => step.toolCalls).filter(
|
|
3045
|
-
|
|
3207
|
+
const allToolCalls = steps.flatMap((step) => step.toolCalls).filter(
|
|
3208
|
+
(call) => !interruptByToolName.has(call.name) && !outputByToolName.has(call.name)
|
|
3209
|
+
);
|
|
3210
|
+
const knownToolCallIds = new Set(
|
|
3211
|
+
steps.flatMap((step) => step.toolCalls.map((call) => call.id))
|
|
3212
|
+
);
|
|
3213
|
+
const allToolResults = steps.flatMap((step) => step.toolResults).filter(
|
|
3214
|
+
(result) => !interruptByToolName.has(result.name) && !outputByToolName.has(result.name)
|
|
3215
|
+
);
|
|
3046
3216
|
let finalText = "";
|
|
3047
3217
|
let finalRaw;
|
|
3048
3218
|
let finalOutput;
|
|
@@ -3057,8 +3227,11 @@ ${tryStringify2(args.response)}`
|
|
|
3057
3227
|
args.initialWarnings
|
|
3058
3228
|
);
|
|
3059
3229
|
let pending = checkpoint?.pending;
|
|
3230
|
+
let pendingStepIndex = checkpoint?.pendingStepIndex;
|
|
3060
3231
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
3061
3232
|
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
3233
|
+
const emissions = checkpoint?.emissions ?? [];
|
|
3234
|
+
let returned = checkpoint?.returned;
|
|
3062
3235
|
const save = () => {
|
|
3063
3236
|
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3064
3237
|
status: "tool_loop",
|
|
@@ -3067,8 +3240,11 @@ ${tryStringify2(args.response)}`
|
|
|
3067
3240
|
steps,
|
|
3068
3241
|
approvedCalls,
|
|
3069
3242
|
interruptHistory,
|
|
3243
|
+
emissions,
|
|
3244
|
+
...returned ? { returned } : {},
|
|
3070
3245
|
finalizing,
|
|
3071
|
-
...pending ? { pending } : {}
|
|
3246
|
+
...pending ? { pending } : {},
|
|
3247
|
+
...pending && pendingStepIndex !== void 0 ? { pendingStepIndex } : {}
|
|
3072
3248
|
};
|
|
3073
3249
|
ctx.stateDirty = true;
|
|
3074
3250
|
};
|
|
@@ -3081,7 +3257,7 @@ ${tryStringify2(args.response)}`
|
|
|
3081
3257
|
});
|
|
3082
3258
|
};
|
|
3083
3259
|
try {
|
|
3084
|
-
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
3260
|
+
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
3085
3261
|
(0, import_core52.throwIfExecutionAborted)(abortSignal);
|
|
3086
3262
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
3087
3263
|
stepIndex,
|
|
@@ -3091,6 +3267,7 @@ ${tryStringify2(args.response)}`
|
|
|
3091
3267
|
const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
|
|
3092
3268
|
const emit = useReasonArgs.emit ?? true;
|
|
3093
3269
|
let textStarted = false;
|
|
3270
|
+
const segmentId = `step-${stepIndex}`;
|
|
3094
3271
|
const structuredChunk = useReasonArgs.outputSchema && (!separateOutput || finalizing) && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
|
|
3095
3272
|
const step = pending ?? await reasonEngine(
|
|
3096
3273
|
{
|
|
@@ -3104,15 +3281,15 @@ ${tryStringify2(args.response)}`
|
|
|
3104
3281
|
if (structuredChunk) structuredChunk(delta);
|
|
3105
3282
|
if (!emit || useReasonArgs.outputSchema || !delta) return;
|
|
3106
3283
|
if (!textStarted) {
|
|
3107
|
-
emitToolEvent("text-start", {});
|
|
3284
|
+
emitToolEvent("text-start", { segmentId });
|
|
3108
3285
|
textStarted = true;
|
|
3109
3286
|
}
|
|
3110
|
-
emitToolEvent("text-delta", { delta });
|
|
3287
|
+
emitToolEvent("text-delta", { delta, segmentId });
|
|
3111
3288
|
}
|
|
3112
3289
|
},
|
|
3113
3290
|
{ ...id ? { id } : {}, opId }
|
|
3114
3291
|
);
|
|
3115
|
-
if (textStarted) emitToolEvent("text-end", {});
|
|
3292
|
+
if (textStarted) emitToolEvent("text-end", { segmentId });
|
|
3116
3293
|
finalText = step.text;
|
|
3117
3294
|
finalRaw = step.raw;
|
|
3118
3295
|
if (!reused) {
|
|
@@ -3128,7 +3305,7 @@ ${tryStringify2(args.response)}`
|
|
|
3128
3305
|
const toolCalls = step.toolCalls ?? [];
|
|
3129
3306
|
const ids = /* @__PURE__ */ new Set();
|
|
3130
3307
|
for (const call of toolCalls) {
|
|
3131
|
-
if (!call.id || ids.has(call.id) || !reused &&
|
|
3308
|
+
if (!call.id || ids.has(call.id) || !reused && knownToolCallIds.has(call.id))
|
|
3132
3309
|
throw new Error("Model returned duplicate or empty tool call IDs.");
|
|
3133
3310
|
ids.add(call.id);
|
|
3134
3311
|
}
|
|
@@ -3144,7 +3321,12 @@ ${tryStringify2(args.response)}`
|
|
|
3144
3321
|
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
3145
3322
|
...step.warnings ? { warnings: step.warnings } : {}
|
|
3146
3323
|
});
|
|
3324
|
+
if (!reused) for (const call of toolCalls) knownToolCallIds.add(call.id);
|
|
3147
3325
|
if (toolCalls.length === 0) {
|
|
3326
|
+
if (Object.keys(useReasonArgs.outputs?.return ?? {}).length > 0 && !returned)
|
|
3327
|
+
throw new Error(
|
|
3328
|
+
"useReason outputs.return requires one terminal output contract call."
|
|
3329
|
+
);
|
|
3148
3330
|
if (interruptConfig?.mode === "required" && interruptHistory.length === 0)
|
|
3149
3331
|
throw new Error(
|
|
3150
3332
|
"useReason interrupts.mode is required, but the model returned a final answer without requesting human input."
|
|
@@ -3182,6 +3364,7 @@ ${tryStringify2(args.response)}`
|
|
|
3182
3364
|
});
|
|
3183
3365
|
finalizing = true;
|
|
3184
3366
|
pending = void 0;
|
|
3367
|
+
pendingStepIndex = void 0;
|
|
3185
3368
|
save();
|
|
3186
3369
|
continue;
|
|
3187
3370
|
}
|
|
@@ -3194,7 +3377,9 @@ ${tryStringify2(args.response)}`
|
|
|
3194
3377
|
);
|
|
3195
3378
|
if (!reused) {
|
|
3196
3379
|
allToolCalls.push(
|
|
3197
|
-
...toolCalls.filter(
|
|
3380
|
+
...toolCalls.filter(
|
|
3381
|
+
(call) => !interruptByToolName.has(call.name) && !outputByToolName.has(call.name)
|
|
3382
|
+
)
|
|
3198
3383
|
);
|
|
3199
3384
|
messages.push({
|
|
3200
3385
|
role: "assistant",
|
|
@@ -3204,7 +3389,197 @@ ${tryStringify2(args.response)}`
|
|
|
3204
3389
|
});
|
|
3205
3390
|
}
|
|
3206
3391
|
pending = step;
|
|
3392
|
+
pendingStepIndex = stepIndex;
|
|
3207
3393
|
save();
|
|
3394
|
+
const outputCalls = toolCalls.filter(
|
|
3395
|
+
(call) => outputByToolName.has(call.name)
|
|
3396
|
+
);
|
|
3397
|
+
if (outputCalls.length > 0) {
|
|
3398
|
+
if (toolCalls.length !== 1)
|
|
3399
|
+
throw new Error(
|
|
3400
|
+
"A useReason output contract call must be the only tool call in its model turn."
|
|
3401
|
+
);
|
|
3402
|
+
const call = outputCalls[0];
|
|
3403
|
+
if (!call) throw new Error("Missing useReason output control call.");
|
|
3404
|
+
const resolved = outputByToolName.get(call.name);
|
|
3405
|
+
if (!resolved)
|
|
3406
|
+
throw new Error(
|
|
3407
|
+
`Unknown useReason output contract tool "${call.name}".`
|
|
3408
|
+
);
|
|
3409
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id)) {
|
|
3410
|
+
pending = void 0;
|
|
3411
|
+
pendingStepIndex = void 0;
|
|
3412
|
+
save();
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
if (resolved.kind === "emit" && emissions.length >= (useReasonArgs.outputs?.maxEmissions ?? 16))
|
|
3416
|
+
throw new Error(
|
|
3417
|
+
`useReason reached outputs.maxEmissions (${useReasonArgs.outputs?.maxEmissions ?? 16}).`
|
|
3418
|
+
);
|
|
3419
|
+
let data;
|
|
3420
|
+
if (resolved.stream) {
|
|
3421
|
+
if (!stream)
|
|
3422
|
+
throw new Error(
|
|
3423
|
+
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
3424
|
+
);
|
|
3425
|
+
if (stepIndex + 1 >= maxSteps)
|
|
3426
|
+
throw new Error(
|
|
3427
|
+
`useReason output streaming requires another model pass within maxSteps (${maxSteps}).`
|
|
3428
|
+
);
|
|
3429
|
+
const instruction = call.input && typeof call.input === "object" && !Array.isArray(call.input) ? call.input.instruction : void 0;
|
|
3430
|
+
if (typeof instruction !== "string" || instruction.trim().length === 0)
|
|
3431
|
+
throw new Error(
|
|
3432
|
+
`useReason streaming output contract "${resolved.name}" requires an instruction.`
|
|
3433
|
+
);
|
|
3434
|
+
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
3435
|
+
const structured = {
|
|
3436
|
+
dataType: resolved.contract.schemaId,
|
|
3437
|
+
schemaId: resolved.contract.schemaId,
|
|
3438
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3439
|
+
fields: resolved.contract.stream?.fields
|
|
3440
|
+
};
|
|
3441
|
+
const inferred = inferOutputFormat(
|
|
3442
|
+
resolved.contract.schema,
|
|
3443
|
+
void 0
|
|
3444
|
+
);
|
|
3445
|
+
try {
|
|
3446
|
+
const outputStep = await reasonEngine(
|
|
3447
|
+
{
|
|
3448
|
+
...useReasonArgs,
|
|
3449
|
+
tools: [],
|
|
3450
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3451
|
+
messages: [
|
|
3452
|
+
...messages,
|
|
3453
|
+
{
|
|
3454
|
+
role: "tool",
|
|
3455
|
+
toolCallId: call.id,
|
|
3456
|
+
name: call.name,
|
|
3457
|
+
content: "Generating the requested structured output."
|
|
3458
|
+
},
|
|
3459
|
+
{
|
|
3460
|
+
role: "user",
|
|
3461
|
+
content: withOutputGuardrails(
|
|
3462
|
+
instruction,
|
|
3463
|
+
resolved.contract.schema
|
|
3464
|
+
)
|
|
3465
|
+
}
|
|
3466
|
+
],
|
|
3467
|
+
emit: false,
|
|
3468
|
+
stream,
|
|
3469
|
+
...emit && stream ? {
|
|
3470
|
+
onTextChunk: createStructuredOutputStreamer(
|
|
3471
|
+
{ structured },
|
|
3472
|
+
id,
|
|
3473
|
+
streamId
|
|
3474
|
+
)
|
|
3475
|
+
} : {}
|
|
3476
|
+
},
|
|
3477
|
+
{ ...id ? { id } : {}, opId }
|
|
3478
|
+
);
|
|
3479
|
+
if (outputStep.toolCalls?.length)
|
|
3480
|
+
throw new Error(
|
|
3481
|
+
`Provider requested a tool during streamed output contract "${resolved.name}".`
|
|
3482
|
+
);
|
|
3483
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, outputStep.usage);
|
|
3484
|
+
accumulateTokenUsage(outputStep.usage);
|
|
3485
|
+
aggregatedWarnings = mergeWarnings(
|
|
3486
|
+
aggregatedWarnings,
|
|
3487
|
+
mergeWarnings(inferred.warnings, outputStep.warnings)
|
|
3488
|
+
);
|
|
3489
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3490
|
+
aggregatedProviderMetadata,
|
|
3491
|
+
outputStep.providerMetadata
|
|
3492
|
+
);
|
|
3493
|
+
finalFinishReason = outputStep.finishReason;
|
|
3494
|
+
finalRaw = outputStep.raw;
|
|
3495
|
+
data = parseReasonOutputWithSchema({
|
|
3496
|
+
text: outputStep.text,
|
|
3497
|
+
schema: resolved.contract.schema,
|
|
3498
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3499
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3500
|
+
label: `useReason output contract "${resolved.name}"`
|
|
3501
|
+
});
|
|
3502
|
+
steps.push({
|
|
3503
|
+
stepIndex: stepIndex + 1,
|
|
3504
|
+
kind: "output",
|
|
3505
|
+
text: outputStep.text,
|
|
3506
|
+
toolCalls: [],
|
|
3507
|
+
toolResults: [],
|
|
3508
|
+
...outputStep.usage ? { usage: outputStep.usage } : {},
|
|
3509
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3510
|
+
...outputStep.providerMetadata ? { providerMetadata: outputStep.providerMetadata } : {},
|
|
3511
|
+
...outputStep.warnings ? { warnings: outputStep.warnings } : {}
|
|
3512
|
+
});
|
|
3513
|
+
stepIndex += 1;
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
traceSpan?.addEvent?.("useReason.output.invalidated", {
|
|
3516
|
+
contract: resolved.name,
|
|
3517
|
+
kind: resolved.kind,
|
|
3518
|
+
schemaId: resolved.contract.schemaId,
|
|
3519
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3520
|
+
streamId
|
|
3521
|
+
});
|
|
3522
|
+
if (emit) {
|
|
3523
|
+
try {
|
|
3524
|
+
ctx.node.emit("structured_data_invalidated", {
|
|
3525
|
+
node: ctx.node.graph.node,
|
|
3526
|
+
streamId
|
|
3527
|
+
});
|
|
3528
|
+
} catch {
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
throw error;
|
|
3532
|
+
}
|
|
3533
|
+
} else {
|
|
3534
|
+
data = parseWithSchema(
|
|
3535
|
+
resolved.contract.schema,
|
|
3536
|
+
call.input,
|
|
3537
|
+
`useReason output contract "${resolved.name}"`
|
|
3538
|
+
);
|
|
3539
|
+
}
|
|
3540
|
+
const entry = { contract: resolved.name, data };
|
|
3541
|
+
const emissionIndex = emissions.length;
|
|
3542
|
+
if (resolved.kind === "emit")
|
|
3543
|
+
emissions.push(entry);
|
|
3544
|
+
else returned = entry;
|
|
3545
|
+
if (emit)
|
|
3546
|
+
emitStructuredData({
|
|
3547
|
+
kind: "final",
|
|
3548
|
+
data,
|
|
3549
|
+
dataType: resolved.contract.schemaId,
|
|
3550
|
+
schemaId: resolved.contract.schemaId,
|
|
3551
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3552
|
+
streamId: `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissionIndex : "return"}`,
|
|
3553
|
+
...id ? { id } : {}
|
|
3554
|
+
});
|
|
3555
|
+
traceSpan?.addEvent?.("useReason.output.emitted", {
|
|
3556
|
+
contract: resolved.name,
|
|
3557
|
+
kind: resolved.kind,
|
|
3558
|
+
schemaId: resolved.contract.schemaId,
|
|
3559
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3560
|
+
emissionIndex,
|
|
3561
|
+
data
|
|
3562
|
+
});
|
|
3563
|
+
if (resolved.kind === "return") {
|
|
3564
|
+
pending = void 0;
|
|
3565
|
+
pendingStepIndex = void 0;
|
|
3566
|
+
completed = true;
|
|
3567
|
+
save();
|
|
3568
|
+
break;
|
|
3569
|
+
}
|
|
3570
|
+
const result2 = normalizeToolResult(call, { emitted: true, data });
|
|
3571
|
+
toolResults.push(result2);
|
|
3572
|
+
messages.push({
|
|
3573
|
+
role: "tool",
|
|
3574
|
+
content: result2.content,
|
|
3575
|
+
toolCallId: result2.toolCallId,
|
|
3576
|
+
name: result2.name
|
|
3577
|
+
});
|
|
3578
|
+
pending = void 0;
|
|
3579
|
+
pendingStepIndex = void 0;
|
|
3580
|
+
save();
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3208
3583
|
const interruptCalls = toolCalls.filter(
|
|
3209
3584
|
(call) => interruptByToolName.has(call.name)
|
|
3210
3585
|
);
|
|
@@ -3227,6 +3602,7 @@ ${tryStringify2(args.response)}`
|
|
|
3227
3602
|
);
|
|
3228
3603
|
if (toolResults.some((result3) => result3.toolCallId === interruptCall.id)) {
|
|
3229
3604
|
pending = void 0;
|
|
3605
|
+
pendingStepIndex = void 0;
|
|
3230
3606
|
save();
|
|
3231
3607
|
continue;
|
|
3232
3608
|
}
|
|
@@ -3280,6 +3656,7 @@ ${tryStringify2(args.response)}`
|
|
|
3280
3656
|
interruptIndex: interruptHistory.length - 1
|
|
3281
3657
|
});
|
|
3282
3658
|
pending = void 0;
|
|
3659
|
+
pendingStepIndex = void 0;
|
|
3283
3660
|
save();
|
|
3284
3661
|
continue;
|
|
3285
3662
|
}
|
|
@@ -3455,6 +3832,7 @@ ${tryStringify2(args.response)}`
|
|
|
3455
3832
|
save();
|
|
3456
3833
|
}
|
|
3457
3834
|
pending = void 0;
|
|
3835
|
+
pendingStepIndex = void 0;
|
|
3458
3836
|
save();
|
|
3459
3837
|
}
|
|
3460
3838
|
if (!completed) {
|
|
@@ -3483,7 +3861,7 @@ ${tryStringify2(args.response)}`
|
|
|
3483
3861
|
const result = {
|
|
3484
3862
|
...id ? { id } : {},
|
|
3485
3863
|
opId,
|
|
3486
|
-
text: finalText,
|
|
3864
|
+
text: useReasonArgs.outputs ? steps.filter((step) => step.kind !== "output").map((step) => step.text).filter(Boolean).join("\n") : finalText,
|
|
3487
3865
|
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
3488
3866
|
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3489
3867
|
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
@@ -3493,6 +3871,8 @@ ${tryStringify2(args.response)}`
|
|
|
3493
3871
|
toolCalls: allToolCalls,
|
|
3494
3872
|
toolResults: allToolResults,
|
|
3495
3873
|
steps,
|
|
3874
|
+
...emissions.length ? { emissions } : {},
|
|
3875
|
+
...returned ? { returned } : {},
|
|
3496
3876
|
...interruptHistory.length > 0 ? { interruptHistory } : {},
|
|
3497
3877
|
...isLegacyInterruptContracts(interruptConfig) && interruptHistory.length > 0 ? {
|
|
3498
3878
|
interruptResponse: interruptHistory.at(-1)?.response
|
|
@@ -3508,11 +3888,13 @@ ${tryStringify2(args.response)}`
|
|
|
3508
3888
|
toolCallCount: allToolCalls.length,
|
|
3509
3889
|
toolResultCount: allToolResults.length,
|
|
3510
3890
|
toolStepCount: steps.length,
|
|
3511
|
-
interruptCount: interruptHistory.length
|
|
3891
|
+
interruptCount: interruptHistory.length,
|
|
3892
|
+
emissionCount: emissions.length,
|
|
3893
|
+
...returned ? { returnContract: returned.contract } : {}
|
|
3512
3894
|
},
|
|
3513
3895
|
telemetry: {
|
|
3514
3896
|
...useReasonArgs.telemetry ?? {},
|
|
3515
|
-
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
3897
|
+
output: useReasonArgs.telemetry?.output ?? returned?.data ?? finalOutput ?? finalText
|
|
3516
3898
|
}
|
|
3517
3899
|
});
|
|
3518
3900
|
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
@@ -3537,7 +3919,7 @@ ${tryStringify2(args.response)}`
|
|
|
3537
3919
|
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
3538
3920
|
var assertReasoningThoughtsCompatibility = (args) => {
|
|
3539
3921
|
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
3540
|
-
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.interrupts) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3922
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.interrupts) || Boolean(args.structured) || Boolean(args.outputs) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3541
3923
|
if (!usesStructuredOutput) return;
|
|
3542
3924
|
throw new Error(
|
|
3543
3925
|
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
@@ -3545,6 +3927,15 @@ ${tryStringify2(args.response)}`
|
|
|
3545
3927
|
};
|
|
3546
3928
|
async function runReason(args) {
|
|
3547
3929
|
assertReasoningThoughtsCompatibility(args);
|
|
3930
|
+
normalizeReasonOutputs(
|
|
3931
|
+
args.outputs,
|
|
3932
|
+
Boolean(args.outputSchema || args.structured)
|
|
3933
|
+
);
|
|
3934
|
+
if (args.outputSchema || args.structured) warnLegacyReasonOutput();
|
|
3935
|
+
if (args.outputs && resolveEffectiveResponseFormatType(args) === "json")
|
|
3936
|
+
throw new Error(
|
|
3937
|
+
"useReason output contracts use tool calls and cannot be combined with JSON responseFormat."
|
|
3938
|
+
);
|
|
3548
3939
|
const interruptConfig = normalizeReasonInterrupts({
|
|
3549
3940
|
interrupt: args.interrupt,
|
|
3550
3941
|
interrupts: args.interrupts
|
|
@@ -3618,7 +4009,7 @@ ${tryStringify2(args.response)}`
|
|
|
3618
4009
|
}
|
|
3619
4010
|
},
|
|
3620
4011
|
async (traceSpan) => {
|
|
3621
|
-
if ((args.tools?.length || args.interrupts) && !existingCheckpoint) {
|
|
4012
|
+
if ((args.tools?.length || args.interrupts || args.outputs) && !existingCheckpoint) {
|
|
3622
4013
|
return runReasonToolLoop({
|
|
3623
4014
|
useReasonArgs: args,
|
|
3624
4015
|
allowValidatedToolOutput,
|
|
@@ -3879,6 +4270,53 @@ ${tryStringify2(args.response)}`
|
|
|
3879
4270
|
}
|
|
3880
4271
|
}
|
|
3881
4272
|
function useStructuredData(args) {
|
|
4273
|
+
if ("contract" in args) {
|
|
4274
|
+
const contract = defineOutputContract(args.contract);
|
|
4275
|
+
const base = {
|
|
4276
|
+
dataType: contract.schemaId,
|
|
4277
|
+
schemaId: contract.schemaId,
|
|
4278
|
+
schemaVersion: contract.schemaVersion,
|
|
4279
|
+
...args.id ? { id: args.id } : {},
|
|
4280
|
+
...args.streamId ? { streamId: args.streamId } : {}
|
|
4281
|
+
};
|
|
4282
|
+
if (args.kind === "set" || args.kind === "append" || args.kind === "text-delta") {
|
|
4283
|
+
if (contract.stream?.fields[args.path] !== args.kind)
|
|
4284
|
+
throw new Error(
|
|
4285
|
+
`Output contract does not allow ${args.kind} at "${args.path}".`
|
|
4286
|
+
);
|
|
4287
|
+
if (args.kind === "set")
|
|
4288
|
+
emitStructuredData({
|
|
4289
|
+
...base,
|
|
4290
|
+
kind: "set",
|
|
4291
|
+
path: args.path,
|
|
4292
|
+
value: args.value,
|
|
4293
|
+
...args.valueSchema ? { valueSchema: args.valueSchema } : {}
|
|
4294
|
+
});
|
|
4295
|
+
else if (args.kind === "append")
|
|
4296
|
+
emitStructuredData({
|
|
4297
|
+
...base,
|
|
4298
|
+
kind: "append",
|
|
4299
|
+
path: args.path,
|
|
4300
|
+
items: args.items,
|
|
4301
|
+
...args.itemSchema ? { itemSchema: args.itemSchema } : {}
|
|
4302
|
+
});
|
|
4303
|
+
else
|
|
4304
|
+
emitStructuredData({
|
|
4305
|
+
...base,
|
|
4306
|
+
kind: "text-delta",
|
|
4307
|
+
path: args.path,
|
|
4308
|
+
delta: args.delta
|
|
4309
|
+
});
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
emitStructuredData({
|
|
4313
|
+
...base,
|
|
4314
|
+
kind: "final",
|
|
4315
|
+
data: args.data,
|
|
4316
|
+
dataSchema: contract.schema
|
|
4317
|
+
});
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
3882
4320
|
emitStructuredData(args);
|
|
3883
4321
|
}
|
|
3884
4322
|
function useReason2(args) {
|
|
@@ -5492,6 +5930,13 @@ async function orchestrateGraphStream({
|
|
|
5492
5930
|
});
|
|
5493
5931
|
return;
|
|
5494
5932
|
}
|
|
5933
|
+
if (event === "structured_data_invalidated") {
|
|
5934
|
+
const streamId = payload?.streamId;
|
|
5935
|
+
if (typeof streamId !== "string" || !streamId) return;
|
|
5936
|
+
structuredStreamIds.delete(streamId);
|
|
5937
|
+
write({ type: "structured-data-invalidated", streamId });
|
|
5938
|
+
return;
|
|
5939
|
+
}
|
|
5495
5940
|
if (event === "transition") {
|
|
5496
5941
|
write({
|
|
5497
5942
|
type: "transition",
|