@kortyx/agent 0.25.0 → 0.26.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/index.d.ts +5 -0
- package/dist/index.js +437 -72
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ var require_internal = __commonJS({
|
|
|
52
52
|
var internal_exports = {};
|
|
53
53
|
__export2(internal_exports, {
|
|
54
54
|
errorDiagnostics: () => errorDiagnostics,
|
|
55
|
+
exceptionDiagnostics: () => exceptionDiagnostics,
|
|
55
56
|
isModelTraceSpan: () => isModelTraceSpan,
|
|
56
57
|
runReasonEngine: () => runReasonEngine,
|
|
57
58
|
safeSpan: () => safeSpan,
|
|
@@ -61,26 +62,53 @@ var require_internal = __commonJS({
|
|
|
61
62
|
});
|
|
62
63
|
module2.exports = __toCommonJS2(internal_exports);
|
|
63
64
|
var import_errors6 = require("@kortyx/core/errors");
|
|
64
|
-
|
|
65
|
+
var MAX_STACK_LENGTH = 32768;
|
|
66
|
+
var MAX_CAUSE_DEPTH = 4;
|
|
67
|
+
var projectDetails = (value, depth, seen) => {
|
|
68
|
+
const objectValue = value && typeof value === "object" ? value : void 0;
|
|
69
|
+
if (objectValue) {
|
|
70
|
+
if (seen.has(objectValue))
|
|
71
|
+
return { type: "Error", message: "Circular error cause." };
|
|
72
|
+
seen.add(objectValue);
|
|
73
|
+
}
|
|
74
|
+
const name = (0, import_errors6.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
|
|
75
|
+
const message = (0, import_errors6.errorProperty)(value, "message");
|
|
76
|
+
const stack = (0, import_errors6.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
|
|
77
|
+
try {
|
|
78
|
+
return value.stack;
|
|
79
|
+
} catch {
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
})() : void 0);
|
|
83
|
+
const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors6.errorProperty)(value, "cause") : null;
|
|
84
|
+
return {
|
|
85
|
+
type: typeof name === "string" ? name.slice(0, 256) : "Error",
|
|
86
|
+
message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
|
|
87
|
+
...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
88
|
+
...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
var sanitizeProjectedDetails = (details, depth = 0) => ({
|
|
92
|
+
...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
|
|
93
|
+
message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
|
|
94
|
+
...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
95
|
+
...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
|
|
96
|
+
});
|
|
97
|
+
function exceptionDiagnostics(error, project) {
|
|
65
98
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const details = project ? project(error) : {
|
|
69
|
-
type: typeof name === "string" ? name : "Error",
|
|
70
|
-
message: typeof error === "string" ? error : typeof message === "string" ? message : "Execution reported a fault."
|
|
71
|
-
};
|
|
72
|
-
if (!details) return {};
|
|
73
|
-
const projectedMessage = details.message;
|
|
74
|
-
const projectedType = details.type;
|
|
75
|
-
if (typeof projectedMessage !== "string") return {};
|
|
76
|
-
return {
|
|
77
|
-
errorMessage: projectedMessage.slice(0, 8192),
|
|
78
|
-
...typeof projectedType === "string" ? { errorType: projectedType.slice(0, 256) } : {}
|
|
79
|
-
};
|
|
99
|
+
const details = project ? project(error) : projectDetails(error, 0, /* @__PURE__ */ new WeakSet());
|
|
100
|
+
return details ? sanitizeProjectedDetails(details) : null;
|
|
80
101
|
} catch {
|
|
81
|
-
return
|
|
102
|
+
return null;
|
|
82
103
|
}
|
|
83
104
|
}
|
|
105
|
+
function errorDiagnostics(error, project) {
|
|
106
|
+
const details = exceptionDiagnostics(error, project);
|
|
107
|
+
return details ? {
|
|
108
|
+
errorMessage: details.message,
|
|
109
|
+
...details.type ? { errorType: details.type } : {}
|
|
110
|
+
} : {};
|
|
111
|
+
}
|
|
84
112
|
function isModelTraceSpan(name) {
|
|
85
113
|
return name === "runReasonEngine" || name === "useReason";
|
|
86
114
|
}
|
|
@@ -497,8 +525,10 @@ var require_dist = __commonJS({
|
|
|
497
525
|
WorkflowCallError: () => WorkflowCallError,
|
|
498
526
|
completeResponse: () => completeResponse,
|
|
499
527
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
528
|
+
defineInterruptContract: () => defineInterruptContract,
|
|
500
529
|
emitWorkflowCall: () => emitWorkflowCall2,
|
|
501
530
|
parallel: () => parallel,
|
|
531
|
+
reportError: () => reportError,
|
|
502
532
|
runWithHookContext: () => runWithHookContext,
|
|
503
533
|
useAbortSignal: () => useAbortSignal,
|
|
504
534
|
useInterrupt: () => useInterrupt,
|
|
@@ -807,6 +837,95 @@ var require_dist = __commonJS({
|
|
|
807
837
|
);
|
|
808
838
|
return Promise.resolve(response);
|
|
809
839
|
};
|
|
840
|
+
var CONTROL_TOOL_PREFIX = "kortyx_request_input__";
|
|
841
|
+
var LEGACY_CONTRACT_NAME = "default";
|
|
842
|
+
var legacyWarningEmitted = false;
|
|
843
|
+
var legacyConfigs = /* @__PURE__ */ new WeakSet();
|
|
844
|
+
var defineInterruptContract = (contract) => {
|
|
845
|
+
if (!contract || typeof contract !== "object")
|
|
846
|
+
throw new Error("defineInterruptContract requires a contract object.");
|
|
847
|
+
for (const [field, value] of [
|
|
848
|
+
["description", contract.description],
|
|
849
|
+
["schemaId", contract.schemaId],
|
|
850
|
+
["schemaVersion", contract.schemaVersion]
|
|
851
|
+
])
|
|
852
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
853
|
+
throw new Error(
|
|
854
|
+
`Interrupt contract ${field} must be a non-empty string.`
|
|
855
|
+
);
|
|
856
|
+
if (typeof contract.requestSchema?.safeParse !== "function")
|
|
857
|
+
throw new Error(
|
|
858
|
+
"Interrupt contract requestSchema must provide safeParse(value)."
|
|
859
|
+
);
|
|
860
|
+
if (typeof contract.responseSchema?.safeParse !== "function")
|
|
861
|
+
throw new Error(
|
|
862
|
+
"Interrupt contract responseSchema must provide safeParse(value)."
|
|
863
|
+
);
|
|
864
|
+
return contract;
|
|
865
|
+
};
|
|
866
|
+
var interruptControlToolName = (name) => `${CONTROL_TOOL_PREFIX}${name}`;
|
|
867
|
+
var isInterruptControlToolName = (name) => name.startsWith(CONTROL_TOOL_PREFIX);
|
|
868
|
+
var assertInterruptContractName = (name) => {
|
|
869
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(name))
|
|
870
|
+
throw new Error(
|
|
871
|
+
`Invalid useReason interrupt contract name "${name}". Use letters, numbers, underscores, or hyphens, starting with a letter.`
|
|
872
|
+
);
|
|
873
|
+
if (interruptControlToolName(name).length > 64)
|
|
874
|
+
throw new Error(
|
|
875
|
+
`useReason interrupt contract name "${name}" is too long for a provider tool name.`
|
|
876
|
+
);
|
|
877
|
+
};
|
|
878
|
+
var warnLegacyUseReasonInterrupt = () => {
|
|
879
|
+
if (legacyWarningEmitted) return;
|
|
880
|
+
legacyWarningEmitted = true;
|
|
881
|
+
process.emitWarning(
|
|
882
|
+
"useReason({ interrupt }) is deprecated and will be removed in the next major release. Define an interrupt contract with defineInterruptContract() and pass it through useReason({ interrupts: { contracts } }). See docs/internal/next-major-removals.md.",
|
|
883
|
+
{
|
|
884
|
+
code: "KORTYX_USE_REASON_INTERRUPT_DEPRECATED",
|
|
885
|
+
type: "DeprecationWarning"
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
};
|
|
889
|
+
var normalizeReasonInterrupts = (args) => {
|
|
890
|
+
if (args.interrupt && args.interrupts)
|
|
891
|
+
throw new Error(
|
|
892
|
+
"useReason cannot receive both deprecated `interrupt` and `interrupts`. Migrate the legacy configuration to an interrupt contract."
|
|
893
|
+
);
|
|
894
|
+
if (args.interrupts) {
|
|
895
|
+
for (const [name, contract] of Object.entries(args.interrupts.contracts)) {
|
|
896
|
+
assertInterruptContractName(name);
|
|
897
|
+
defineInterruptContract(contract);
|
|
898
|
+
}
|
|
899
|
+
if (Object.keys(args.interrupts.contracts).length === 0)
|
|
900
|
+
throw new Error("useReason interrupts.contracts cannot be empty.");
|
|
901
|
+
if (args.interrupts.maxRequests !== void 0 && (!Number.isInteger(args.interrupts.maxRequests) || args.interrupts.maxRequests < 1))
|
|
902
|
+
throw new Error("useReason interrupts.maxRequests must be at least 1.");
|
|
903
|
+
return args.interrupts;
|
|
904
|
+
}
|
|
905
|
+
if (!args.interrupt) return void 0;
|
|
906
|
+
warnLegacyUseReasonInterrupt();
|
|
907
|
+
const normalized = {
|
|
908
|
+
mode: args.interrupt.mode,
|
|
909
|
+
maxRequests: 1,
|
|
910
|
+
contracts: {
|
|
911
|
+
[LEGACY_CONTRACT_NAME]: {
|
|
912
|
+
description: "Ask the user for the additional information required to complete this request.",
|
|
913
|
+
requestSchema: args.interrupt.requestSchema,
|
|
914
|
+
responseSchema: args.interrupt.responseSchema ?? {
|
|
915
|
+
safeParse: (value) => ({
|
|
916
|
+
success: true,
|
|
917
|
+
data: value
|
|
918
|
+
})
|
|
919
|
+
},
|
|
920
|
+
schemaId: args.interrupt.schemaId ?? "kortyx.legacy-interrupt",
|
|
921
|
+
schemaVersion: args.interrupt.schemaVersion ?? "1"
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
legacyConfigs.add(normalized);
|
|
926
|
+
return normalized;
|
|
927
|
+
};
|
|
928
|
+
var isLegacyInterruptContracts = (config) => Boolean(config && legacyConfigs.has(config));
|
|
810
929
|
var import_core62 = require("@kortyx/core");
|
|
811
930
|
function safeSpan(span) {
|
|
812
931
|
const guard = (fn) => (...args) => {
|
|
@@ -977,26 +1096,53 @@ var require_dist = __commonJS({
|
|
|
977
1096
|
var import_core22 = require("@kortyx/core");
|
|
978
1097
|
var import_errors32 = require("@kortyx/core/errors");
|
|
979
1098
|
var import_errors22 = require("@kortyx/core/errors");
|
|
980
|
-
|
|
1099
|
+
var MAX_STACK_LENGTH = 32768;
|
|
1100
|
+
var MAX_CAUSE_DEPTH = 4;
|
|
1101
|
+
var projectDetails = (value, depth, seen) => {
|
|
1102
|
+
const objectValue = value && typeof value === "object" ? value : void 0;
|
|
1103
|
+
if (objectValue) {
|
|
1104
|
+
if (seen.has(objectValue))
|
|
1105
|
+
return { type: "Error", message: "Circular error cause." };
|
|
1106
|
+
seen.add(objectValue);
|
|
1107
|
+
}
|
|
1108
|
+
const name = (0, import_errors22.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
|
|
1109
|
+
const message = (0, import_errors22.errorProperty)(value, "message");
|
|
1110
|
+
const stack = (0, import_errors22.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
|
|
1111
|
+
try {
|
|
1112
|
+
return value.stack;
|
|
1113
|
+
} catch {
|
|
1114
|
+
return void 0;
|
|
1115
|
+
}
|
|
1116
|
+
})() : void 0);
|
|
1117
|
+
const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors22.errorProperty)(value, "cause") : null;
|
|
1118
|
+
return {
|
|
1119
|
+
type: typeof name === "string" ? name.slice(0, 256) : "Error",
|
|
1120
|
+
message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
|
|
1121
|
+
...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
1122
|
+
...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
|
|
1123
|
+
};
|
|
1124
|
+
};
|
|
1125
|
+
var sanitizeProjectedDetails = (details, depth = 0) => ({
|
|
1126
|
+
...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
|
|
1127
|
+
message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
|
|
1128
|
+
...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
1129
|
+
...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
|
|
1130
|
+
});
|
|
1131
|
+
function exceptionDiagnostics(error, project) {
|
|
981
1132
|
try {
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
const details = project ? project(error) : {
|
|
985
|
-
type: typeof name === "string" ? name : "Error",
|
|
986
|
-
message: typeof error === "string" ? error : typeof message === "string" ? message : "Execution reported a fault."
|
|
987
|
-
};
|
|
988
|
-
if (!details) return {};
|
|
989
|
-
const projectedMessage = details.message;
|
|
990
|
-
const projectedType = details.type;
|
|
991
|
-
if (typeof projectedMessage !== "string") return {};
|
|
992
|
-
return {
|
|
993
|
-
errorMessage: projectedMessage.slice(0, 8192),
|
|
994
|
-
...typeof projectedType === "string" ? { errorType: projectedType.slice(0, 256) } : {}
|
|
995
|
-
};
|
|
1133
|
+
const details = project ? project(error) : projectDetails(error, 0, /* @__PURE__ */ new WeakSet());
|
|
1134
|
+
return details ? sanitizeProjectedDetails(details) : null;
|
|
996
1135
|
} catch {
|
|
997
|
-
return
|
|
1136
|
+
return null;
|
|
998
1137
|
}
|
|
999
1138
|
}
|
|
1139
|
+
function errorDiagnostics(error, project) {
|
|
1140
|
+
const details = exceptionDiagnostics(error, project);
|
|
1141
|
+
return details ? {
|
|
1142
|
+
errorMessage: details.message,
|
|
1143
|
+
...details.type ? { errorType: details.type } : {}
|
|
1144
|
+
} : {};
|
|
1145
|
+
}
|
|
1000
1146
|
var import_node_crypto22 = require("crypto");
|
|
1001
1147
|
function emitWorkflowCall2(telemetry, type, payload) {
|
|
1002
1148
|
const base = telemetry?.correlation;
|
|
@@ -2756,6 +2902,7 @@ ${tryStringify2(args.response)}`
|
|
|
2756
2902
|
};
|
|
2757
2903
|
var import_core52 = require("@kortyx/core");
|
|
2758
2904
|
var import_errors62 = require("@kortyx/core/errors");
|
|
2905
|
+
var import_zod42 = require("zod");
|
|
2759
2906
|
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
2760
2907
|
name: tool.name,
|
|
2761
2908
|
...tool.title ? { title: tool.title } : {},
|
|
@@ -2791,7 +2938,12 @@ ${tryStringify2(args.response)}`
|
|
|
2791
2938
|
};
|
|
2792
2939
|
};
|
|
2793
2940
|
var createInitialMessages = (args) => [
|
|
2794
|
-
...typeof args.system === "string"
|
|
2941
|
+
...typeof args.system === "string" || args.interruptInstructions ? [
|
|
2942
|
+
{
|
|
2943
|
+
role: "system",
|
|
2944
|
+
content: [args.system, args.interruptInstructions].filter((value) => Boolean(value)).join("\n\n")
|
|
2945
|
+
}
|
|
2946
|
+
] : [],
|
|
2795
2947
|
{ role: "user", content: String(args.input ?? "") }
|
|
2796
2948
|
];
|
|
2797
2949
|
var runReasonToolLoop = async (args) => {
|
|
@@ -2806,19 +2958,23 @@ ${tryStringify2(args.response)}`
|
|
|
2806
2958
|
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
2807
2959
|
const opId = checkpoint?.opId ?? args.opId;
|
|
2808
2960
|
const tools2 = useReasonArgs.tools ?? [];
|
|
2961
|
+
const interruptConfig = normalizeReasonInterrupts({
|
|
2962
|
+
interrupt: useReasonArgs.interrupt,
|
|
2963
|
+
interrupts: useReasonArgs.interrupts
|
|
2964
|
+
});
|
|
2965
|
+
const interruptByToolName = /* @__PURE__ */ new Map();
|
|
2809
2966
|
const toolByName = /* @__PURE__ */ new Map();
|
|
2810
2967
|
let separateOutput = false;
|
|
2811
2968
|
for (const tool of tools2) {
|
|
2969
|
+
if (isInterruptControlToolName(tool.name))
|
|
2970
|
+
throw new Error(
|
|
2971
|
+
`useReason tool name "${tool.name}" uses the reserved interrupt control-tool namespace.`
|
|
2972
|
+
);
|
|
2812
2973
|
if (toolByName.has(tool.name)) {
|
|
2813
2974
|
throw new Error(`useReason received duplicate tool name "${tool.name}".`);
|
|
2814
2975
|
}
|
|
2815
2976
|
toolByName.set(tool.name, tool);
|
|
2816
2977
|
}
|
|
2817
|
-
if (useReasonArgs.interrupt) {
|
|
2818
|
-
throw new Error(
|
|
2819
|
-
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
2820
|
-
);
|
|
2821
|
-
}
|
|
2822
2978
|
separateOutput = Boolean(
|
|
2823
2979
|
useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
|
|
2824
2980
|
...useReasonArgs.model.options,
|
|
@@ -2830,21 +2986,63 @@ ${tryStringify2(args.response)}`
|
|
|
2830
2986
|
) ?? {}
|
|
2831
2987
|
}).requiresSeparateStructuredOutput
|
|
2832
2988
|
);
|
|
2833
|
-
const
|
|
2989
|
+
const interruptDefinitions = [];
|
|
2990
|
+
for (const [name, contract] of Object.entries(
|
|
2991
|
+
interruptConfig?.contracts ?? {}
|
|
2992
|
+
)) {
|
|
2993
|
+
const toolName = interruptControlToolName(name);
|
|
2994
|
+
if (toolByName.has(toolName))
|
|
2995
|
+
throw new Error(
|
|
2996
|
+
`useReason tool name "${toolName}" is reserved for interrupt contracts.`
|
|
2997
|
+
);
|
|
2998
|
+
let inputSchema;
|
|
2999
|
+
try {
|
|
3000
|
+
inputSchema = (0, import_zod42.toJSONSchema)(contract.requestSchema);
|
|
3001
|
+
} catch (error) {
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
`useReason interrupt contract "${name}" requestSchema must support JSON Schema conversion.`,
|
|
3004
|
+
{ cause: error }
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
interruptByToolName.set(toolName, { name, contract });
|
|
3008
|
+
interruptDefinitions.push({
|
|
3009
|
+
name: toolName,
|
|
3010
|
+
title: `Request human input: ${name}`,
|
|
3011
|
+
description: [
|
|
3012
|
+
contract.description,
|
|
3013
|
+
"Call this only when human input is needed. Execution pauses until the validated response is returned as this tool's result."
|
|
3014
|
+
].join(" "),
|
|
3015
|
+
inputSchema,
|
|
3016
|
+
metadata: {
|
|
3017
|
+
kortyxControl: "interrupt",
|
|
3018
|
+
contract: name,
|
|
3019
|
+
schemaId: contract.schemaId,
|
|
3020
|
+
schemaVersion: contract.schemaVersion
|
|
3021
|
+
}
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
const toolDefinitions = [
|
|
3025
|
+
...toToolDefinitions(tools2),
|
|
3026
|
+
...interruptDefinitions
|
|
3027
|
+
];
|
|
2834
3028
|
let finalizing = checkpoint?.finalizing ?? false;
|
|
2835
3029
|
let completed = false;
|
|
2836
3030
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
2837
3031
|
const messages = checkpoint?.messages ?? createInitialMessages({
|
|
2838
3032
|
system: useReasonArgs.system,
|
|
2839
|
-
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
|
|
3033
|
+
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input,
|
|
3034
|
+
...interruptConfig ? {
|
|
3035
|
+
interruptInstructions: [
|
|
3036
|
+
"Human-input rules:",
|
|
3037
|
+
"- The kortyx_request_input__* tools pause this reasoning operation and return the validated human response as a normal tool result.",
|
|
3038
|
+
"- Call at most one human-input tool in a model turn, and do not combine it with other tool calls in that turn.",
|
|
3039
|
+
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
|
+
].join("\n")
|
|
3041
|
+
} : {}
|
|
2840
3042
|
});
|
|
2841
3043
|
const steps = checkpoint?.steps ?? [];
|
|
2842
|
-
const allToolCalls = steps.flatMap(
|
|
2843
|
-
|
|
2844
|
-
);
|
|
2845
|
-
const allToolResults = steps.flatMap(
|
|
2846
|
-
(step) => step.toolResults
|
|
2847
|
-
);
|
|
3044
|
+
const allToolCalls = steps.flatMap((step) => step.toolCalls).filter((call) => !interruptByToolName.has(call.name));
|
|
3045
|
+
const allToolResults = steps.flatMap((step) => step.toolResults).filter((result) => !interruptByToolName.has(result.name));
|
|
2848
3046
|
let finalText = "";
|
|
2849
3047
|
let finalRaw;
|
|
2850
3048
|
let finalOutput;
|
|
@@ -2860,6 +3058,7 @@ ${tryStringify2(args.response)}`
|
|
|
2860
3058
|
);
|
|
2861
3059
|
let pending = checkpoint?.pending;
|
|
2862
3060
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
3061
|
+
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
2863
3062
|
const save = () => {
|
|
2864
3063
|
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2865
3064
|
status: "tool_loop",
|
|
@@ -2867,6 +3066,7 @@ ${tryStringify2(args.response)}`
|
|
|
2867
3066
|
messages,
|
|
2868
3067
|
steps,
|
|
2869
3068
|
approvedCalls,
|
|
3069
|
+
interruptHistory,
|
|
2870
3070
|
finalizing,
|
|
2871
3071
|
...pending ? { pending } : {}
|
|
2872
3072
|
};
|
|
@@ -2945,6 +3145,10 @@ ${tryStringify2(args.response)}`
|
|
|
2945
3145
|
...step.warnings ? { warnings: step.warnings } : {}
|
|
2946
3146
|
});
|
|
2947
3147
|
if (toolCalls.length === 0) {
|
|
3148
|
+
if (interruptConfig?.mode === "required" && interruptHistory.length === 0)
|
|
3149
|
+
throw new Error(
|
|
3150
|
+
"useReason interrupts.mode is required, but the model returned a final answer without requesting human input."
|
|
3151
|
+
);
|
|
2948
3152
|
if (separateOutput && !finalizing) {
|
|
2949
3153
|
if (args.allowValidatedToolOutput && useReasonArgs.outputSchema) {
|
|
2950
3154
|
try {
|
|
@@ -2989,7 +3193,9 @@ ${tryStringify2(args.response)}`
|
|
|
2989
3193
|
"Provider requested a tool during schema-only finalization."
|
|
2990
3194
|
);
|
|
2991
3195
|
if (!reused) {
|
|
2992
|
-
allToolCalls.push(
|
|
3196
|
+
allToolCalls.push(
|
|
3197
|
+
...toolCalls.filter((call) => !interruptByToolName.has(call.name))
|
|
3198
|
+
);
|
|
2993
3199
|
messages.push({
|
|
2994
3200
|
role: "assistant",
|
|
2995
3201
|
content: step.text,
|
|
@@ -2999,6 +3205,84 @@ ${tryStringify2(args.response)}`
|
|
|
2999
3205
|
}
|
|
3000
3206
|
pending = step;
|
|
3001
3207
|
save();
|
|
3208
|
+
const interruptCalls = toolCalls.filter(
|
|
3209
|
+
(call) => interruptByToolName.has(call.name)
|
|
3210
|
+
);
|
|
3211
|
+
if (interruptCalls.length > 0) {
|
|
3212
|
+
if (toolCalls.length !== 1)
|
|
3213
|
+
throw new Error(
|
|
3214
|
+
"A useReason interrupt contract call must be the only tool call in its model turn."
|
|
3215
|
+
);
|
|
3216
|
+
const interruptCall = interruptCalls[0];
|
|
3217
|
+
if (!interruptCall)
|
|
3218
|
+
throw new Error("Missing useReason interrupt control call.");
|
|
3219
|
+
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
3220
|
+
throw new Error(
|
|
3221
|
+
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3222
|
+
);
|
|
3223
|
+
const resolved = interruptByToolName.get(interruptCall.name);
|
|
3224
|
+
if (!resolved)
|
|
3225
|
+
throw new Error(
|
|
3226
|
+
`Unknown useReason interrupt contract tool "${interruptCall.name}".`
|
|
3227
|
+
);
|
|
3228
|
+
if (toolResults.some((result3) => result3.toolCallId === interruptCall.id)) {
|
|
3229
|
+
pending = void 0;
|
|
3230
|
+
save();
|
|
3231
|
+
continue;
|
|
3232
|
+
}
|
|
3233
|
+
const request = parseWithSchema(
|
|
3234
|
+
resolved.contract.requestSchema,
|
|
3235
|
+
interruptCall.input,
|
|
3236
|
+
`useReason interrupt contract "${resolved.name}" request`
|
|
3237
|
+
);
|
|
3238
|
+
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3239
|
+
checkpointKey,
|
|
3240
|
+
contract: resolved.name,
|
|
3241
|
+
interruptIndex: interruptHistory.length
|
|
3242
|
+
});
|
|
3243
|
+
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
3244
|
+
const response = await awaitInterruptInternal({
|
|
3245
|
+
request: {
|
|
3246
|
+
kind: "custom",
|
|
3247
|
+
request,
|
|
3248
|
+
contract: resolved.name,
|
|
3249
|
+
...question ? { question } : {},
|
|
3250
|
+
schemaId: resolved.contract.schemaId,
|
|
3251
|
+
schemaVersion: resolved.contract.schemaVersion
|
|
3252
|
+
},
|
|
3253
|
+
responseSchema: resolved.contract.responseSchema,
|
|
3254
|
+
id: `${id ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
3255
|
+
meta: {
|
|
3256
|
+
__kortyxReason: {
|
|
3257
|
+
opId,
|
|
3258
|
+
stepIndex,
|
|
3259
|
+
interruptIndex: interruptHistory.length,
|
|
3260
|
+
contract: resolved.name
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
});
|
|
3264
|
+
const result2 = normalizeToolResult(interruptCall, response);
|
|
3265
|
+
toolResults.push(result2);
|
|
3266
|
+
messages.push({
|
|
3267
|
+
role: "tool",
|
|
3268
|
+
content: result2.content,
|
|
3269
|
+
toolCallId: result2.toolCallId,
|
|
3270
|
+
name: result2.name
|
|
3271
|
+
});
|
|
3272
|
+
interruptHistory.push({
|
|
3273
|
+
contract: resolved.name,
|
|
3274
|
+
request,
|
|
3275
|
+
response
|
|
3276
|
+
});
|
|
3277
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3278
|
+
checkpointKey,
|
|
3279
|
+
contract: resolved.name,
|
|
3280
|
+
interruptIndex: interruptHistory.length - 1
|
|
3281
|
+
});
|
|
3282
|
+
pending = void 0;
|
|
3283
|
+
save();
|
|
3284
|
+
continue;
|
|
3285
|
+
}
|
|
3002
3286
|
for (const toolCall of toolCalls) {
|
|
3003
3287
|
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
3004
3288
|
await replayToolObservations(
|
|
@@ -3208,7 +3492,11 @@ ${tryStringify2(args.response)}`
|
|
|
3208
3492
|
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
3209
3493
|
toolCalls: allToolCalls,
|
|
3210
3494
|
toolResults: allToolResults,
|
|
3211
|
-
steps
|
|
3495
|
+
steps,
|
|
3496
|
+
...interruptHistory.length > 0 ? { interruptHistory } : {},
|
|
3497
|
+
...isLegacyInterruptContracts(interruptConfig) && interruptHistory.length > 0 ? {
|
|
3498
|
+
interruptResponse: interruptHistory.at(-1)?.response
|
|
3499
|
+
} : {}
|
|
3212
3500
|
};
|
|
3213
3501
|
traceSpan?.end?.({
|
|
3214
3502
|
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
@@ -3219,7 +3507,8 @@ ${tryStringify2(args.response)}`
|
|
|
3219
3507
|
textLength: finalText.length,
|
|
3220
3508
|
toolCallCount: allToolCalls.length,
|
|
3221
3509
|
toolResultCount: allToolResults.length,
|
|
3222
|
-
toolStepCount: steps.length
|
|
3510
|
+
toolStepCount: steps.length,
|
|
3511
|
+
interruptCount: interruptHistory.length
|
|
3223
3512
|
},
|
|
3224
3513
|
telemetry: {
|
|
3225
3514
|
...useReasonArgs.telemetry ?? {},
|
|
@@ -3247,7 +3536,7 @@ ${tryStringify2(args.response)}`
|
|
|
3247
3536
|
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
3248
3537
|
var assertReasoningThoughtsCompatibility = (args) => {
|
|
3249
3538
|
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
3250
|
-
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3539
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.interrupts) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3251
3540
|
if (!usesStructuredOutput) return;
|
|
3252
3541
|
throw new Error(
|
|
3253
3542
|
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
@@ -3255,6 +3544,10 @@ ${tryStringify2(args.response)}`
|
|
|
3255
3544
|
};
|
|
3256
3545
|
async function runReason(args) {
|
|
3257
3546
|
assertReasoningThoughtsCompatibility(args);
|
|
3547
|
+
const interruptConfig = normalizeReasonInterrupts({
|
|
3548
|
+
interrupt: args.interrupt,
|
|
3549
|
+
interrupts: args.interrupts
|
|
3550
|
+
});
|
|
3258
3551
|
const ctx = getHookContext();
|
|
3259
3552
|
const abortSignal = (0, import_core62.combineAbortSignals)(
|
|
3260
3553
|
ctx.node.abortSignal,
|
|
@@ -3269,20 +3562,23 @@ ${tryStringify2(args.response)}`
|
|
|
3269
3562
|
args.outputSchema,
|
|
3270
3563
|
args.responseFormat ?? args.model.options?.responseFormat
|
|
3271
3564
|
);
|
|
3565
|
+
const usesLegacyNoToolInterrupt = Boolean(
|
|
3566
|
+
args.interrupt && !args.interrupts && !args.tools?.length
|
|
3567
|
+
);
|
|
3272
3568
|
args = {
|
|
3273
3569
|
...args,
|
|
3274
3570
|
abortSignal,
|
|
3275
|
-
...!
|
|
3571
|
+
...!usesLegacyNoToolInterrupt && inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {}
|
|
3276
3572
|
};
|
|
3277
3573
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
3278
|
-
const
|
|
3574
|
+
const provisionalOpId = createRuntimeId2();
|
|
3279
3575
|
const checkpointKey = resolveReasonCheckpointKey({
|
|
3280
3576
|
...id ? { id } : {},
|
|
3281
3577
|
autoIndex: ctx.reasonCallIndex++
|
|
3282
3578
|
});
|
|
3283
|
-
const
|
|
3284
|
-
|
|
3285
|
-
);
|
|
3579
|
+
const checkpointValue = ctx.currentNodeState.byKey[checkpointKey];
|
|
3580
|
+
const opId = checkpointValue && typeof checkpointValue === "object" && "status" in checkpointValue && checkpointValue.status === "tool_loop" && "opId" in checkpointValue && typeof checkpointValue.opId === "string" ? checkpointValue.opId : provisionalOpId;
|
|
3581
|
+
const existingCompleted = readReasonCompletedCheckpoint(checkpointValue);
|
|
3286
3582
|
if (existingCompleted) {
|
|
3287
3583
|
await replayToolObservations(
|
|
3288
3584
|
existingCompleted.result.steps?.flatMap(
|
|
@@ -3291,9 +3587,7 @@ ${tryStringify2(args.response)}`
|
|
|
3291
3587
|
);
|
|
3292
3588
|
return existingCompleted.result;
|
|
3293
3589
|
}
|
|
3294
|
-
const existingCheckpoint = readReasonCheckpoint(
|
|
3295
|
-
ctx.currentNodeState.byKey[checkpointKey]
|
|
3296
|
-
);
|
|
3590
|
+
const existingCheckpoint = readReasonCheckpoint(checkpointValue);
|
|
3297
3591
|
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
3298
3592
|
return withSafeTraceSpan2(
|
|
3299
3593
|
getReasonTraceAdapter(),
|
|
@@ -3312,7 +3606,8 @@ ${tryStringify2(args.response)}`
|
|
|
3312
3606
|
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
3313
3607
|
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
3314
3608
|
hasOutputSchema: Boolean(args.outputSchema),
|
|
3315
|
-
hasInterrupt: Boolean(
|
|
3609
|
+
hasInterrupt: Boolean(interruptConfig),
|
|
3610
|
+
interruptContractCount: Object.keys(interruptConfig?.contracts ?? {}).length,
|
|
3316
3611
|
hasStructured: Boolean(args.structured),
|
|
3317
3612
|
hasTools: Boolean(args.tools?.length)
|
|
3318
3613
|
},
|
|
@@ -3322,7 +3617,7 @@ ${tryStringify2(args.response)}`
|
|
|
3322
3617
|
}
|
|
3323
3618
|
},
|
|
3324
3619
|
async (traceSpan) => {
|
|
3325
|
-
if (args.tools?.length) {
|
|
3620
|
+
if ((args.tools?.length || args.interrupts) && !existingCheckpoint) {
|
|
3326
3621
|
return runReasonToolLoop({
|
|
3327
3622
|
useReasonArgs: args,
|
|
3328
3623
|
allowValidatedToolOutput,
|
|
@@ -3589,6 +3884,24 @@ ${tryStringify2(args.response)}`
|
|
|
3589
3884
|
return useReason(args);
|
|
3590
3885
|
}
|
|
3591
3886
|
function useInterrupt(args) {
|
|
3887
|
+
if ("contract" in args) {
|
|
3888
|
+
const contract = defineInterruptContract(args.contract);
|
|
3889
|
+
return awaitInterruptInternal({
|
|
3890
|
+
request: {
|
|
3891
|
+
kind: "custom",
|
|
3892
|
+
request: parseWithSchema(
|
|
3893
|
+
contract.requestSchema,
|
|
3894
|
+
args.request,
|
|
3895
|
+
"useInterrupt contract request"
|
|
3896
|
+
),
|
|
3897
|
+
schemaId: contract.schemaId,
|
|
3898
|
+
schemaVersion: contract.schemaVersion,
|
|
3899
|
+
...args.id ? { id: args.id } : {},
|
|
3900
|
+
...args.meta ? { meta: args.meta } : {}
|
|
3901
|
+
},
|
|
3902
|
+
responseSchema: contract.responseSchema
|
|
3903
|
+
});
|
|
3904
|
+
}
|
|
3592
3905
|
return awaitInterruptInternal(args);
|
|
3593
3906
|
}
|
|
3594
3907
|
function useAbortSignal() {
|
|
@@ -3802,6 +4115,12 @@ ${tryStringify2(args.response)}`
|
|
|
3802
4115
|
});
|
|
3803
4116
|
return promise;
|
|
3804
4117
|
}
|
|
4118
|
+
function reportError(error, options = {}) {
|
|
4119
|
+
try {
|
|
4120
|
+
getReasonTraceAdapter()?.reportError?.(error, options);
|
|
4121
|
+
} catch {
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
3805
4124
|
var import_node_crypto6 = require("crypto");
|
|
3806
4125
|
var import_core8 = require("@kortyx/core");
|
|
3807
4126
|
var import_errors8 = require("@kortyx/core/errors");
|
|
@@ -4888,7 +5207,10 @@ async function orchestrateGraphStream({
|
|
|
4888
5207
|
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4889
5208
|
resumeOutcome: "failed",
|
|
4890
5209
|
resumeError: (0, import_errors4.serializeFailure)(resumeError).message,
|
|
4891
|
-
failure: (0, import_errors4.serializeFailure)(resumeError)
|
|
5210
|
+
failure: (0, import_errors4.serializeFailure)(resumeError),
|
|
5211
|
+
...typeof config.telemetryInterruptContract === "string" ? { contract: config.telemetryInterruptContract } : {},
|
|
5212
|
+
...typeof config.telemetryInterruptSchemaId === "string" ? { schemaId: config.telemetryInterruptSchemaId } : {},
|
|
5213
|
+
...typeof config.telemetryInterruptSchemaVersion === "string" ? { schemaVersion: config.telemetryInterruptSchemaVersion } : {}
|
|
4892
5214
|
},
|
|
4893
5215
|
flush: true
|
|
4894
5216
|
});
|
|
@@ -4994,6 +5316,7 @@ async function orchestrateGraphStream({
|
|
|
4994
5316
|
const optionsList = Array.isArray(input.options) ? input.options : [];
|
|
4995
5317
|
const kind = input.kind || (input.multiple ? "multi-choice" : "choice");
|
|
4996
5318
|
const isText = kind === "text";
|
|
5319
|
+
const isCustom = kind === "custom";
|
|
4997
5320
|
const record = {
|
|
4998
5321
|
token,
|
|
4999
5322
|
ready: false,
|
|
@@ -5004,7 +5327,17 @@ async function orchestrateGraphStream({
|
|
|
5004
5327
|
workflow: payload.workflow || currentState.currentWorkflow,
|
|
5005
5328
|
node: payload.node || "",
|
|
5006
5329
|
state: { ...currentState, awaitingHumanInput: true },
|
|
5007
|
-
schema:
|
|
5330
|
+
schema: isCustom ? {
|
|
5331
|
+
kind: "custom",
|
|
5332
|
+
multiple: false,
|
|
5333
|
+
...input.question ? { question: input.question } : {},
|
|
5334
|
+
...typeof input.id === "string" && input.id.length > 0 ? { id: input.id } : {},
|
|
5335
|
+
...typeof input.contract === "string" && input.contract.length > 0 ? { contract: input.contract } : {},
|
|
5336
|
+
...Object.hasOwn(input, "request") ? { request: input.request } : {},
|
|
5337
|
+
...typeof input.schemaId === "string" && input.schemaId.length > 0 ? { schemaId: input.schemaId } : {},
|
|
5338
|
+
...typeof input.schemaVersion === "string" && input.schemaVersion.length > 0 ? { schemaVersion: input.schemaVersion } : {},
|
|
5339
|
+
...input.meta && typeof input.meta === "object" ? { meta: input.meta } : {}
|
|
5340
|
+
} : isText ? {
|
|
5008
5341
|
kind,
|
|
5009
5342
|
multiple: Boolean(input.multiple),
|
|
5010
5343
|
...input.question ? { question: input.question } : {},
|
|
@@ -5059,6 +5392,8 @@ async function orchestrateGraphStream({
|
|
|
5059
5392
|
kind: record.schema.kind,
|
|
5060
5393
|
multiple: record.schema.multiple,
|
|
5061
5394
|
question: record.schema.question,
|
|
5395
|
+
...record.schema.contract ? { contract: record.schema.contract } : {},
|
|
5396
|
+
...Object.hasOwn(record.schema, "request") ? { request: record.schema.request } : {},
|
|
5062
5397
|
...typeof record.schema.id === "string" && record.schema.id.length > 0 ? { id: record.schema.id } : {},
|
|
5063
5398
|
...typeof record.schema.schemaId === "string" && record.schema.schemaId.length > 0 ? { schemaId: record.schema.schemaId } : {},
|
|
5064
5399
|
...typeof record.schema.schemaVersion === "string" && record.schema.schemaVersion.length > 0 ? { schemaVersion: record.schema.schemaVersion } : {},
|
|
@@ -5070,6 +5405,11 @@ async function orchestrateGraphStream({
|
|
|
5070
5405
|
}))
|
|
5071
5406
|
}
|
|
5072
5407
|
});
|
|
5408
|
+
const captureRequest = shouldCaptureTelemetryContent(
|
|
5409
|
+
telemetryConfig.captureContent,
|
|
5410
|
+
"output"
|
|
5411
|
+
);
|
|
5412
|
+
const structuredRequest = isRecord3(record.schema.request) ? record.schema.request : void 0;
|
|
5073
5413
|
emitTelemetryEvent({
|
|
5074
5414
|
config,
|
|
5075
5415
|
type: "interrupt.created",
|
|
@@ -5085,11 +5425,11 @@ async function orchestrateGraphStream({
|
|
|
5085
5425
|
interactionMode: record.schema.kind === "text" ? "freeform" : record.options.length > 0 ? "static-options" : record.schema.schemaId ? "dynamic-picker" : "unknown",
|
|
5086
5426
|
...record.schema.schemaId ? { schemaId: record.schema.schemaId } : {},
|
|
5087
5427
|
...record.schema.schemaVersion ? { schemaVersion: record.schema.schemaVersion } : {},
|
|
5088
|
-
...
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5428
|
+
...record.schema.contract ? { contract: record.schema.contract } : {},
|
|
5429
|
+
requestCaptured: Boolean(captureRequest && structuredRequest),
|
|
5430
|
+
...captureRequest && structuredRequest ? { request: structuredRequest } : {},
|
|
5431
|
+
...typeof record.schema.question === "string" && captureRequest ? { question: record.schema.question } : {},
|
|
5432
|
+
...captureRequest ? {
|
|
5093
5433
|
options: record.options.map((option) => ({
|
|
5094
5434
|
id: option.id,
|
|
5095
5435
|
label: option.label,
|
|
@@ -5898,6 +6238,7 @@ function parseResumeMeta(msg) {
|
|
|
5898
6238
|
const token = typeof raw.token === "string" ? raw.token : "";
|
|
5899
6239
|
const requestId = typeof raw.requestId === "string" ? raw.requestId : "";
|
|
5900
6240
|
const cancel = raw.cancel === true;
|
|
6241
|
+
const hasValue = Object.hasOwn(raw, "value");
|
|
5901
6242
|
let selected = [];
|
|
5902
6243
|
const rawSelected = raw.selected;
|
|
5903
6244
|
if (typeof rawSelected === "string") selected = [rawSelected];
|
|
@@ -5907,7 +6248,13 @@ function parseResumeMeta(msg) {
|
|
|
5907
6248
|
else if (Array.isArray(raw.choices))
|
|
5908
6249
|
selected = raw.choices.map((c) => isRecord4(c) ? c.id : void 0).filter((id) => typeof id === "string");
|
|
5909
6250
|
if (!token || !requestId) return null;
|
|
5910
|
-
return {
|
|
6251
|
+
return {
|
|
6252
|
+
token,
|
|
6253
|
+
requestId,
|
|
6254
|
+
selected,
|
|
6255
|
+
cancel,
|
|
6256
|
+
...hasValue ? { value: raw.value, hasValue: true } : {}
|
|
6257
|
+
};
|
|
5911
6258
|
}
|
|
5912
6259
|
async function tryPrepareResumeStream({
|
|
5913
6260
|
limits,
|
|
@@ -5994,7 +6341,8 @@ async function tryPrepareResumeStream({
|
|
|
5994
6341
|
},
|
|
5995
6342
|
payload: {
|
|
5996
6343
|
interruptId: pending.requestId,
|
|
5997
|
-
reason: "cancelled_by_client"
|
|
6344
|
+
reason: "cancelled_by_client",
|
|
6345
|
+
...pending.schema.contract ? { contract: pending.schema.contract } : {}
|
|
5998
6346
|
},
|
|
5999
6347
|
flush: true
|
|
6000
6348
|
});
|
|
@@ -6074,7 +6422,7 @@ async function tryPrepareResumeStream({
|
|
|
6074
6422
|
resumeUpdate.runtime = resumeStatePatch;
|
|
6075
6423
|
}
|
|
6076
6424
|
const hasResumeUpdate = !isLimitPause && Object.keys(resumeUpdate).length > 0;
|
|
6077
|
-
const resumeValue = isLimitPause ? void 0 : meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
|
|
6425
|
+
const resumeValue = isLimitPause ? void 0 : meta.hasValue && pending.schema.kind === "custom" ? meta.value : meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
|
|
6078
6426
|
const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
|
|
6079
6427
|
const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
|
|
6080
6428
|
...telemetryConfig,
|
|
@@ -6084,7 +6432,7 @@ async function tryPrepareResumeStream({
|
|
|
6084
6432
|
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
6085
6433
|
});
|
|
6086
6434
|
await store.delete(pending.token);
|
|
6087
|
-
const response = responseFromSelection(meta.selected);
|
|
6435
|
+
const response = meta.hasValue ? JSON.stringify(meta.value) : responseFromSelection(meta.selected);
|
|
6088
6436
|
const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
|
|
6089
6437
|
const responseCaptured = Boolean(
|
|
6090
6438
|
response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
|
|
@@ -6103,6 +6451,10 @@ async function tryPrepareResumeStream({
|
|
|
6103
6451
|
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6104
6452
|
resumeOutcome: "resumed",
|
|
6105
6453
|
responseCaptured,
|
|
6454
|
+
...pending.schema.contract ? { contract: pending.schema.contract } : {},
|
|
6455
|
+
...pending.schema.schemaId ? { schemaId: pending.schema.schemaId } : {},
|
|
6456
|
+
...pending.schema.schemaVersion ? { schemaVersion: pending.schema.schemaVersion } : {},
|
|
6457
|
+
...responseCaptured && meta.hasValue ? { responseValue: meta.value } : {},
|
|
6106
6458
|
...responseCaptured && response ? { response } : {}
|
|
6107
6459
|
},
|
|
6108
6460
|
flush: true
|
|
@@ -6124,6 +6476,11 @@ async function tryPrepareResumeStream({
|
|
|
6124
6476
|
resume: true,
|
|
6125
6477
|
telemetryInterruptId: pending.requestId,
|
|
6126
6478
|
telemetryInterruptNodeId: pending.node,
|
|
6479
|
+
...pending.schema.contract ? { telemetryInterruptContract: pending.schema.contract } : {},
|
|
6480
|
+
...pending.schema.schemaId ? { telemetryInterruptSchemaId: pending.schema.schemaId } : {},
|
|
6481
|
+
...pending.schema.schemaVersion ? {
|
|
6482
|
+
telemetryInterruptSchemaVersion: pending.schema.schemaVersion
|
|
6483
|
+
} : {},
|
|
6127
6484
|
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
6128
6485
|
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
6129
6486
|
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
@@ -6157,6 +6514,7 @@ var responseSchema = import_zod3.z.discriminatedUnion("type", [
|
|
|
6157
6514
|
type: import_zod3.z.literal("select"),
|
|
6158
6515
|
ids: import_zod3.z.array(import_zod3.z.string().min(1)).min(1)
|
|
6159
6516
|
}).strict(),
|
|
6517
|
+
import_zod3.z.object({ type: import_zod3.z.literal("value"), value: import_zod3.z.unknown() }).strict(),
|
|
6160
6518
|
import_zod3.z.object({ type: import_zod3.z.literal("cancel") }).strict()
|
|
6161
6519
|
]);
|
|
6162
6520
|
function resultFromOutcome(outcome, runId, sessionId) {
|
|
@@ -6297,7 +6655,13 @@ async function resumeWorkflow(services, args) {
|
|
|
6297
6655
|
"Resume handle does not match the root execution and workflow version."
|
|
6298
6656
|
);
|
|
6299
6657
|
if (response.type === "cancel") return;
|
|
6300
|
-
if (response.type === "
|
|
6658
|
+
if (response.type === "value") {
|
|
6659
|
+
if (pending.schema.kind !== "custom")
|
|
6660
|
+
throw new ExecutionRequestError(
|
|
6661
|
+
"INVALID_RESPONSE",
|
|
6662
|
+
"Structured values are only valid for custom interrupts."
|
|
6663
|
+
);
|
|
6664
|
+
} else if (response.type === "text") {
|
|
6301
6665
|
if (pending.schema.kind !== "text")
|
|
6302
6666
|
throw new ExecutionRequestError(
|
|
6303
6667
|
"INVALID_RESPONSE",
|
|
@@ -6327,6 +6691,7 @@ async function resumeWorkflow(services, args) {
|
|
|
6327
6691
|
token: handle.token,
|
|
6328
6692
|
requestId: handle.requestId,
|
|
6329
6693
|
selected: response.type === "text" ? [response.text] : response.type === "select" ? response.ids : [],
|
|
6694
|
+
...response.type === "value" ? { value: response.value, hasValue: true } : {},
|
|
6330
6695
|
cancel: response.type === "cancel"
|
|
6331
6696
|
},
|
|
6332
6697
|
sessionId: handle.sessionId,
|