@kortyx/hooks 0.30.2 → 0.32.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 +23 -0
- package/README.md +25 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +586 -290
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +9 -5
- package/dist/internal.js +243 -35
- package/dist/internal.js.map +1 -1
- package/dist/{tracing-BsxYlgEw.d.ts → tracing-mXE0Euvx.d.ts} +7 -2
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -286,7 +286,8 @@ var readReasonCompletedCheckpoint = (raw) => {
|
|
|
286
286
|
if (!value.result || typeof value.result !== "object") return void 0;
|
|
287
287
|
return {
|
|
288
288
|
status: "completed",
|
|
289
|
-
result: value.result
|
|
289
|
+
result: value.result,
|
|
290
|
+
...Array.isArray(value.resolvedInterrupts) ? { resolvedInterrupts: value.resolvedInterrupts } : {}
|
|
290
291
|
};
|
|
291
292
|
};
|
|
292
293
|
var resolveHookStatePatch = (args) => ({
|
|
@@ -348,6 +349,10 @@ var awaitInterruptInternal = (args) => {
|
|
|
348
349
|
raw,
|
|
349
350
|
"useInterrupt response"
|
|
350
351
|
);
|
|
352
|
+
if (args.onResolved) {
|
|
353
|
+
const { __kortyxResumeStatePatch: _patch, ...meta } = enrichedRequest.meta ?? {};
|
|
354
|
+
args.onResolved({ ...enrichedRequest, meta });
|
|
355
|
+
}
|
|
351
356
|
return Promise.resolve(response);
|
|
352
357
|
};
|
|
353
358
|
|
|
@@ -717,52 +722,257 @@ var import_core2 = require("@kortyx/core");
|
|
|
717
722
|
var import_errors3 = require("@kortyx/core/errors");
|
|
718
723
|
|
|
719
724
|
// src/error-diagnostics.ts
|
|
725
|
+
var import_telemetry_contracts2 = require("@kortyx/telemetry-contracts");
|
|
726
|
+
|
|
727
|
+
// src/diagnostic-capture.ts
|
|
720
728
|
var import_errors2 = require("@kortyx/core/errors");
|
|
721
|
-
var
|
|
722
|
-
var
|
|
723
|
-
var
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
+
var import_telemetry_contracts = require("@kortyx/telemetry-contracts");
|
|
730
|
+
var encoder = new TextEncoder();
|
|
731
|
+
var nativeStackGetter = Object.getOwnPropertyDescriptor(
|
|
732
|
+
new Error(),
|
|
733
|
+
"stack"
|
|
734
|
+
)?.get;
|
|
735
|
+
function nativeStack(object, getter) {
|
|
736
|
+
if (!getter || getter !== nativeStackGetter)
|
|
737
|
+
throw new Error("Custom stack accessor");
|
|
738
|
+
for (const key of ["name", "message"]) {
|
|
739
|
+
let cursor = object;
|
|
740
|
+
while (cursor) {
|
|
741
|
+
const descriptor = Object.getOwnPropertyDescriptor(cursor, key);
|
|
742
|
+
if (descriptor) {
|
|
743
|
+
if (!("value" in descriptor) || typeof descriptor.value !== "string")
|
|
744
|
+
throw new Error("Custom error formatting");
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
cursor = Object.getPrototypeOf(cursor);
|
|
748
|
+
}
|
|
729
749
|
}
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
750
|
+
const formatter = Object.getOwnPropertyDescriptor(Error, "prepareStackTrace");
|
|
751
|
+
if (formatter && (!("value" in formatter) || !formatter.writable))
|
|
752
|
+
throw new Error("Custom stack formatter");
|
|
753
|
+
Object.defineProperty(Error, "prepareStackTrace", {
|
|
754
|
+
...formatter ?? { configurable: true, writable: true },
|
|
755
|
+
value: void 0
|
|
756
|
+
});
|
|
757
|
+
try {
|
|
758
|
+
return getter.call(object);
|
|
759
|
+
} finally {
|
|
760
|
+
if (formatter) Object.defineProperty(Error, "prepareStackTrace", formatter);
|
|
761
|
+
else Reflect.deleteProperty(Error, "prepareStackTrace");
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
var pointer = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
765
|
+
function captureDiagnosticContent(error, project) {
|
|
766
|
+
const omissions = [];
|
|
767
|
+
const redactions = [];
|
|
768
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
769
|
+
let nodes = 0;
|
|
770
|
+
let remaining = import_telemetry_contracts.DIAGNOSTIC_LIMITS.bytes - 64 * 1024;
|
|
771
|
+
const omit = (path, reason, extra = {}) => {
|
|
772
|
+
if (omissions.length < 1e3)
|
|
773
|
+
omissions.push({ path: path.slice(0, 4096), reason, ...extra });
|
|
774
|
+
return { $omitted: reason };
|
|
775
|
+
};
|
|
776
|
+
const visit = (value, path, depth) => {
|
|
777
|
+
if (++nodes > import_telemetry_contracts.DIAGNOSTIC_LIMITS.nodes) return omit(path, "node_limit");
|
|
778
|
+
if (depth > import_telemetry_contracts.DIAGNOSTIC_LIMITS.depth) return omit(path, "depth_limit");
|
|
779
|
+
if (typeof value === "string") {
|
|
780
|
+
const clean = (0, import_telemetry_contracts.redactDiagnosticText)(value);
|
|
781
|
+
if (clean !== value && redactions.length < 1e3)
|
|
782
|
+
redactions.push({
|
|
783
|
+
path: path.slice(0, 4096),
|
|
784
|
+
reason: "credential_text"
|
|
785
|
+
});
|
|
786
|
+
const bytes = encoder.encode(JSON.stringify(clean)).length;
|
|
787
|
+
if (bytes > remaining)
|
|
788
|
+
return omit(path, "byte_limit", {
|
|
789
|
+
originalBytes: encoder.encode(value).length
|
|
790
|
+
});
|
|
791
|
+
remaining -= bytes;
|
|
792
|
+
return clean;
|
|
793
|
+
}
|
|
794
|
+
if (value === null || typeof value === "boolean") return value;
|
|
795
|
+
if (typeof value === "number")
|
|
796
|
+
return Number.isFinite(value) ? value : { $type: "number", value: String(value) };
|
|
797
|
+
if (typeof value === "bigint")
|
|
798
|
+
return { $type: "bigint", value: value.toString() };
|
|
799
|
+
if (typeof value === "undefined") return { $type: "undefined" };
|
|
800
|
+
if (typeof value === "function" || typeof value === "symbol")
|
|
801
|
+
return omit(path, `unsupported_${typeof value}`);
|
|
802
|
+
const object = value;
|
|
803
|
+
const reference = seen.get(object);
|
|
804
|
+
if (reference) return { $ref: reference };
|
|
805
|
+
seen.set(object, path);
|
|
806
|
+
remaining -= 32;
|
|
807
|
+
if (remaining < 0) return omit(path, "byte_limit");
|
|
733
808
|
try {
|
|
734
|
-
|
|
809
|
+
if (value instanceof Date)
|
|
810
|
+
return { $type: "Date", value: Date.prototype.toISOString.call(value) };
|
|
811
|
+
if (value instanceof Map || value instanceof Set) {
|
|
812
|
+
const isMap = value instanceof Map;
|
|
813
|
+
const iterator = isMap ? Map.prototype.entries.call(value) : Set.prototype.values.call(value);
|
|
814
|
+
const count = Object.getOwnPropertyDescriptor(
|
|
815
|
+
isMap ? Map.prototype : Set.prototype,
|
|
816
|
+
"size"
|
|
817
|
+
)?.get?.call(value);
|
|
818
|
+
const entries = [];
|
|
819
|
+
for (let index = 0; index < Math.min(count, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties); index++)
|
|
820
|
+
entries.push(iterator.next().value);
|
|
821
|
+
if (count > entries.length)
|
|
822
|
+
omissions.push({
|
|
823
|
+
path,
|
|
824
|
+
reason: "collection_limit",
|
|
825
|
+
originalCount: count
|
|
826
|
+
});
|
|
827
|
+
return isMap ? {
|
|
828
|
+
$type: "Map",
|
|
829
|
+
entries: visit(entries, `${path}/entries`, depth + 1)
|
|
830
|
+
} : {
|
|
831
|
+
$type: "Set",
|
|
832
|
+
values: visit(entries, `${path}/values`, depth + 1)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
if (Array.isArray(value)) {
|
|
836
|
+
const count = Math.min(value.length, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties);
|
|
837
|
+
const items = Array.from(
|
|
838
|
+
{ length: count },
|
|
839
|
+
(_, index) => read(value, String(index), `${path}/${index}`, depth)
|
|
840
|
+
);
|
|
841
|
+
if (count < value.length)
|
|
842
|
+
items.push(
|
|
843
|
+
omit(path, "collection_limit", { originalCount: value.length })
|
|
844
|
+
);
|
|
845
|
+
return items;
|
|
846
|
+
}
|
|
847
|
+
const keys = Object.getOwnPropertyNames(object);
|
|
848
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
849
|
+
if (value instanceof Error) {
|
|
850
|
+
const ownName = Object.getOwnPropertyDescriptor(object, "name");
|
|
851
|
+
let type = ownName && "value" in ownName ? ownName.value : void 0;
|
|
852
|
+
if (typeof type !== "string") {
|
|
853
|
+
const name = Object.getOwnPropertyDescriptor(
|
|
854
|
+
Object.getPrototypeOf(value),
|
|
855
|
+
"name"
|
|
856
|
+
);
|
|
857
|
+
type = name && "value" in name ? name.value : "Error";
|
|
858
|
+
}
|
|
859
|
+
result.type = visit(
|
|
860
|
+
typeof type === "string" ? type : "Error",
|
|
861
|
+
`${path}/type`,
|
|
862
|
+
depth + 1
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
for (const key of keys.slice(0, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties)) {
|
|
866
|
+
if (["__proto__", "constructor", "prototype"].includes(key)) {
|
|
867
|
+
result[key] = omit(`${path}/${pointer(key)}`, "unsafe_key");
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
remaining -= encoder.encode(key).length + 4;
|
|
871
|
+
result[key] = read(object, key, `${path}/${pointer(key)}`, depth);
|
|
872
|
+
}
|
|
873
|
+
if (keys.length > import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties)
|
|
874
|
+
result.$omittedProperties = omit(path, "property_limit", {
|
|
875
|
+
originalCount: keys.length
|
|
876
|
+
});
|
|
877
|
+
return result;
|
|
735
878
|
} catch {
|
|
736
|
-
return
|
|
879
|
+
return omit(path, "unreadable_object");
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
const read = (object, key, path, depth) => {
|
|
883
|
+
if ((0, import_telemetry_contracts.isPrivateDiagnosticField)(key)) {
|
|
884
|
+
if (redactions.length < 1e3)
|
|
885
|
+
redactions.push({
|
|
886
|
+
path: path.slice(0, 4096),
|
|
887
|
+
reason: "credential_field"
|
|
888
|
+
});
|
|
889
|
+
return "[REDACTED]";
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
const property = Object.getOwnPropertyDescriptor(object, key);
|
|
893
|
+
if (property && "value" in property)
|
|
894
|
+
return visit(property.value, path, depth + 1);
|
|
895
|
+
if (key === "stack" && object instanceof Error && property?.get && property.get === nativeStackGetter)
|
|
896
|
+
return visit(nativeStack(object, property.get), path, depth + 1);
|
|
897
|
+
return omit(
|
|
898
|
+
path,
|
|
899
|
+
property ? "accessor_not_evaluated" : "unavailable_property"
|
|
900
|
+
);
|
|
901
|
+
} catch {
|
|
902
|
+
return omit(path, "unreadable_property");
|
|
737
903
|
}
|
|
738
|
-
})() : void 0);
|
|
739
|
-
const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors2.errorProperty)(value, "cause") : null;
|
|
740
|
-
return {
|
|
741
|
-
type: typeof name === "string" ? name.slice(0, 256) : "Error",
|
|
742
|
-
message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
|
|
743
|
-
...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
744
|
-
...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
|
|
745
904
|
};
|
|
746
|
-
};
|
|
747
|
-
var sanitizeProjectedDetails = (details, depth = 0) => ({
|
|
748
|
-
...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
|
|
749
|
-
message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
|
|
750
|
-
...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
751
|
-
...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
|
|
752
|
-
});
|
|
753
|
-
function exceptionDiagnostics(error, project) {
|
|
754
905
|
try {
|
|
755
|
-
const
|
|
756
|
-
|
|
906
|
+
const original = project ? project(error) : error;
|
|
907
|
+
if (project && original === null) return null;
|
|
908
|
+
const captured = visit(original, "#/data", 0);
|
|
909
|
+
const upstreamOmissions = (0, import_errors2.errorProperty)(original, "diagnosticOmissions");
|
|
910
|
+
if (Array.isArray(upstreamOmissions))
|
|
911
|
+
for (const note of upstreamOmissions.slice(0, 1e3)) {
|
|
912
|
+
const path = (0, import_errors2.errorProperty)(note, "path");
|
|
913
|
+
const reason = (0, import_errors2.errorProperty)(note, "reason");
|
|
914
|
+
if (typeof path === "string" && typeof reason === "string")
|
|
915
|
+
omissions.push({
|
|
916
|
+
path: (0, import_telemetry_contracts.redactDiagnosticText)(path).slice(0, 4096),
|
|
917
|
+
reason: (0, import_telemetry_contracts.redactDiagnosticText)(reason).slice(0, 128)
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
const data = captured && typeof captured === "object" && !Array.isArray(captured) ? captured : {
|
|
921
|
+
type: "Error",
|
|
922
|
+
message: typeof captured === "string" ? captured : "Execution reported a fault.",
|
|
923
|
+
value: captured
|
|
924
|
+
};
|
|
925
|
+
if (typeof data.message !== "string" && !("message" in data))
|
|
926
|
+
data.message = "Execution reported a fault.";
|
|
927
|
+
if (!("type" in data))
|
|
928
|
+
data.type = typeof data.name === "string" ? data.name : "Error";
|
|
929
|
+
const content = {
|
|
930
|
+
schemaVersion: 1,
|
|
931
|
+
data,
|
|
932
|
+
capture: {
|
|
933
|
+
status: omissions.length ? "partial" : "complete",
|
|
934
|
+
omissions,
|
|
935
|
+
redactions
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
if (encoder.encode(JSON.stringify(content)).length > import_telemetry_contracts.DIAGNOSTIC_LIMITS.bytes)
|
|
939
|
+
return {
|
|
940
|
+
schemaVersion: 1,
|
|
941
|
+
data: {
|
|
942
|
+
type: "Error",
|
|
943
|
+
message: "Diagnostic exceeded the capture budget."
|
|
944
|
+
},
|
|
945
|
+
capture: {
|
|
946
|
+
status: "partial",
|
|
947
|
+
redactions: [],
|
|
948
|
+
omissions: [{ path: "#/data", reason: "serialized_byte_limit" }]
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
return content;
|
|
757
952
|
} catch {
|
|
758
|
-
return
|
|
953
|
+
return {
|
|
954
|
+
schemaVersion: 1,
|
|
955
|
+
data: { type: "Error", message: "Diagnostic capture failed." },
|
|
956
|
+
capture: {
|
|
957
|
+
status: "failed",
|
|
958
|
+
redactions: [],
|
|
959
|
+
omissions: [{ path: "#/data", reason: "projection_or_capture_failed" }]
|
|
960
|
+
}
|
|
961
|
+
};
|
|
759
962
|
}
|
|
760
963
|
}
|
|
964
|
+
|
|
965
|
+
// src/error-diagnostics.ts
|
|
966
|
+
function exceptionDiagnostics(error, project) {
|
|
967
|
+
const content = captureDiagnosticContent(error, project);
|
|
968
|
+
return !content || content.capture.status === "failed" ? null : content.data;
|
|
969
|
+
}
|
|
761
970
|
function errorDiagnostics(error, project) {
|
|
762
|
-
const
|
|
971
|
+
const captured = exceptionDiagnostics(error, project);
|
|
972
|
+
const details = captured ? (0, import_telemetry_contracts2.summarizeDiagnostic)(captured) : null;
|
|
763
973
|
return details ? {
|
|
764
|
-
errorMessage: details.message,
|
|
765
|
-
...details.type ? { errorType: details.type } : {}
|
|
974
|
+
errorMessage: details.message.slice(0, 8192),
|
|
975
|
+
...details.type ? { errorType: details.type.slice(0, 256) } : {}
|
|
766
976
|
} : {};
|
|
767
977
|
}
|
|
768
978
|
|
|
@@ -854,10 +1064,40 @@ async function closeOwnedTools(owned) {
|
|
|
854
1064
|
function captureToolError(observation, error, policy, returned = false) {
|
|
855
1065
|
try {
|
|
856
1066
|
const reported = returned && error && typeof error === "object" && "isError" in error && error.isError === true && "content" in error && typeof error.content === "string" ? { name: "ToolError", message: error.content } : error;
|
|
1067
|
+
let projected = false;
|
|
1068
|
+
let projection;
|
|
1069
|
+
let projectionFailure;
|
|
1070
|
+
let projectionFailed = false;
|
|
1071
|
+
const project = policy?.error ? (original) => {
|
|
1072
|
+
if (!projected) {
|
|
1073
|
+
projected = true;
|
|
1074
|
+
try {
|
|
1075
|
+
projection = policy.error?.(original);
|
|
1076
|
+
} catch (error2) {
|
|
1077
|
+
projectionFailure = error2;
|
|
1078
|
+
projectionFailed = true;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (projectionFailed) throw projectionFailure;
|
|
1082
|
+
return projection ?? null;
|
|
1083
|
+
} : void 0;
|
|
857
1084
|
Object.assign(
|
|
858
1085
|
observation,
|
|
859
|
-
errorDiagnostics(policy?.error ? error : reported,
|
|
1086
|
+
errorDiagnostics(policy?.error ? error : reported, project)
|
|
1087
|
+
);
|
|
1088
|
+
const diagnosticId = getHookContext().reasonTrace?.reportError?.(
|
|
1089
|
+
policy?.error ? error : reported,
|
|
1090
|
+
{
|
|
1091
|
+
mechanism: "tool",
|
|
1092
|
+
...project ? { error: project } : {},
|
|
1093
|
+
correlation: {
|
|
1094
|
+
toolCallId: observation.toolCallId,
|
|
1095
|
+
attemptId: observation.attemptId
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
860
1098
|
);
|
|
1099
|
+
if (typeof diagnosticId === "string")
|
|
1100
|
+
observation.diagnosticId = diagnosticId;
|
|
861
1101
|
} catch {
|
|
862
1102
|
}
|
|
863
1103
|
}
|
|
@@ -2743,7 +2983,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2743
2983
|
interruptInstructions: [
|
|
2744
2984
|
"Human-input rules:",
|
|
2745
2985
|
"- The kortyx_request_input__* tools pause this reasoning operation and return the validated human response as a normal tool result.",
|
|
2746
|
-
"-
|
|
2986
|
+
"- You may combine human-input tools with domain tools and output controls. Tools, emitted outputs, and human-input requests run in their requested order. Terminal output publishes last using the completed results.",
|
|
2747
2987
|
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."
|
|
2748
2988
|
].join("\n")
|
|
2749
2989
|
} : {},
|
|
@@ -2753,7 +2993,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2753
2993
|
"- The kortyx_emit__* tools publish validated structured output and return an acknowledgement. You may continue with text or more tools afterward.",
|
|
2754
2994
|
"- The kortyx_return__* tools publish a validated result and end this reasoning operation. If return tools are available, call exactly one before finishing.",
|
|
2755
2995
|
"- 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.",
|
|
2756
|
-
"-
|
|
2996
|
+
"- You may combine multiple emit controls, domain tools, and human-input requests in one turn. Tools, emitted outputs, and human-input requests run in their requested order; the terminal return runs last. Outputs after domain tools or human input are regenerated using their completed results. Request at most one terminal return per turn."
|
|
2757
2997
|
].join("\n")
|
|
2758
2998
|
} : {}
|
|
2759
2999
|
});
|
|
@@ -2782,6 +3022,19 @@ var runReasonToolLoop = async (args) => {
|
|
|
2782
3022
|
);
|
|
2783
3023
|
let pending = checkpoint?.pending;
|
|
2784
3024
|
let pendingStepIndex = checkpoint?.pendingStepIndex;
|
|
3025
|
+
let outputContractCorrections = checkpoint?.outputContractCorrections ?? 0;
|
|
3026
|
+
const correctionExhausted = () => {
|
|
3027
|
+
const message = `useReason could not complete output-contract correction within toolExecution.maxSteps (${maxSteps}). Select exactly one terminal return contract; multiple emit contracts are allowed. Increase maxSteps to allow correction and output generation.`;
|
|
3028
|
+
return new import_errors6.ProviderRequestError(message, {
|
|
3029
|
+
code: "REASON_OUTPUT_CONTRACT_CORRECTION_EXHAUSTED",
|
|
3030
|
+
operation: "useReason",
|
|
3031
|
+
safeMessage: message,
|
|
3032
|
+
retryable: false,
|
|
3033
|
+
details: { maxSteps, outputContractCorrections }
|
|
3034
|
+
});
|
|
3035
|
+
};
|
|
3036
|
+
const resolvedInterrupts = checkpoint?.resolvedInterrupts ?? [];
|
|
3037
|
+
for (const request of resolvedInterrupts) ctx.node.replayInterrupt?.(request);
|
|
2785
3038
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
2786
3039
|
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
2787
3040
|
const emissions = checkpoint?.emissions ?? [];
|
|
@@ -2793,8 +3046,10 @@ var runReasonToolLoop = async (args) => {
|
|
|
2793
3046
|
messages,
|
|
2794
3047
|
steps,
|
|
2795
3048
|
approvedCalls,
|
|
3049
|
+
resolvedInterrupts,
|
|
2796
3050
|
interruptHistory,
|
|
2797
3051
|
emissions,
|
|
3052
|
+
outputContractCorrections,
|
|
2798
3053
|
...returned ? { returned } : {},
|
|
2799
3054
|
finalizing,
|
|
2800
3055
|
...pending ? { pending } : {},
|
|
@@ -2802,6 +3057,10 @@ var runReasonToolLoop = async (args) => {
|
|
|
2802
3057
|
};
|
|
2803
3058
|
ctx.stateDirty = true;
|
|
2804
3059
|
};
|
|
3060
|
+
const rememberInterrupt = (request) => {
|
|
3061
|
+
resolvedInterrupts.push(request);
|
|
3062
|
+
save();
|
|
3063
|
+
};
|
|
2805
3064
|
const emitToolEvent = (event, payload) => {
|
|
2806
3065
|
ctx.node.emit(event, {
|
|
2807
3066
|
...payload,
|
|
@@ -2811,7 +3070,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2811
3070
|
});
|
|
2812
3071
|
};
|
|
2813
3072
|
try {
|
|
2814
|
-
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex
|
|
3073
|
+
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex = steps.length) {
|
|
2815
3074
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2816
3075
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
2817
3076
|
stepIndex,
|
|
@@ -2948,273 +3207,301 @@ var runReasonToolLoop = async (args) => {
|
|
|
2948
3207
|
const outputCalls = toolCalls.filter(
|
|
2949
3208
|
(call) => outputByToolName.has(call.name)
|
|
2950
3209
|
);
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
const call
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
3210
|
+
const returnCalls = outputCalls.filter(
|
|
3211
|
+
(call) => outputByToolName.get(call.name)?.kind === "return"
|
|
3212
|
+
);
|
|
3213
|
+
if (returnCalls.length > 1) {
|
|
3214
|
+
let corrected = false;
|
|
3215
|
+
for (const call of returnCalls) {
|
|
3216
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id))
|
|
3217
|
+
continue;
|
|
3218
|
+
const result2 = {
|
|
3219
|
+
toolCallId: call.id,
|
|
3220
|
+
name: call.name,
|
|
3221
|
+
isError: true,
|
|
3222
|
+
content: "MULTIPLE_TERMINAL_OUTPUTS: No terminal output was published. Select exactly one return contract using the tool and human-input results. Do not repeat completed calls. Multiple emit contracts are allowed."
|
|
3223
|
+
};
|
|
3224
|
+
toolResults.push(result2);
|
|
3225
|
+
messages.push({ role: "tool", ...result2 });
|
|
3226
|
+
corrected = true;
|
|
2968
3227
|
}
|
|
2969
|
-
if (
|
|
2970
|
-
|
|
2971
|
-
|
|
3228
|
+
if (corrected) outputContractCorrections += 1;
|
|
3229
|
+
save();
|
|
3230
|
+
}
|
|
3231
|
+
const orderedCalls = [
|
|
3232
|
+
...toolCalls.filter(
|
|
3233
|
+
(call) => outputByToolName.get(call.name)?.kind !== "return"
|
|
3234
|
+
),
|
|
3235
|
+
...returnCalls
|
|
3236
|
+
];
|
|
3237
|
+
for (const toolCall of orderedCalls) {
|
|
3238
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
3239
|
+
if (outputByToolName.has(toolCall.name)) {
|
|
3240
|
+
const call = toolCall;
|
|
3241
|
+
const hasEvidenceCalls = toolResults.some(
|
|
3242
|
+
(result3) => !outputByToolName.has(result3.name)
|
|
2972
3243
|
);
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
if (!stream)
|
|
3244
|
+
const resolved = outputByToolName.get(call.name);
|
|
3245
|
+
if (!resolved)
|
|
2976
3246
|
throw new Error(
|
|
2977
|
-
`useReason
|
|
3247
|
+
`Unknown useReason output contract tool "${call.name}".`
|
|
2978
3248
|
);
|
|
2979
|
-
if (
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
if (typeof instruction !== "string" || instruction.trim().length === 0)
|
|
3249
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id)) {
|
|
3250
|
+
if (resolved.kind === "return" && returned) completed = true;
|
|
3251
|
+
continue;
|
|
3252
|
+
}
|
|
3253
|
+
if (resolved.kind === "emit" && emissions.length >= (useReasonArgs.outputs?.maxEmissions ?? 16))
|
|
2985
3254
|
throw new Error(
|
|
2986
|
-
`useReason
|
|
2987
|
-
);
|
|
2988
|
-
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
2989
|
-
const structured = {
|
|
2990
|
-
dataType: resolved.contract.schemaId,
|
|
2991
|
-
schemaId: resolved.contract.schemaId,
|
|
2992
|
-
schemaVersion: resolved.contract.schemaVersion,
|
|
2993
|
-
fields: resolved.contract.stream?.fields
|
|
2994
|
-
};
|
|
2995
|
-
const inferred = inferOutputFormat(
|
|
2996
|
-
resolved.contract.schema,
|
|
2997
|
-
void 0
|
|
2998
|
-
);
|
|
2999
|
-
try {
|
|
3000
|
-
const outputStep = await reasonEngine(
|
|
3001
|
-
{
|
|
3002
|
-
...useReasonArgs,
|
|
3003
|
-
tools: [],
|
|
3004
|
-
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3005
|
-
messages: [
|
|
3006
|
-
...messages,
|
|
3007
|
-
{
|
|
3008
|
-
role: "tool",
|
|
3009
|
-
toolCallId: call.id,
|
|
3010
|
-
name: call.name,
|
|
3011
|
-
content: "Generating the requested structured output."
|
|
3012
|
-
},
|
|
3013
|
-
{
|
|
3014
|
-
role: "user",
|
|
3015
|
-
content: withOutputGuardrails(
|
|
3016
|
-
instruction,
|
|
3017
|
-
resolved.contract.schema
|
|
3018
|
-
)
|
|
3019
|
-
}
|
|
3020
|
-
],
|
|
3021
|
-
emit: false,
|
|
3022
|
-
stream,
|
|
3023
|
-
...emit && stream ? {
|
|
3024
|
-
onTextChunk: createStructuredOutputStreamer(
|
|
3025
|
-
{ structured },
|
|
3026
|
-
id,
|
|
3027
|
-
streamId
|
|
3028
|
-
)
|
|
3029
|
-
} : {}
|
|
3030
|
-
},
|
|
3031
|
-
{ ...id ? { id } : {}, opId }
|
|
3255
|
+
`useReason reached outputs.maxEmissions (${useReasonArgs.outputs?.maxEmissions ?? 16}).`
|
|
3032
3256
|
);
|
|
3033
|
-
|
|
3257
|
+
let data;
|
|
3258
|
+
if (resolved.stream || hasEvidenceCalls) {
|
|
3259
|
+
if (resolved.stream && !stream)
|
|
3034
3260
|
throw new Error(
|
|
3035
|
-
`
|
|
3261
|
+
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
3036
3262
|
);
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3263
|
+
if (steps.length >= maxSteps) {
|
|
3264
|
+
const message = `useReason output generation requires another model pass within toolExecution.maxSteps (${maxSteps}). Increase maxSteps to allow outputs to use completed tool and interrupt results.`;
|
|
3265
|
+
throw new import_errors6.ProviderRequestError(message, {
|
|
3266
|
+
code: "REASON_OUTPUT_BUDGET_EXHAUSTED",
|
|
3267
|
+
operation: "useReason",
|
|
3268
|
+
safeMessage: message,
|
|
3269
|
+
retryable: false,
|
|
3270
|
+
details: { maxSteps, modelPasses: steps.length }
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
const streamInstruction = call.input && typeof call.input === "object" && !Array.isArray(call.input) ? call.input.instruction : void 0;
|
|
3274
|
+
if (resolved.stream && (typeof streamInstruction !== "string" || streamInstruction.trim().length === 0))
|
|
3275
|
+
throw new Error(
|
|
3276
|
+
`useReason streaming output contract "${resolved.name}" requires an instruction.`
|
|
3277
|
+
);
|
|
3278
|
+
const instruction = [
|
|
3279
|
+
`Generate output contract "${resolved.name}": ${resolved.contract.description}`,
|
|
3280
|
+
"Use the completed tool and human-input results. Do not assume results for pending calls.",
|
|
3281
|
+
resolved.stream ? String(streamInstruction) : `Regenerate this original draft, which was written before those results were available: ${JSON.stringify(call.input)}`
|
|
3282
|
+
].join("\n\n");
|
|
3283
|
+
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
3284
|
+
const structured = {
|
|
3285
|
+
dataType: resolved.contract.schemaId,
|
|
3286
|
+
schemaId: resolved.contract.schemaId,
|
|
3287
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3288
|
+
fields: resolved.contract.stream?.fields
|
|
3289
|
+
};
|
|
3290
|
+
const inferred = inferOutputFormat(
|
|
3291
|
+
resolved.contract.schema,
|
|
3292
|
+
void 0
|
|
3046
3293
|
);
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
data = parseReasonOutputWithSchema({
|
|
3050
|
-
text: outputStep.text,
|
|
3051
|
-
schema: resolved.contract.schema,
|
|
3052
|
-
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3053
|
-
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3054
|
-
label: `useReason output contract "${resolved.name}"`
|
|
3055
|
-
});
|
|
3056
|
-
steps.push({
|
|
3057
|
-
stepIndex: stepIndex + 1,
|
|
3294
|
+
const outputRecord = {
|
|
3295
|
+
stepIndex: steps.length,
|
|
3058
3296
|
kind: "output",
|
|
3059
|
-
text:
|
|
3297
|
+
text: "",
|
|
3060
3298
|
toolCalls: [],
|
|
3061
|
-
toolResults: []
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3299
|
+
toolResults: []
|
|
3300
|
+
};
|
|
3301
|
+
steps.push(outputRecord);
|
|
3302
|
+
save();
|
|
3303
|
+
try {
|
|
3304
|
+
const outputStep = await reasonEngine(
|
|
3305
|
+
{
|
|
3306
|
+
...useReasonArgs,
|
|
3307
|
+
tools: [],
|
|
3308
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3309
|
+
messages: [
|
|
3310
|
+
...messages,
|
|
3311
|
+
...toolCalls.filter(
|
|
3312
|
+
(pendingCall) => !toolResults.some(
|
|
3313
|
+
(result3) => result3.toolCallId === pendingCall.id
|
|
3314
|
+
)
|
|
3315
|
+
).map(
|
|
3316
|
+
(pendingCall) => ({
|
|
3317
|
+
role: "tool",
|
|
3318
|
+
toolCallId: pendingCall.id,
|
|
3319
|
+
name: pendingCall.name,
|
|
3320
|
+
content: "This call has not completed yet. Use only completed results; do not assume a result for this pending call."
|
|
3321
|
+
})
|
|
3322
|
+
),
|
|
3323
|
+
{
|
|
3324
|
+
role: "user",
|
|
3325
|
+
content: withOutputGuardrails(
|
|
3326
|
+
instruction,
|
|
3327
|
+
resolved.contract.schema
|
|
3328
|
+
)
|
|
3329
|
+
}
|
|
3330
|
+
],
|
|
3331
|
+
emit: false,
|
|
3332
|
+
stream: resolved.stream && stream,
|
|
3333
|
+
...emit && resolved.stream && stream ? {
|
|
3334
|
+
onTextChunk: createStructuredOutputStreamer(
|
|
3335
|
+
{ structured },
|
|
3336
|
+
id,
|
|
3337
|
+
streamId
|
|
3338
|
+
)
|
|
3339
|
+
} : {}
|
|
3340
|
+
},
|
|
3341
|
+
{ ...id ? { id } : {}, opId }
|
|
3342
|
+
);
|
|
3343
|
+
if (outputStep.toolCalls?.length)
|
|
3344
|
+
throw new Error(
|
|
3345
|
+
`Provider requested a tool during streamed output contract "${resolved.name}".`
|
|
3346
|
+
);
|
|
3347
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, outputStep.usage);
|
|
3348
|
+
accumulateTokenUsage(outputStep.usage);
|
|
3349
|
+
aggregatedWarnings = mergeWarnings(
|
|
3350
|
+
aggregatedWarnings,
|
|
3351
|
+
mergeWarnings(inferred.warnings, outputStep.warnings)
|
|
3352
|
+
);
|
|
3353
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3354
|
+
aggregatedProviderMetadata,
|
|
3355
|
+
outputStep.providerMetadata
|
|
3356
|
+
);
|
|
3357
|
+
finalFinishReason = outputStep.finishReason;
|
|
3358
|
+
finalRaw = outputStep.raw;
|
|
3359
|
+
Object.assign(outputRecord, {
|
|
3360
|
+
stepIndex: outputRecord.stepIndex,
|
|
3361
|
+
kind: "output",
|
|
3362
|
+
text: outputStep.text,
|
|
3363
|
+
toolCalls: [],
|
|
3364
|
+
toolResults: [],
|
|
3365
|
+
...outputStep.usage ? { usage: outputStep.usage } : {},
|
|
3366
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3367
|
+
...outputStep.providerMetadata ? { providerMetadata: outputStep.providerMetadata } : {},
|
|
3368
|
+
...outputStep.warnings ? { warnings: outputStep.warnings } : {}
|
|
3369
|
+
});
|
|
3370
|
+
save();
|
|
3371
|
+
data = parseReasonOutputWithSchema({
|
|
3372
|
+
text: outputStep.text,
|
|
3373
|
+
schema: resolved.contract.schema,
|
|
3374
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3375
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3376
|
+
label: `useReason output contract "${resolved.name}"`
|
|
3377
|
+
});
|
|
3378
|
+
} catch (error) {
|
|
3379
|
+
traceSpan?.addEvent?.("useReason.output.invalidated", {
|
|
3380
|
+
contract: resolved.name,
|
|
3381
|
+
kind: resolved.kind,
|
|
3382
|
+
schemaId: resolved.contract.schemaId,
|
|
3383
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3384
|
+
streamId
|
|
3385
|
+
});
|
|
3386
|
+
if (emit && resolved.stream) {
|
|
3387
|
+
try {
|
|
3388
|
+
ctx.node.emit("structured_data_invalidated", {
|
|
3389
|
+
node: ctx.node.graph.node,
|
|
3390
|
+
streamId
|
|
3391
|
+
});
|
|
3392
|
+
} catch {
|
|
3393
|
+
}
|
|
3083
3394
|
}
|
|
3395
|
+
throw error;
|
|
3084
3396
|
}
|
|
3085
|
-
|
|
3397
|
+
} else {
|
|
3398
|
+
data = parseWithSchema(
|
|
3399
|
+
resolved.contract.schema,
|
|
3400
|
+
call.input,
|
|
3401
|
+
`useReason output contract "${resolved.name}"`
|
|
3402
|
+
);
|
|
3086
3403
|
}
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
)
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3404
|
+
const entry = { contract: resolved.name, data };
|
|
3405
|
+
const emissionIndex = emissions.length;
|
|
3406
|
+
if (resolved.kind === "emit")
|
|
3407
|
+
emissions.push(entry);
|
|
3408
|
+
else returned = entry;
|
|
3409
|
+
if (emit)
|
|
3410
|
+
emitStructuredData({
|
|
3411
|
+
kind: "final",
|
|
3412
|
+
data,
|
|
3413
|
+
dataType: resolved.contract.schemaId,
|
|
3414
|
+
schemaId: resolved.contract.schemaId,
|
|
3415
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3416
|
+
streamId: `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissionIndex : "return"}`,
|
|
3417
|
+
...id ? { id } : {}
|
|
3418
|
+
});
|
|
3419
|
+
traceSpan?.addEvent?.("useReason.output.emitted", {
|
|
3420
|
+
contract: resolved.name,
|
|
3421
|
+
kind: resolved.kind,
|
|
3104
3422
|
schemaId: resolved.contract.schemaId,
|
|
3105
3423
|
schemaVersion: resolved.contract.schemaVersion,
|
|
3106
|
-
|
|
3107
|
-
|
|
3424
|
+
emissionIndex,
|
|
3425
|
+
data
|
|
3108
3426
|
});
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
pending = void 0;
|
|
3119
|
-
pendingStepIndex = void 0;
|
|
3120
|
-
completed = true;
|
|
3427
|
+
const result2 = normalizeToolResult(call, { emitted: true, data });
|
|
3428
|
+
toolResults.push(result2);
|
|
3429
|
+
messages.push({
|
|
3430
|
+
role: "tool",
|
|
3431
|
+
content: result2.content,
|
|
3432
|
+
toolCallId: result2.toolCallId,
|
|
3433
|
+
name: result2.name
|
|
3434
|
+
});
|
|
3435
|
+
if (resolved.kind === "return") completed = true;
|
|
3121
3436
|
save();
|
|
3122
|
-
|
|
3437
|
+
continue;
|
|
3123
3438
|
}
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
if (toolCalls.length !== 1)
|
|
3142
|
-
throw new Error(
|
|
3143
|
-
"A useReason interrupt contract call must be the only tool call in its model turn."
|
|
3144
|
-
);
|
|
3145
|
-
const interruptCall = interruptCalls[0];
|
|
3146
|
-
if (!interruptCall)
|
|
3147
|
-
throw new Error("Missing useReason interrupt control call.");
|
|
3148
|
-
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
3149
|
-
throw new Error(
|
|
3150
|
-
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3151
|
-
);
|
|
3152
|
-
const resolved = interruptByToolName.get(interruptCall.name);
|
|
3153
|
-
if (!resolved)
|
|
3154
|
-
throw new Error(
|
|
3155
|
-
`Unknown useReason interrupt contract tool "${interruptCall.name}".`
|
|
3439
|
+
if (interruptByToolName.has(toolCall.name)) {
|
|
3440
|
+
const resolved = interruptByToolName.get(toolCall.name);
|
|
3441
|
+
if (!resolved)
|
|
3442
|
+
throw new Error(
|
|
3443
|
+
`Unknown useReason interrupt contract tool "${toolCall.name}".`
|
|
3444
|
+
);
|
|
3445
|
+
if (toolResults.some((result3) => result3.toolCallId === toolCall.id)) {
|
|
3446
|
+
continue;
|
|
3447
|
+
}
|
|
3448
|
+
if (interruptHistory.length >= (interruptConfig?.maxRequests ?? 1))
|
|
3449
|
+
throw new Error(
|
|
3450
|
+
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3451
|
+
);
|
|
3452
|
+
const request = parseWithSchema(
|
|
3453
|
+
resolved.contract.requestSchema,
|
|
3454
|
+
toolCall.input,
|
|
3455
|
+
`useReason interrupt contract "${resolved.name}" request`
|
|
3156
3456
|
);
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3457
|
+
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3458
|
+
checkpointKey,
|
|
3459
|
+
contract: resolved.name,
|
|
3460
|
+
interruptIndex: interruptHistory.length
|
|
3461
|
+
});
|
|
3462
|
+
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
3463
|
+
const response = await awaitInterruptInternal({
|
|
3464
|
+
onResolved: rememberInterrupt,
|
|
3465
|
+
request: {
|
|
3466
|
+
kind: "custom",
|
|
3467
|
+
request,
|
|
3468
|
+
contract: resolved.name,
|
|
3469
|
+
...question ? { question } : {},
|
|
3470
|
+
schemaId: resolved.contract.schemaId,
|
|
3471
|
+
schemaVersion: resolved.contract.schemaVersion
|
|
3472
|
+
},
|
|
3473
|
+
responseSchema: resolved.contract.responseSchema,
|
|
3474
|
+
id: `${id ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
3475
|
+
meta: {
|
|
3476
|
+
__kortyxReason: {
|
|
3477
|
+
opId,
|
|
3478
|
+
stepIndex,
|
|
3479
|
+
interruptIndex: interruptHistory.length,
|
|
3480
|
+
contract: resolved.name
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
});
|
|
3484
|
+
const result2 = normalizeToolResult(toolCall, response);
|
|
3485
|
+
toolResults.push(result2);
|
|
3486
|
+
messages.push({
|
|
3487
|
+
role: "tool",
|
|
3488
|
+
content: result2.content,
|
|
3489
|
+
toolCallId: result2.toolCallId,
|
|
3490
|
+
name: result2.name
|
|
3491
|
+
});
|
|
3492
|
+
interruptHistory.push({
|
|
3493
|
+
contract: resolved.name,
|
|
3494
|
+
request,
|
|
3495
|
+
response
|
|
3496
|
+
});
|
|
3497
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3498
|
+
checkpointKey,
|
|
3499
|
+
contract: resolved.name,
|
|
3500
|
+
interruptIndex: interruptHistory.length - 1
|
|
3501
|
+
});
|
|
3160
3502
|
save();
|
|
3161
3503
|
continue;
|
|
3162
3504
|
}
|
|
3163
|
-
const request = parseWithSchema(
|
|
3164
|
-
resolved.contract.requestSchema,
|
|
3165
|
-
interruptCall.input,
|
|
3166
|
-
`useReason interrupt contract "${resolved.name}" request`
|
|
3167
|
-
);
|
|
3168
|
-
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3169
|
-
checkpointKey,
|
|
3170
|
-
contract: resolved.name,
|
|
3171
|
-
interruptIndex: interruptHistory.length
|
|
3172
|
-
});
|
|
3173
|
-
const question = request && typeof request === "object" && !Array.isArray(request) && typeof request.question === "string" ? String(request.question) : void 0;
|
|
3174
|
-
const response = await awaitInterruptInternal({
|
|
3175
|
-
request: {
|
|
3176
|
-
kind: "custom",
|
|
3177
|
-
request,
|
|
3178
|
-
contract: resolved.name,
|
|
3179
|
-
...question ? { question } : {},
|
|
3180
|
-
schemaId: resolved.contract.schemaId,
|
|
3181
|
-
schemaVersion: resolved.contract.schemaVersion
|
|
3182
|
-
},
|
|
3183
|
-
responseSchema: resolved.contract.responseSchema,
|
|
3184
|
-
id: `${id ?? "reason"}:${resolved.name}:${interruptHistory.length + 1}`,
|
|
3185
|
-
meta: {
|
|
3186
|
-
__kortyxReason: {
|
|
3187
|
-
opId,
|
|
3188
|
-
stepIndex,
|
|
3189
|
-
interruptIndex: interruptHistory.length,
|
|
3190
|
-
contract: resolved.name
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
|
-
});
|
|
3194
|
-
const result2 = normalizeToolResult(interruptCall, response);
|
|
3195
|
-
toolResults.push(result2);
|
|
3196
|
-
messages.push({
|
|
3197
|
-
role: "tool",
|
|
3198
|
-
content: result2.content,
|
|
3199
|
-
toolCallId: result2.toolCallId,
|
|
3200
|
-
name: result2.name
|
|
3201
|
-
});
|
|
3202
|
-
interruptHistory.push({
|
|
3203
|
-
contract: resolved.name,
|
|
3204
|
-
request,
|
|
3205
|
-
response
|
|
3206
|
-
});
|
|
3207
|
-
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3208
|
-
checkpointKey,
|
|
3209
|
-
contract: resolved.name,
|
|
3210
|
-
interruptIndex: interruptHistory.length - 1
|
|
3211
|
-
});
|
|
3212
|
-
pending = void 0;
|
|
3213
|
-
pendingStepIndex = void 0;
|
|
3214
|
-
save();
|
|
3215
|
-
continue;
|
|
3216
|
-
}
|
|
3217
|
-
for (const toolCall of toolCalls) {
|
|
3218
3505
|
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
3219
3506
|
await replayToolObservations(
|
|
3220
3507
|
steps[stepIndex]?.toolObservations?.filter(
|
|
@@ -3260,6 +3547,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3260
3547
|
"waiting"
|
|
3261
3548
|
);
|
|
3262
3549
|
const approval = await awaitInterruptInternal({
|
|
3550
|
+
onResolved: rememberInterrupt,
|
|
3263
3551
|
id: `tool:${toolCall.id}`,
|
|
3264
3552
|
request: {
|
|
3265
3553
|
kind: "choice",
|
|
@@ -3374,22 +3662,24 @@ var runReasonToolLoop = async (args) => {
|
|
|
3374
3662
|
});
|
|
3375
3663
|
}
|
|
3376
3664
|
}
|
|
3377
|
-
const
|
|
3378
|
-
if (
|
|
3665
|
+
const completedToolResult = toolResults.at(-1);
|
|
3666
|
+
if (completedToolResult)
|
|
3379
3667
|
messages.push({
|
|
3380
3668
|
role: "tool",
|
|
3381
|
-
content:
|
|
3382
|
-
toolCallId:
|
|
3383
|
-
name:
|
|
3384
|
-
...
|
|
3669
|
+
content: completedToolResult.content,
|
|
3670
|
+
toolCallId: completedToolResult.toolCallId,
|
|
3671
|
+
name: completedToolResult.name,
|
|
3672
|
+
...completedToolResult.isError !== void 0 ? { isError: completedToolResult.isError } : {}
|
|
3385
3673
|
});
|
|
3386
3674
|
save();
|
|
3387
3675
|
}
|
|
3388
3676
|
pending = void 0;
|
|
3389
3677
|
pendingStepIndex = void 0;
|
|
3390
3678
|
save();
|
|
3679
|
+
if (completed) break;
|
|
3391
3680
|
}
|
|
3392
3681
|
if (!completed) {
|
|
3682
|
+
if (outputContractCorrections > 0) throw correctionExhausted();
|
|
3393
3683
|
throw new Error(
|
|
3394
3684
|
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
3395
3685
|
);
|
|
@@ -3451,7 +3741,11 @@ var runReasonToolLoop = async (args) => {
|
|
|
3451
3741
|
output: useReasonArgs.telemetry?.output ?? returned?.data ?? finalOutput ?? finalText
|
|
3452
3742
|
}
|
|
3453
3743
|
});
|
|
3454
|
-
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3744
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3745
|
+
status: "completed",
|
|
3746
|
+
result,
|
|
3747
|
+
resolvedInterrupts
|
|
3748
|
+
};
|
|
3455
3749
|
ctx.stateDirty = true;
|
|
3456
3750
|
return result;
|
|
3457
3751
|
} catch (error) {
|
|
@@ -3528,6 +3822,8 @@ async function runReason(args) {
|
|
|
3528
3822
|
const opId = checkpointValue && typeof checkpointValue === "object" && "status" in checkpointValue && checkpointValue.status === "tool_loop" && "opId" in checkpointValue && typeof checkpointValue.opId === "string" ? checkpointValue.opId : provisionalOpId;
|
|
3529
3823
|
const existingCompleted = readReasonCompletedCheckpoint(checkpointValue);
|
|
3530
3824
|
if (existingCompleted) {
|
|
3825
|
+
for (const request of existingCompleted.resolvedInterrupts ?? [])
|
|
3826
|
+
ctx.node.replayInterrupt?.(request);
|
|
3531
3827
|
await replayToolObservations(
|
|
3532
3828
|
existingCompleted.result.steps?.flatMap(
|
|
3533
3829
|
(step) => step.toolObservations ?? []
|