@kortyx/agent 0.26.2 → 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 +32 -0
- package/dist/{browser-wjeg_MwV.d.ts → browser-EVWVR9BO.d.ts} +1 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -0
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +87 -17
- package/dist/index.js +934 -261
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ var require_internal = __commonJS({
|
|
|
61
61
|
withSafeTraceSpan: () => withSafeTraceSpan2
|
|
62
62
|
});
|
|
63
63
|
module2.exports = __toCommonJS2(internal_exports);
|
|
64
|
-
var
|
|
64
|
+
var import_errors8 = require("@kortyx/core/errors");
|
|
65
65
|
var MAX_STACK_LENGTH = 32768;
|
|
66
66
|
var MAX_CAUSE_DEPTH = 4;
|
|
67
67
|
var projectDetails = (value, depth, seen) => {
|
|
@@ -71,16 +71,16 @@ var require_internal = __commonJS({
|
|
|
71
71
|
return { type: "Error", message: "Circular error cause." };
|
|
72
72
|
seen.add(objectValue);
|
|
73
73
|
}
|
|
74
|
-
const name = (0,
|
|
75
|
-
const message = (0,
|
|
76
|
-
const stack = (0,
|
|
74
|
+
const name = (0, import_errors8.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
|
|
75
|
+
const message = (0, import_errors8.errorProperty)(value, "message");
|
|
76
|
+
const stack = (0, import_errors8.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
|
|
77
77
|
try {
|
|
78
78
|
return value.stack;
|
|
79
79
|
} catch {
|
|
80
80
|
return void 0;
|
|
81
81
|
}
|
|
82
82
|
})() : void 0);
|
|
83
|
-
const cause = depth < MAX_CAUSE_DEPTH ? (0,
|
|
83
|
+
const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors8.errorProperty)(value, "cause") : null;
|
|
84
84
|
return {
|
|
85
85
|
type: typeof name === "string" ? name.slice(0, 256) : "Error",
|
|
86
86
|
message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
|
|
@@ -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,
|
|
@@ -789,14 +790,14 @@ var require_dist = __commonJS({
|
|
|
789
790
|
workflowState: args.workflowState
|
|
790
791
|
}
|
|
791
792
|
});
|
|
792
|
-
var
|
|
793
|
+
var import_errors8 = require("@kortyx/core/errors");
|
|
793
794
|
var parseWithSchema = (schema, value, label) => {
|
|
794
795
|
if (!schema) return value;
|
|
795
796
|
const parsed = schema.safeParse(value);
|
|
796
797
|
if (parsed.success) return parsed.data;
|
|
797
798
|
const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
|
|
798
799
|
const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
|
|
799
|
-
throw new
|
|
800
|
+
throw new import_errors8.ValidationError(
|
|
800
801
|
label === "useReason output" ? "MODEL_OUTPUT_SCHEMA" : "SCHEMA_VALIDATION",
|
|
801
802
|
`${label} validation failed: ${reason}`,
|
|
802
803
|
parsedError
|
|
@@ -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) {
|
|
@@ -3950,7 +4388,7 @@ ${tryStringify2(args.response)}`
|
|
|
3950
4388
|
return [getValue(), setValue];
|
|
3951
4389
|
}
|
|
3952
4390
|
var import_core7 = require("@kortyx/core");
|
|
3953
|
-
var
|
|
4391
|
+
var import_errors72 = require("@kortyx/core/errors");
|
|
3954
4392
|
var ParallelChildWaiting = class extends Error {
|
|
3955
4393
|
name = "ParallelChildWaiting";
|
|
3956
4394
|
};
|
|
@@ -3964,14 +4402,14 @@ ${tryStringify2(args.response)}`
|
|
|
3964
4402
|
);
|
|
3965
4403
|
this.results = results;
|
|
3966
4404
|
this.name = "ParallelError";
|
|
3967
|
-
this.failure = (0,
|
|
4405
|
+
this.failure = (0, import_errors72.serializeFailure)({
|
|
3968
4406
|
version: 1,
|
|
3969
4407
|
code: this.code,
|
|
3970
4408
|
category: "workflow",
|
|
3971
4409
|
message: this.message,
|
|
3972
4410
|
retryable: null,
|
|
3973
4411
|
results: results.map(
|
|
3974
|
-
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0,
|
|
4412
|
+
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors72.serializeFailure)(result.reason) }
|
|
3975
4413
|
)
|
|
3976
4414
|
});
|
|
3977
4415
|
}
|
|
@@ -3979,7 +4417,7 @@ ${tryStringify2(args.response)}`
|
|
|
3979
4417
|
code = "PARALLEL_FAILED";
|
|
3980
4418
|
failure;
|
|
3981
4419
|
toJSON() {
|
|
3982
|
-
return (0,
|
|
4420
|
+
return (0, import_errors72.serializeFailure)(this);
|
|
3983
4421
|
}
|
|
3984
4422
|
};
|
|
3985
4423
|
function registerWorkflowTask(id) {
|
|
@@ -4124,11 +4562,11 @@ ${tryStringify2(args.response)}`
|
|
|
4124
4562
|
}
|
|
4125
4563
|
var import_node_crypto6 = require("crypto");
|
|
4126
4564
|
var import_core8 = require("@kortyx/core");
|
|
4127
|
-
var
|
|
4128
|
-
var WorkflowCallError = class extends
|
|
4565
|
+
var import_errors82 = require("@kortyx/core/errors");
|
|
4566
|
+
var WorkflowCallError = class extends import_errors82.KortyxError {
|
|
4129
4567
|
constructor(workflow, message, cause, failure) {
|
|
4130
|
-
const detail = (0,
|
|
4131
|
-
failure ?? cause ?? new
|
|
4568
|
+
const detail = (0, import_errors82.serializeFailure)(
|
|
4569
|
+
failure ?? cause ?? new import_errors82.WorkflowContractError(message)
|
|
4132
4570
|
);
|
|
4133
4571
|
super(detail.code, `Workflow '${workflow}': ${message}`, {
|
|
4134
4572
|
...detail,
|
|
@@ -4149,12 +4587,12 @@ ${tryStringify2(args.response)}`
|
|
|
4149
4587
|
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
4150
4588
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
4151
4589
|
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
4152
|
-
throw new
|
|
4590
|
+
throw new import_errors82.WorkflowContractError(
|
|
4153
4591
|
"Workflow input and output must be JSON serializable."
|
|
4154
4592
|
);
|
|
4155
4593
|
}
|
|
4156
4594
|
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
4157
|
-
throw new
|
|
4595
|
+
throw new import_errors82.WorkflowContractError(
|
|
4158
4596
|
"Workflow input and output must contain plain JSON objects."
|
|
4159
4597
|
);
|
|
4160
4598
|
}
|
|
@@ -4175,7 +4613,7 @@ ${tryStringify2(args.response)}`
|
|
|
4175
4613
|
useWorkflow: (args) => {
|
|
4176
4614
|
const workflow = workflows[args.workflow];
|
|
4177
4615
|
if (!workflow || workflow.id !== args.workflow)
|
|
4178
|
-
throw new
|
|
4616
|
+
throw new import_errors82.WorkflowContractError(
|
|
4179
4617
|
"Workflow registry keys must match definition ids."
|
|
4180
4618
|
);
|
|
4181
4619
|
return useWorkflow({ ...args, workflow });
|
|
@@ -4201,23 +4639,23 @@ ${tryStringify2(args.response)}`
|
|
|
4201
4639
|
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
4202
4640
|
const service = ctx.node.callWorkflow;
|
|
4203
4641
|
if (!service)
|
|
4204
|
-
throw new
|
|
4642
|
+
throw new import_errors82.WorkflowContractError(
|
|
4205
4643
|
"useWorkflow requires an agent workflow registry."
|
|
4206
4644
|
);
|
|
4207
4645
|
if (!args.id.trim())
|
|
4208
|
-
throw new
|
|
4646
|
+
throw new import_errors82.WorkflowContractError(
|
|
4209
4647
|
"useWorkflow requires a stable, nonempty id."
|
|
4210
4648
|
);
|
|
4211
4649
|
if (ctx.workflowCallActive && !task.group)
|
|
4212
|
-
throw new
|
|
4650
|
+
throw new import_errors82.WorkflowContractError(
|
|
4213
4651
|
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
4214
4652
|
);
|
|
4215
4653
|
if (ctx.workflowCallIds.has(args.id))
|
|
4216
|
-
throw new
|
|
4654
|
+
throw new import_errors82.WorkflowContractError(
|
|
4217
4655
|
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
4218
4656
|
);
|
|
4219
4657
|
if (ctx.workflowCallIds.size >= 64)
|
|
4220
|
-
throw new
|
|
4658
|
+
throw new import_errors82.WorkflowContractError(
|
|
4221
4659
|
"A node may call at most 64 child workflows per activation."
|
|
4222
4660
|
);
|
|
4223
4661
|
ctx.workflowCallIds.add(args.id);
|
|
@@ -4298,7 +4736,7 @@ ${tryStringify2(args.response)}`
|
|
|
4298
4736
|
workflow,
|
|
4299
4737
|
record.error ?? "child failed",
|
|
4300
4738
|
void 0,
|
|
4301
|
-
record.failure ?? (0,
|
|
4739
|
+
record.failure ?? (0, import_errors82.serializeFailure)(void 0)
|
|
4302
4740
|
);
|
|
4303
4741
|
if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
|
|
4304
4742
|
throw new ParallelChildWaiting();
|
|
@@ -4334,7 +4772,7 @@ ${tryStringify2(args.response)}`
|
|
|
4334
4772
|
throw error;
|
|
4335
4773
|
}
|
|
4336
4774
|
record.status = "failed";
|
|
4337
|
-
record.failure = (0,
|
|
4775
|
+
record.failure = (0, import_errors82.serializeFailure)(error);
|
|
4338
4776
|
record.error = record.failure.message;
|
|
4339
4777
|
report("failed", { error: "Child workflow failed" });
|
|
4340
4778
|
throw new WorkflowCallError(
|
|
@@ -4395,6 +4833,7 @@ ${tryStringify2(args.response)}`
|
|
|
4395
4833
|
// src/index.ts
|
|
4396
4834
|
var index_exports = {};
|
|
4397
4835
|
__export(index_exports, {
|
|
4836
|
+
ChatLifecycleHookError: () => ChatLifecycleHookError,
|
|
4398
4837
|
ExecutionRequestError: () => ExecutionRequestError,
|
|
4399
4838
|
createAgent: () => createAgent,
|
|
4400
4839
|
createChatRouteHandler: () => createChatRouteHandler,
|
|
@@ -4417,198 +4856,44 @@ __export(index_exports, {
|
|
|
4417
4856
|
module.exports = __toCommonJS(index_exports);
|
|
4418
4857
|
|
|
4419
4858
|
// src/adapters/http.ts
|
|
4859
|
+
var import_errors5 = require("@kortyx/core/errors");
|
|
4860
|
+
var import_stream2 = require("@kortyx/stream");
|
|
4861
|
+
var import_zod2 = require("zod");
|
|
4862
|
+
|
|
4863
|
+
// src/chat/lifecycle.ts
|
|
4420
4864
|
var import_errors = require("@kortyx/core/errors");
|
|
4421
|
-
var
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
id: import_zod.z.string().optional(),
|
|
4428
|
-
timestamp: import_zod.z.number().refine(Number.isFinite).optional()
|
|
4429
|
-
}).strict();
|
|
4430
|
-
var chatRequestBodySchema = import_zod.z.looseObject({
|
|
4431
|
-
sessionId: import_zod.z.string().optional(),
|
|
4432
|
-
workflowId: import_zod.z.string().optional(),
|
|
4433
|
-
stream: import_zod.z.boolean().optional(),
|
|
4434
|
-
context: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
|
|
4435
|
-
messages: import_zod.z.array(chatMessageSchema)
|
|
4436
|
-
});
|
|
4437
|
-
var checkpointRequestBodySchema = import_zod.z.discriminatedUnion("action", [
|
|
4438
|
-
import_zod.z.object({
|
|
4439
|
-
action: import_zod.z.literal("list"),
|
|
4440
|
-
sessionId: import_zod.z.string().min(1)
|
|
4441
|
-
}).strict(),
|
|
4442
|
-
import_zod.z.object({
|
|
4443
|
-
action: import_zod.z.literal("get"),
|
|
4444
|
-
checkpointId: import_zod.z.string().min(1)
|
|
4445
|
-
}).strict(),
|
|
4446
|
-
import_zod.z.object({
|
|
4447
|
-
action: import_zod.z.literal("rollback"),
|
|
4448
|
-
checkpointId: import_zod.z.string().min(1)
|
|
4449
|
-
}).strict(),
|
|
4450
|
-
import_zod.z.object({
|
|
4451
|
-
action: import_zod.z.literal("fork"),
|
|
4452
|
-
checkpointId: import_zod.z.string().min(1),
|
|
4453
|
-
newSessionId: import_zod.z.string().min(1).optional()
|
|
4454
|
-
}).strict()
|
|
4455
|
-
]);
|
|
4456
|
-
async function readRequestJson(request) {
|
|
4457
|
-
try {
|
|
4458
|
-
const value = await request.json();
|
|
4459
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4460
|
-
throw new Error("Expected a JSON object.");
|
|
4461
|
-
return value;
|
|
4462
|
-
} catch (cause) {
|
|
4463
|
-
throw new import_errors.KortyxError(
|
|
4464
|
-
"INVALID_REQUEST",
|
|
4465
|
-
"Request body is not valid JSON.",
|
|
4865
|
+
var ChatLifecycleHookError = class extends import_errors.KortyxError {
|
|
4866
|
+
phase;
|
|
4867
|
+
constructor(phase, cause) {
|
|
4868
|
+
super(
|
|
4869
|
+
"CHAT_LIFECYCLE_HOOK_FAILED",
|
|
4870
|
+
`Chat lifecycle hook failed: ${phase}`,
|
|
4466
4871
|
{
|
|
4467
|
-
category: "
|
|
4468
|
-
retryable:
|
|
4469
|
-
safeMessage: "
|
|
4872
|
+
category: "persistence",
|
|
4873
|
+
retryable: null,
|
|
4874
|
+
safeMessage: "Chat state could not be saved.",
|
|
4470
4875
|
cause
|
|
4471
4876
|
}
|
|
4472
4877
|
);
|
|
4878
|
+
this.name = "ChatLifecycleHookError";
|
|
4879
|
+
this.phase = phase;
|
|
4473
4880
|
}
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
4881
|
+
};
|
|
4882
|
+
|
|
4883
|
+
// src/interrupt/resume-handler.ts
|
|
4884
|
+
var import_core3 = require("@kortyx/core");
|
|
4885
|
+
var import_runtime2 = require("@kortyx/runtime");
|
|
4886
|
+
|
|
4887
|
+
// src/execution/types.ts
|
|
4888
|
+
var import_errors2 = require("@kortyx/core/errors");
|
|
4889
|
+
var ExecutionRequestError = class extends import_errors2.KortyxError {
|
|
4890
|
+
name = "ExecutionRequestError";
|
|
4891
|
+
constructor(code, message, cause) {
|
|
4892
|
+
super(code, message, {
|
|
4487
4893
|
category: "request",
|
|
4488
4894
|
retryable: false,
|
|
4489
|
-
safeMessage: "The request
|
|
4490
|
-
|
|
4491
|
-
}
|
|
4492
|
-
const sessionId = parsed.data.sessionId?.trim();
|
|
4493
|
-
const workflowId = parsed.data.workflowId?.trim();
|
|
4494
|
-
return {
|
|
4495
|
-
...sessionId ? { sessionId } : {},
|
|
4496
|
-
...workflowId ? { workflowId } : {},
|
|
4497
|
-
...typeof parsed.data.stream === "boolean" ? { stream: parsed.data.stream } : {},
|
|
4498
|
-
...parsed.data.context ? { context: parsed.data.context } : {},
|
|
4499
|
-
messages: parsed.data.messages
|
|
4500
|
-
};
|
|
4501
|
-
}
|
|
4502
|
-
function parseCheckpointRequestBody(value) {
|
|
4503
|
-
const parsed = checkpointRequestBodySchema.safeParse(value);
|
|
4504
|
-
if (parsed.success) return parsed.data;
|
|
4505
|
-
const [firstIssue] = parsed.error.issues;
|
|
4506
|
-
throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
4507
|
-
category: "request",
|
|
4508
|
-
retryable: false,
|
|
4509
|
-
safeMessage: "The request body is invalid."
|
|
4510
|
-
});
|
|
4511
|
-
}
|
|
4512
|
-
async function handleChatRequestBody(args) {
|
|
4513
|
-
const { agent, body } = args;
|
|
4514
|
-
const stream = await agent.streamChat(body.messages, {
|
|
4515
|
-
...args.onExecution ? { onExecution: args.onExecution } : {},
|
|
4516
|
-
...args.abortSignal ? { abortSignal: args.abortSignal } : {},
|
|
4517
|
-
sessionId: body.sessionId,
|
|
4518
|
-
workflowId: body.workflowId,
|
|
4519
|
-
context: body.context
|
|
4520
|
-
});
|
|
4521
|
-
if (body.stream === false) {
|
|
4522
|
-
const buffered = await (0, import_stream.collectBufferedStream)(stream);
|
|
4523
|
-
return new Response(JSON.stringify(buffered), {
|
|
4524
|
-
headers: {
|
|
4525
|
-
"content-type": "application/json"
|
|
4526
|
-
}
|
|
4527
|
-
});
|
|
4528
|
-
}
|
|
4529
|
-
return (0, import_stream.toSSE)(stream);
|
|
4530
|
-
}
|
|
4531
|
-
function createChatRouteHandler(args) {
|
|
4532
|
-
const { agent, errorStatus } = args;
|
|
4533
|
-
return async function POST(request) {
|
|
4534
|
-
try {
|
|
4535
|
-
const body = parseChatRequestBody(await readRequestJson(request));
|
|
4536
|
-
return await handleChatRequestBody({
|
|
4537
|
-
agent,
|
|
4538
|
-
body,
|
|
4539
|
-
abortSignal: request.signal,
|
|
4540
|
-
onExecution: args.onExecution
|
|
4541
|
-
});
|
|
4542
|
-
} catch (error) {
|
|
4543
|
-
return createFailureResponse(error, errorStatus);
|
|
4544
|
-
}
|
|
4545
|
-
};
|
|
4546
|
-
}
|
|
4547
|
-
async function handleCheckpointRequestBody(args) {
|
|
4548
|
-
const { agent, body } = args;
|
|
4549
|
-
const result = body.action === "list" ? await agent.listCheckpoints(body.sessionId) : body.action === "get" ? await agent.getCheckpoint(body.checkpointId) : body.action === "rollback" ? await agent.rollbackTo(body.checkpointId) : await agent.fork(body.checkpointId, {
|
|
4550
|
-
...body.newSessionId ? { newSessionId: body.newSessionId } : {}
|
|
4551
|
-
});
|
|
4552
|
-
return new Response(JSON.stringify(result), {
|
|
4553
|
-
headers: {
|
|
4554
|
-
"content-type": "application/json"
|
|
4555
|
-
}
|
|
4556
|
-
});
|
|
4557
|
-
}
|
|
4558
|
-
function createCheckpointRouteHandler(args) {
|
|
4559
|
-
const { agent, errorStatus } = args;
|
|
4560
|
-
return async function POST(request) {
|
|
4561
|
-
try {
|
|
4562
|
-
const body = parseCheckpointRequestBody(await readRequestJson(request));
|
|
4563
|
-
return await handleCheckpointRequestBody({ agent, body });
|
|
4564
|
-
} catch (error) {
|
|
4565
|
-
return createFailureResponse(error, errorStatus);
|
|
4566
|
-
}
|
|
4567
|
-
};
|
|
4568
|
-
}
|
|
4569
|
-
|
|
4570
|
-
// src/adapters/http-client.ts
|
|
4571
|
-
var import_browser = require("@kortyx/stream/browser");
|
|
4572
|
-
async function* streamChatFromRoute(args) {
|
|
4573
|
-
yield* (0, import_browser.streamFromRoute)({
|
|
4574
|
-
endpoint: args.endpoint,
|
|
4575
|
-
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {},
|
|
4576
|
-
...args.headers ? { headers: args.headers } : {},
|
|
4577
|
-
body: {
|
|
4578
|
-
sessionId: args.sessionId,
|
|
4579
|
-
workflowId: args.workflowId,
|
|
4580
|
-
messages: args.messages
|
|
4581
|
-
}
|
|
4582
|
-
});
|
|
4583
|
-
}
|
|
4584
|
-
|
|
4585
|
-
// src/chat/create-agent.ts
|
|
4586
|
-
var import_node_path = require("path");
|
|
4587
|
-
var import_core5 = require("@kortyx/core");
|
|
4588
|
-
var import_providers = require("@kortyx/providers");
|
|
4589
|
-
var import_runtime5 = require("@kortyx/runtime");
|
|
4590
|
-
var import_zod5 = require("zod");
|
|
4591
|
-
|
|
4592
|
-
// src/execution/execute.ts
|
|
4593
|
-
var import_core4 = require("@kortyx/core");
|
|
4594
|
-
var import_errors5 = require("@kortyx/core/errors");
|
|
4595
|
-
var import_runtime3 = require("@kortyx/runtime");
|
|
4596
|
-
var import_zod3 = require("zod");
|
|
4597
|
-
|
|
4598
|
-
// src/interrupt/resume-handler.ts
|
|
4599
|
-
var import_core3 = require("@kortyx/core");
|
|
4600
|
-
var import_runtime2 = require("@kortyx/runtime");
|
|
4601
|
-
|
|
4602
|
-
// src/execution/types.ts
|
|
4603
|
-
var import_errors2 = require("@kortyx/core/errors");
|
|
4604
|
-
var ExecutionRequestError = class extends import_errors2.KortyxError {
|
|
4605
|
-
name = "ExecutionRequestError";
|
|
4606
|
-
constructor(code, message, cause) {
|
|
4607
|
-
super(code, message, {
|
|
4608
|
-
category: "request",
|
|
4609
|
-
retryable: false,
|
|
4610
|
-
safeMessage: "The execution request is invalid. Check the workflow contract and request fields.",
|
|
4611
|
-
cause
|
|
4895
|
+
safeMessage: "The execution request is invalid. Check the workflow contract and request fields.",
|
|
4896
|
+
cause
|
|
4612
4897
|
});
|
|
4613
4898
|
}
|
|
4614
4899
|
};
|
|
@@ -4619,11 +4904,12 @@ var import_core2 = require("@kortyx/core");
|
|
|
4619
4904
|
var import_errors4 = require("@kortyx/core/errors");
|
|
4620
4905
|
var import_internal = __toESM(require_internal());
|
|
4621
4906
|
var import_runtime = require("@kortyx/runtime");
|
|
4907
|
+
var import_stream = require("@kortyx/stream");
|
|
4622
4908
|
var import_langgraph = require("@langchain/langgraph");
|
|
4623
4909
|
|
|
4624
4910
|
// src/execution/contracts.ts
|
|
4625
4911
|
var import_errors3 = require("@kortyx/core/errors");
|
|
4626
|
-
var
|
|
4912
|
+
var import_zod = require("zod");
|
|
4627
4913
|
async function resolveExecutionWorkflow(select, reference) {
|
|
4628
4914
|
const id = typeof reference === "string" ? reference : reference.id;
|
|
4629
4915
|
let workflow;
|
|
@@ -4655,12 +4941,12 @@ async function resolveExecutionWorkflow(select, reference) {
|
|
|
4655
4941
|
}
|
|
4656
4942
|
function parseExecutionInput(workflow, input) {
|
|
4657
4943
|
try {
|
|
4658
|
-
|
|
4944
|
+
import_zod.z.json().parse(input);
|
|
4659
4945
|
const parsed = workflow.inputSchema ? workflow.inputSchema.parse(input) : input;
|
|
4660
|
-
|
|
4946
|
+
import_zod.z.json().parse(parsed);
|
|
4661
4947
|
return parsed;
|
|
4662
4948
|
} catch (error) {
|
|
4663
|
-
if (!(error instanceof
|
|
4949
|
+
if (!(error instanceof import_zod.z.ZodError)) throw error;
|
|
4664
4950
|
throw new ExecutionRequestError("INVALID_INPUT", error.message, error);
|
|
4665
4951
|
}
|
|
4666
4952
|
}
|
|
@@ -4678,9 +4964,9 @@ async function validateExecutionOutput(state, select) {
|
|
|
4678
4964
|
for (const schema of /* @__PURE__ */ new Set([terminal.outputSchema, root.outputSchema])) {
|
|
4679
4965
|
if (schema) data = schema.parse(data);
|
|
4680
4966
|
}
|
|
4681
|
-
if (terminal.outputSchema || root.outputSchema)
|
|
4967
|
+
if (terminal.outputSchema || root.outputSchema) import_zod.z.json().parse(data);
|
|
4682
4968
|
} catch (error) {
|
|
4683
|
-
if (!(error instanceof
|
|
4969
|
+
if (!(error instanceof import_zod.z.ZodError)) throw error;
|
|
4684
4970
|
const failure = new import_errors3.ValidationError(
|
|
4685
4971
|
"INVALID_OUTPUT",
|
|
4686
4972
|
`Workflow output validation failed: ${error.message}`,
|
|
@@ -5091,6 +5377,9 @@ async function orchestrateGraphStream({
|
|
|
5091
5377
|
abortSignal: requestSignal,
|
|
5092
5378
|
executionSignal,
|
|
5093
5379
|
onExecution,
|
|
5380
|
+
clientTurnId,
|
|
5381
|
+
continueOnDisconnect = false,
|
|
5382
|
+
onResponseFinalized,
|
|
5094
5383
|
emitOutput = true,
|
|
5095
5384
|
onOutcome,
|
|
5096
5385
|
selectWorkflow,
|
|
@@ -5099,12 +5388,34 @@ async function orchestrateGraphStream({
|
|
|
5099
5388
|
executionLeaseRelease
|
|
5100
5389
|
}) {
|
|
5101
5390
|
const out = new import_node_stream.PassThrough({ objectMode: true });
|
|
5391
|
+
const visibleMessage = emitOutput && clientTurnId && onResponseFinalized && initialConfig.responseCompleted !== true ? (0, import_stream.createFinalizedChatMessageAccumulator)(clientTurnId) : void 0;
|
|
5392
|
+
let responseFinalized = false;
|
|
5393
|
+
const finalizeVisibleResponse = async (status) => {
|
|
5394
|
+
if (!visibleMessage || !onResponseFinalized || !clientTurnId || !sessionId || responseFinalized)
|
|
5395
|
+
return;
|
|
5396
|
+
responseFinalized = true;
|
|
5397
|
+
try {
|
|
5398
|
+
await onResponseFinalized({
|
|
5399
|
+
sessionId,
|
|
5400
|
+
runId,
|
|
5401
|
+
clientTurnId,
|
|
5402
|
+
status,
|
|
5403
|
+
message: visibleMessage.message(),
|
|
5404
|
+
...sessionCheckpointId ? { checkpointId: sessionCheckpointId } : {}
|
|
5405
|
+
});
|
|
5406
|
+
} catch (error) {
|
|
5407
|
+
console.error("[chat:onResponseFinalized]", (0, import_errors4.serializeFailure)(error));
|
|
5408
|
+
}
|
|
5409
|
+
};
|
|
5102
5410
|
const write = (chunk) => {
|
|
5103
5411
|
if (isRecord3(chunk) && chunk.type === "done" && isGraphState(chunk.data)) {
|
|
5104
5412
|
const { __kortyxParallelGraph: _journal, ...runtime } = chunk.data.runtime;
|
|
5105
5413
|
chunk = { ...chunk, data: { ...chunk.data, runtime } };
|
|
5106
5414
|
}
|
|
5107
|
-
if (emitOutput && !response.closed
|
|
5415
|
+
if (emitOutput && !response.closed) {
|
|
5416
|
+
visibleMessage?.apply(chunk);
|
|
5417
|
+
if (!out.destroyed) out.write(chunk);
|
|
5418
|
+
}
|
|
5108
5419
|
};
|
|
5109
5420
|
let outcomeState = state;
|
|
5110
5421
|
let outcomeError;
|
|
@@ -5119,7 +5430,7 @@ async function orchestrateGraphStream({
|
|
|
5119
5430
|
const response = createResponseLifecycle({
|
|
5120
5431
|
enabled: emitOutput,
|
|
5121
5432
|
restored: initialConfig.responseCompleted === true,
|
|
5122
|
-
requestSignal,
|
|
5433
|
+
requestSignal: continueOnDisconnect ? void 0 : requestSignal,
|
|
5123
5434
|
executionSignal: executionSignal ? AbortSignal.any([executionSignal, leaseAbort.signal]) : leaseAbort.signal,
|
|
5124
5435
|
onClosed: () => {
|
|
5125
5436
|
out.write({ type: "done" });
|
|
@@ -5183,11 +5494,12 @@ async function orchestrateGraphStream({
|
|
|
5183
5494
|
},
|
|
5184
5495
|
flush: true
|
|
5185
5496
|
});
|
|
5497
|
+
await finalizeVisibleResponse("completed");
|
|
5186
5498
|
}
|
|
5187
5499
|
});
|
|
5188
5500
|
const abortSignal = response.signal;
|
|
5189
5501
|
out.on("close", () => {
|
|
5190
|
-
if (!finished) response.disconnect();
|
|
5502
|
+
if (!finished && !continueOnDisconnect) response.disconnect();
|
|
5191
5503
|
});
|
|
5192
5504
|
let structuredSeq = 0;
|
|
5193
5505
|
const debugEnabled = Boolean(config?.features?.tracing);
|
|
@@ -5618,6 +5930,13 @@ async function orchestrateGraphStream({
|
|
|
5618
5930
|
});
|
|
5619
5931
|
return;
|
|
5620
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
|
+
}
|
|
5621
5940
|
if (event === "transition") {
|
|
5622
5941
|
write({
|
|
5623
5942
|
type: "transition",
|
|
@@ -5940,6 +6259,9 @@ async function orchestrateGraphStream({
|
|
|
5940
6259
|
);
|
|
5941
6260
|
}
|
|
5942
6261
|
}
|
|
6262
|
+
await finalizeVisibleResponse(
|
|
6263
|
+
outcomeError ? "failed" : shouldKeepFrameworkState ? "interrupted" : "completed"
|
|
6264
|
+
);
|
|
5943
6265
|
write({ type: "done", data: workflowFinalState });
|
|
5944
6266
|
out.end();
|
|
5945
6267
|
return;
|
|
@@ -6017,6 +6339,9 @@ async function orchestrateGraphStream({
|
|
|
6017
6339
|
});
|
|
6018
6340
|
finished = true;
|
|
6019
6341
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
6342
|
+
await finalizeVisibleResponse(
|
|
6343
|
+
outcomeError ? "failed" : pendingRecordToken || naturalFinalState.awaitingHumanInput ? "interrupted" : "completed"
|
|
6344
|
+
);
|
|
6020
6345
|
write({ type: "done", data: naturalFinalState });
|
|
6021
6346
|
out.end();
|
|
6022
6347
|
return;
|
|
@@ -6077,6 +6402,7 @@ async function orchestrateGraphStream({
|
|
|
6077
6402
|
fallbackRunSpan?.setAttributes?.({ "kortyx.run.cancelled": true });
|
|
6078
6403
|
fallbackRunSpan?.end?.();
|
|
6079
6404
|
write({ type: "cancelled", runId, reason: "Execution cancelled." });
|
|
6405
|
+
await finalizeVisibleResponse("cancelled");
|
|
6080
6406
|
write({ type: "done" });
|
|
6081
6407
|
finished = true;
|
|
6082
6408
|
out.end();
|
|
@@ -6223,6 +6549,9 @@ async function orchestrateGraphStream({
|
|
|
6223
6549
|
console.error("[execution:releaseLease]", (0, import_errors4.serializeFailure)(error));
|
|
6224
6550
|
}
|
|
6225
6551
|
response.dispose();
|
|
6552
|
+
await finalizeVisibleResponse(
|
|
6553
|
+
outcomeError ? (0, import_core2.isExecutionCancelled)(outcomeError) ? "cancelled" : "failed" : pendingRecordToken || outcomeState.awaitingHumanInput ? "interrupted" : "completed"
|
|
6554
|
+
);
|
|
6226
6555
|
onOutcome?.({
|
|
6227
6556
|
state: outcomeState,
|
|
6228
6557
|
pending: pendingRecordToken ? activePendingRequests.get(pendingRecordToken) : void 0,
|
|
@@ -6272,6 +6601,9 @@ async function tryPrepareResumeStream({
|
|
|
6272
6601
|
abortSignal,
|
|
6273
6602
|
executionSignal,
|
|
6274
6603
|
onExecution,
|
|
6604
|
+
clientTurnId,
|
|
6605
|
+
continueOnDisconnect,
|
|
6606
|
+
onResponseFinalized,
|
|
6275
6607
|
lastMessage,
|
|
6276
6608
|
meta: suppliedMeta,
|
|
6277
6609
|
emitOutput,
|
|
@@ -6490,6 +6822,9 @@ async function tryPrepareResumeStream({
|
|
|
6490
6822
|
executionSignal: continuedSignal ? AbortSignal.any([continuedSignal, leaseAbort.signal]) : leaseAbort.signal,
|
|
6491
6823
|
executionLeaseRelease: releaseLease,
|
|
6492
6824
|
onExecution,
|
|
6825
|
+
clientTurnId,
|
|
6826
|
+
continueOnDisconnect,
|
|
6827
|
+
onResponseFinalized,
|
|
6493
6828
|
emitOutput,
|
|
6494
6829
|
onOutcome,
|
|
6495
6830
|
sessionId,
|
|
@@ -6526,7 +6861,316 @@ async function tryPrepareResumeStream({
|
|
|
6526
6861
|
}
|
|
6527
6862
|
}
|
|
6528
6863
|
|
|
6864
|
+
// src/adapters/http.ts
|
|
6865
|
+
var reportLifecycleError = async (callback, event) => {
|
|
6866
|
+
if (!callback) {
|
|
6867
|
+
console.error("[chat:lifecycle]", event.error);
|
|
6868
|
+
return;
|
|
6869
|
+
}
|
|
6870
|
+
try {
|
|
6871
|
+
await callback(event);
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
console.error("[chat:onLifecycleError]", (0, import_errors5.serializeFailure)(error));
|
|
6874
|
+
}
|
|
6875
|
+
};
|
|
6876
|
+
var chatMessageSchema = import_zod2.z.object({
|
|
6877
|
+
role: import_zod2.z.enum(["user", "assistant", "system"]),
|
|
6878
|
+
content: import_zod2.z.string(),
|
|
6879
|
+
metadata: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional(),
|
|
6880
|
+
id: import_zod2.z.string().optional(),
|
|
6881
|
+
timestamp: import_zod2.z.number().refine(Number.isFinite).optional()
|
|
6882
|
+
}).strict();
|
|
6883
|
+
var chatRequestBodySchema = import_zod2.z.looseObject({
|
|
6884
|
+
sessionId: import_zod2.z.string().optional(),
|
|
6885
|
+
clientTurnId: import_zod2.z.string().max(128).optional(),
|
|
6886
|
+
workflowId: import_zod2.z.string().optional(),
|
|
6887
|
+
stream: import_zod2.z.boolean().optional(),
|
|
6888
|
+
context: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional(),
|
|
6889
|
+
messages: import_zod2.z.array(chatMessageSchema)
|
|
6890
|
+
});
|
|
6891
|
+
var checkpointRequestBodySchema = import_zod2.z.discriminatedUnion("action", [
|
|
6892
|
+
import_zod2.z.object({
|
|
6893
|
+
action: import_zod2.z.literal("list"),
|
|
6894
|
+
sessionId: import_zod2.z.string().min(1)
|
|
6895
|
+
}).strict(),
|
|
6896
|
+
import_zod2.z.object({
|
|
6897
|
+
action: import_zod2.z.literal("get"),
|
|
6898
|
+
checkpointId: import_zod2.z.string().min(1)
|
|
6899
|
+
}).strict(),
|
|
6900
|
+
import_zod2.z.object({
|
|
6901
|
+
action: import_zod2.z.literal("rollback"),
|
|
6902
|
+
checkpointId: import_zod2.z.string().min(1)
|
|
6903
|
+
}).strict(),
|
|
6904
|
+
import_zod2.z.object({
|
|
6905
|
+
action: import_zod2.z.literal("fork"),
|
|
6906
|
+
checkpointId: import_zod2.z.string().min(1),
|
|
6907
|
+
newSessionId: import_zod2.z.string().min(1).optional()
|
|
6908
|
+
}).strict()
|
|
6909
|
+
]);
|
|
6910
|
+
async function readRequestJson(request) {
|
|
6911
|
+
try {
|
|
6912
|
+
const value = await request.json();
|
|
6913
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
6914
|
+
throw new Error("Expected a JSON object.");
|
|
6915
|
+
return value;
|
|
6916
|
+
} catch (cause) {
|
|
6917
|
+
throw new import_errors5.KortyxError(
|
|
6918
|
+
"INVALID_REQUEST",
|
|
6919
|
+
"Request body is not valid JSON.",
|
|
6920
|
+
{
|
|
6921
|
+
category: "request",
|
|
6922
|
+
retryable: false,
|
|
6923
|
+
safeMessage: "The request body must contain a valid JSON object.",
|
|
6924
|
+
cause
|
|
6925
|
+
}
|
|
6926
|
+
);
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
function createFailureResponse(error, status) {
|
|
6930
|
+
const failure = (0, import_errors5.serializeFailure)(error);
|
|
6931
|
+
return Response.json(
|
|
6932
|
+
{ error: failure.message, failure },
|
|
6933
|
+
{ status: status ?? (0, import_errors5.failureHttpStatus)(failure) }
|
|
6934
|
+
);
|
|
6935
|
+
}
|
|
6936
|
+
function parseChatRequestBody(value) {
|
|
6937
|
+
const parsed = chatRequestBodySchema.safeParse(value);
|
|
6938
|
+
if (!parsed.success) {
|
|
6939
|
+
const [firstIssue] = parsed.error.issues;
|
|
6940
|
+
throw new import_errors5.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
6941
|
+
category: "request",
|
|
6942
|
+
retryable: false,
|
|
6943
|
+
safeMessage: "The request body is invalid."
|
|
6944
|
+
});
|
|
6945
|
+
}
|
|
6946
|
+
const sessionId = parsed.data.sessionId?.trim();
|
|
6947
|
+
const workflowId = parsed.data.workflowId?.trim();
|
|
6948
|
+
const clientTurnId = parsed.data.clientTurnId?.trim();
|
|
6949
|
+
return {
|
|
6950
|
+
...sessionId ? { sessionId } : {},
|
|
6951
|
+
...workflowId ? { workflowId } : {},
|
|
6952
|
+
...clientTurnId ? { clientTurnId } : {},
|
|
6953
|
+
...typeof parsed.data.stream === "boolean" ? { stream: parsed.data.stream } : {},
|
|
6954
|
+
...parsed.data.context ? { context: parsed.data.context } : {},
|
|
6955
|
+
messages: parsed.data.messages
|
|
6956
|
+
};
|
|
6957
|
+
}
|
|
6958
|
+
function parseCheckpointRequestBody(value) {
|
|
6959
|
+
const parsed = checkpointRequestBodySchema.safeParse(value);
|
|
6960
|
+
if (parsed.success) return parsed.data;
|
|
6961
|
+
const [firstIssue] = parsed.error.issues;
|
|
6962
|
+
throw new import_errors5.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
6963
|
+
category: "request",
|
|
6964
|
+
retryable: false,
|
|
6965
|
+
safeMessage: "The request body is invalid."
|
|
6966
|
+
});
|
|
6967
|
+
}
|
|
6968
|
+
async function handleChatRequestBody(args) {
|
|
6969
|
+
const { agent, body } = args;
|
|
6970
|
+
if (args.onResponseFinalized && (!body.sessionId || !body.clientTurnId)) {
|
|
6971
|
+
throw new import_errors5.KortyxError(
|
|
6972
|
+
"INVALID_REQUEST",
|
|
6973
|
+
"sessionId and clientTurnId are required with onResponseFinalized.",
|
|
6974
|
+
{
|
|
6975
|
+
category: "request",
|
|
6976
|
+
retryable: false,
|
|
6977
|
+
safeMessage: "This chat request needs a session and turn ID."
|
|
6978
|
+
}
|
|
6979
|
+
);
|
|
6980
|
+
}
|
|
6981
|
+
const stream = await agent.streamChat(body.messages, {
|
|
6982
|
+
...args.onExecution ? { onExecution: args.onExecution } : {},
|
|
6983
|
+
...body.clientTurnId ? { clientTurnId: body.clientTurnId } : {},
|
|
6984
|
+
...args.continueOnDisconnect ? { continueOnDisconnect: true } : {},
|
|
6985
|
+
...args.onResponseFinalized ? { onResponseFinalized: args.onResponseFinalized } : {},
|
|
6986
|
+
...args.abortSignal && !args.continueOnDisconnect ? { abortSignal: args.abortSignal } : {},
|
|
6987
|
+
sessionId: body.sessionId,
|
|
6988
|
+
workflowId: body.workflowId,
|
|
6989
|
+
context: body.context
|
|
6990
|
+
});
|
|
6991
|
+
if (body.stream === false) {
|
|
6992
|
+
const buffered = await (0, import_stream2.collectBufferedStream)(stream);
|
|
6993
|
+
return new Response(JSON.stringify(buffered), {
|
|
6994
|
+
headers: {
|
|
6995
|
+
"content-type": "application/json"
|
|
6996
|
+
}
|
|
6997
|
+
});
|
|
6998
|
+
}
|
|
6999
|
+
return (0, import_stream2.toSSE)(stream);
|
|
7000
|
+
}
|
|
7001
|
+
function createChatRouteHandler(args) {
|
|
7002
|
+
const { agent, errorStatus } = args;
|
|
7003
|
+
if (args.disconnect === "continue" && !args.onExecution) {
|
|
7004
|
+
throw new Error(
|
|
7005
|
+
'disconnect: "continue" requires onExecution to retain host lifetime.'
|
|
7006
|
+
);
|
|
7007
|
+
}
|
|
7008
|
+
return async function POST(request) {
|
|
7009
|
+
try {
|
|
7010
|
+
const body = parseChatRequestBody(await readRequestJson(request));
|
|
7011
|
+
if (args.onTurnAccepted || args.onResponseFinalized) {
|
|
7012
|
+
if (!body.sessionId || !body.clientTurnId) {
|
|
7013
|
+
throw new import_errors5.KortyxError(
|
|
7014
|
+
"INVALID_REQUEST",
|
|
7015
|
+
"sessionId and clientTurnId are required with chat lifecycle hooks.",
|
|
7016
|
+
{
|
|
7017
|
+
category: "request",
|
|
7018
|
+
retryable: false,
|
|
7019
|
+
safeMessage: "This chat request needs a session and turn ID."
|
|
7020
|
+
}
|
|
7021
|
+
);
|
|
7022
|
+
}
|
|
7023
|
+
const userMessage = body.messages.at(-1);
|
|
7024
|
+
if (!userMessage || userMessage.role !== "user") {
|
|
7025
|
+
throw new import_errors5.KortyxError(
|
|
7026
|
+
"INVALID_REQUEST",
|
|
7027
|
+
"The last chat message must be from the user.",
|
|
7028
|
+
{
|
|
7029
|
+
category: "request",
|
|
7030
|
+
retryable: false,
|
|
7031
|
+
safeMessage: "The chat request needs a user message."
|
|
7032
|
+
}
|
|
7033
|
+
);
|
|
7034
|
+
}
|
|
7035
|
+
if (args.onTurnAccepted) {
|
|
7036
|
+
try {
|
|
7037
|
+
await args.onTurnAccepted({
|
|
7038
|
+
request,
|
|
7039
|
+
sessionId: body.sessionId,
|
|
7040
|
+
clientTurnId: body.clientTurnId,
|
|
7041
|
+
userMessage: { ...userMessage, role: "user" },
|
|
7042
|
+
kind: parseResumeMeta(userMessage) ? "interrupt-response" : "prompt"
|
|
7043
|
+
});
|
|
7044
|
+
} catch (cause) {
|
|
7045
|
+
const error = new ChatLifecycleHookError("turn-accepted", cause);
|
|
7046
|
+
await reportLifecycleError(args.onLifecycleError, {
|
|
7047
|
+
phase: "turn-accepted",
|
|
7048
|
+
clientTurnId: body.clientTurnId,
|
|
7049
|
+
error
|
|
7050
|
+
});
|
|
7051
|
+
throw error;
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
return await handleChatRequestBody({
|
|
7056
|
+
agent,
|
|
7057
|
+
body,
|
|
7058
|
+
abortSignal: request.signal,
|
|
7059
|
+
onExecution: args.onExecution,
|
|
7060
|
+
continueOnDisconnect: args.disconnect === "continue",
|
|
7061
|
+
...args.onResponseFinalized ? {
|
|
7062
|
+
onResponseFinalized: async (event) => {
|
|
7063
|
+
try {
|
|
7064
|
+
await args.onResponseFinalized?.({ ...event, request });
|
|
7065
|
+
} catch (cause) {
|
|
7066
|
+
await reportLifecycleError(args.onLifecycleError, {
|
|
7067
|
+
phase: "response-finalized",
|
|
7068
|
+
clientTurnId: event.clientTurnId,
|
|
7069
|
+
error: new ChatLifecycleHookError(
|
|
7070
|
+
"response-finalized",
|
|
7071
|
+
cause
|
|
7072
|
+
)
|
|
7073
|
+
});
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
} : {}
|
|
7077
|
+
});
|
|
7078
|
+
} catch (error) {
|
|
7079
|
+
return createFailureResponse(error, errorStatus);
|
|
7080
|
+
}
|
|
7081
|
+
};
|
|
7082
|
+
}
|
|
7083
|
+
async function handleCheckpointRequestBody(args) {
|
|
7084
|
+
const { agent, body } = args;
|
|
7085
|
+
const result = body.action === "list" ? await agent.listCheckpoints(body.sessionId) : body.action === "get" ? await agent.getCheckpoint(body.checkpointId) : body.action === "rollback" ? await agent.rollbackTo(body.checkpointId) : await agent.fork(body.checkpointId, {
|
|
7086
|
+
...body.newSessionId ? { newSessionId: body.newSessionId } : {}
|
|
7087
|
+
});
|
|
7088
|
+
if (args.request && body.action === "fork" && args.onForked) {
|
|
7089
|
+
const fork = result;
|
|
7090
|
+
try {
|
|
7091
|
+
await args.onForked({
|
|
7092
|
+
request: args.request,
|
|
7093
|
+
sourceSessionId: fork.parentSessionId,
|
|
7094
|
+
newSessionId: fork.sessionId,
|
|
7095
|
+
sourceCheckpointId: fork.forkedFrom,
|
|
7096
|
+
newCheckpointId: fork.checkpoint.id
|
|
7097
|
+
});
|
|
7098
|
+
} catch (cause) {
|
|
7099
|
+
await reportLifecycleError(args.onLifecycleError, {
|
|
7100
|
+
phase: "forked",
|
|
7101
|
+
error: new ChatLifecycleHookError("forked", cause)
|
|
7102
|
+
});
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
if (args.request && body.action === "rollback" && args.onRolledBack) {
|
|
7106
|
+
const rollback = result;
|
|
7107
|
+
try {
|
|
7108
|
+
await args.onRolledBack({
|
|
7109
|
+
request: args.request,
|
|
7110
|
+
sessionId: rollback.sessionId,
|
|
7111
|
+
headCheckpointId: rollback.head,
|
|
7112
|
+
invalidatedStructuredStreamIds: rollback.invalidatedStructuredStreamIds
|
|
7113
|
+
});
|
|
7114
|
+
} catch (cause) {
|
|
7115
|
+
await reportLifecycleError(args.onLifecycleError, {
|
|
7116
|
+
phase: "rolled-back",
|
|
7117
|
+
error: new ChatLifecycleHookError("rolled-back", cause)
|
|
7118
|
+
});
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
return new Response(JSON.stringify(result), {
|
|
7122
|
+
headers: {
|
|
7123
|
+
"content-type": "application/json"
|
|
7124
|
+
}
|
|
7125
|
+
});
|
|
7126
|
+
}
|
|
7127
|
+
function createCheckpointRouteHandler(args) {
|
|
7128
|
+
const { agent, errorStatus } = args;
|
|
7129
|
+
return async function POST(request) {
|
|
7130
|
+
try {
|
|
7131
|
+
const body = parseCheckpointRequestBody(await readRequestJson(request));
|
|
7132
|
+
return await handleCheckpointRequestBody({
|
|
7133
|
+
agent,
|
|
7134
|
+
body,
|
|
7135
|
+
request,
|
|
7136
|
+
onForked: args.onForked,
|
|
7137
|
+
onRolledBack: args.onRolledBack,
|
|
7138
|
+
onLifecycleError: args.onLifecycleError
|
|
7139
|
+
});
|
|
7140
|
+
} catch (error) {
|
|
7141
|
+
return createFailureResponse(error, errorStatus);
|
|
7142
|
+
}
|
|
7143
|
+
};
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
// src/adapters/http-client.ts
|
|
7147
|
+
var import_browser = require("@kortyx/stream/browser");
|
|
7148
|
+
async function* streamChatFromRoute(args) {
|
|
7149
|
+
yield* (0, import_browser.streamFromRoute)({
|
|
7150
|
+
endpoint: args.endpoint,
|
|
7151
|
+
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {},
|
|
7152
|
+
...args.headers ? { headers: args.headers } : {},
|
|
7153
|
+
body: {
|
|
7154
|
+
sessionId: args.sessionId,
|
|
7155
|
+
clientTurnId: args.clientTurnId,
|
|
7156
|
+
workflowId: args.workflowId,
|
|
7157
|
+
messages: args.messages
|
|
7158
|
+
}
|
|
7159
|
+
});
|
|
7160
|
+
}
|
|
7161
|
+
|
|
7162
|
+
// src/chat/create-agent.ts
|
|
7163
|
+
var import_node_path = require("path");
|
|
7164
|
+
var import_core5 = require("@kortyx/core");
|
|
7165
|
+
var import_providers = require("@kortyx/providers");
|
|
7166
|
+
var import_runtime5 = require("@kortyx/runtime");
|
|
7167
|
+
var import_zod5 = require("zod");
|
|
7168
|
+
|
|
6529
7169
|
// src/execution/execute.ts
|
|
7170
|
+
var import_core4 = require("@kortyx/core");
|
|
7171
|
+
var import_errors6 = require("@kortyx/core/errors");
|
|
7172
|
+
var import_runtime3 = require("@kortyx/runtime");
|
|
7173
|
+
var import_zod3 = require("zod");
|
|
6530
7174
|
var handleSchema = import_zod3.z.object({
|
|
6531
7175
|
token: import_zod3.z.string().min(1),
|
|
6532
7176
|
requestId: import_zod3.z.string().min(1),
|
|
@@ -6557,7 +7201,7 @@ function resultFromOutcome(outcome, runId, sessionId) {
|
|
|
6557
7201
|
return {
|
|
6558
7202
|
...info,
|
|
6559
7203
|
status: "failed",
|
|
6560
|
-
error: (0,
|
|
7204
|
+
error: (0, import_errors6.serializeFailure)(error)
|
|
6561
7205
|
};
|
|
6562
7206
|
}
|
|
6563
7207
|
if (outcome.cancelled)
|
|
@@ -6738,7 +7382,7 @@ async function resumeWorkflow(services, args) {
|
|
|
6738
7382
|
);
|
|
6739
7383
|
}).catch(
|
|
6740
7384
|
(error) => reject(
|
|
6741
|
-
error instanceof Error ? error : new
|
|
7385
|
+
error instanceof Error ? error : new import_errors6.KortyxError("RESUME_FAILED", "Resume failed.", {
|
|
6742
7386
|
category: "persistence",
|
|
6743
7387
|
cause: error,
|
|
6744
7388
|
safeMessage: "The saved execution could not be resumed."
|
|
@@ -6941,6 +7585,7 @@ function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
|
|
|
6941
7585
|
}
|
|
6942
7586
|
|
|
6943
7587
|
// src/chat/process-chat.ts
|
|
7588
|
+
var import_errors7 = require("@kortyx/core/errors");
|
|
6944
7589
|
var import_runtime4 = require("@kortyx/runtime");
|
|
6945
7590
|
|
|
6946
7591
|
// src/utils/extract-latest-message.ts
|
|
@@ -6961,6 +7606,9 @@ async function streamChat({
|
|
|
6961
7606
|
abortSignal,
|
|
6962
7607
|
executionSignal,
|
|
6963
7608
|
onExecution,
|
|
7609
|
+
clientTurnId,
|
|
7610
|
+
continueOnDisconnect,
|
|
7611
|
+
onResponseFinalized,
|
|
6964
7612
|
options,
|
|
6965
7613
|
sessionId,
|
|
6966
7614
|
defaultWorkflowId,
|
|
@@ -6972,6 +7620,18 @@ async function streamChat({
|
|
|
6972
7620
|
getProvider,
|
|
6973
7621
|
applyResumeSelection
|
|
6974
7622
|
}) {
|
|
7623
|
+
const suppliedSessionId = sessionId || options?.sessionId;
|
|
7624
|
+
if (onResponseFinalized && (!suppliedSessionId || !clientTurnId?.trim() || clientTurnId.length > 128)) {
|
|
7625
|
+
throw new import_errors7.KortyxError(
|
|
7626
|
+
"INVALID_REQUEST",
|
|
7627
|
+
"sessionId and clientTurnId are required with onResponseFinalized.",
|
|
7628
|
+
{
|
|
7629
|
+
category: "request",
|
|
7630
|
+
retryable: false,
|
|
7631
|
+
safeMessage: "This chat request needs a session and turn ID."
|
|
7632
|
+
}
|
|
7633
|
+
);
|
|
7634
|
+
}
|
|
6975
7635
|
const config = await loadRuntimeConfig(options);
|
|
6976
7636
|
let runtimeConfig2 = {
|
|
6977
7637
|
...config,
|
|
@@ -7010,6 +7670,9 @@ async function streamChat({
|
|
|
7010
7670
|
abortSignal,
|
|
7011
7671
|
executionSignal,
|
|
7012
7672
|
onExecution,
|
|
7673
|
+
clientTurnId,
|
|
7674
|
+
continueOnDisconnect,
|
|
7675
|
+
onResponseFinalized,
|
|
7013
7676
|
lastMessage: last,
|
|
7014
7677
|
sessionId: resolvedSessionId,
|
|
7015
7678
|
config: runtimeConfig2,
|
|
@@ -7083,6 +7746,9 @@ async function streamChat({
|
|
|
7083
7746
|
abortSignal,
|
|
7084
7747
|
executionSignal,
|
|
7085
7748
|
onExecution,
|
|
7749
|
+
clientTurnId,
|
|
7750
|
+
continueOnDisconnect,
|
|
7751
|
+
onResponseFinalized,
|
|
7086
7752
|
sessionId: resolvedSessionId,
|
|
7087
7753
|
runId,
|
|
7088
7754
|
graph,
|
|
@@ -7101,6 +7767,9 @@ var agentProcessOptionsSchema = import_zod5.z.object({
|
|
|
7101
7767
|
onExecution: import_zod5.z.custom(
|
|
7102
7768
|
(value) => typeof value === "function"
|
|
7103
7769
|
).optional(),
|
|
7770
|
+
clientTurnId: import_zod5.z.string().min(1).max(128).optional(),
|
|
7771
|
+
continueOnDisconnect: import_zod5.z.boolean().optional(),
|
|
7772
|
+
onResponseFinalized: import_zod5.z.custom((value) => typeof value === "function").optional(),
|
|
7104
7773
|
limits: import_core5.ExecutionLimitsSchema.optional(),
|
|
7105
7774
|
abortSignal: import_zod5.z.custom((value) => value instanceof AbortSignal).optional(),
|
|
7106
7775
|
sessionId: import_zod5.z.string().optional(),
|
|
@@ -7247,6 +7916,9 @@ function createAgent(args) {
|
|
|
7247
7916
|
abortSignal: parsedOptions?.abortSignal,
|
|
7248
7917
|
executionSignal: parsedOptions?.executionSignal,
|
|
7249
7918
|
onExecution: parsedOptions?.onExecution,
|
|
7919
|
+
clientTurnId: parsedOptions?.clientTurnId,
|
|
7920
|
+
continueOnDisconnect: parsedOptions?.continueOnDisconnect,
|
|
7921
|
+
onResponseFinalized: parsedOptions?.onResponseFinalized,
|
|
7250
7922
|
options: parsedOptions,
|
|
7251
7923
|
workflowRegistry: registry,
|
|
7252
7924
|
...knownWorkflowIds ? { knownWorkflowIds } : {},
|
|
@@ -7403,6 +8075,7 @@ function createAgent(args) {
|
|
|
7403
8075
|
}
|
|
7404
8076
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7405
8077
|
0 && (module.exports = {
|
|
8078
|
+
ChatLifecycleHookError,
|
|
7406
8079
|
ExecutionRequestError,
|
|
7407
8080
|
createAgent,
|
|
7408
8081
|
createChatRouteHandler,
|