@kortyx/hooks 0.34.2 → 0.35.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 +30 -0
- package/README.md +2 -0
- package/dist/index.js +156 -330
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -6
- package/dist/internal.js +5 -240
- package/dist/internal.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -245,7 +245,7 @@ var import_node = require("@kortyx/usage-telemetry/node");
|
|
|
245
245
|
// package.json
|
|
246
246
|
var package_default = {
|
|
247
247
|
name: "@kortyx/hooks",
|
|
248
|
-
version: "0.
|
|
248
|
+
version: "0.35.0",
|
|
249
249
|
private: false,
|
|
250
250
|
description: "Lifecycle hooks and extension points for Kortyx.",
|
|
251
251
|
keywords: [
|
|
@@ -822,252 +822,17 @@ var emitStructuredData = (args) => {
|
|
|
822
822
|
var import_node_async_hooks2 = require("async_hooks");
|
|
823
823
|
var import_node_crypto3 = require("crypto");
|
|
824
824
|
var import_core2 = require("@kortyx/core");
|
|
825
|
-
var
|
|
825
|
+
var import_errors2 = require("@kortyx/core/errors");
|
|
826
826
|
|
|
827
827
|
// src/error-diagnostics.ts
|
|
828
828
|
var import_telemetry_contracts2 = require("@kortyx/telemetry-contracts");
|
|
829
829
|
|
|
830
830
|
// src/diagnostic-capture.ts
|
|
831
|
-
var import_errors2 = require("@kortyx/core/errors");
|
|
832
831
|
var import_telemetry_contracts = require("@kortyx/telemetry-contracts");
|
|
833
|
-
var encoder = new TextEncoder();
|
|
834
|
-
var nativeStackGetter = Object.getOwnPropertyDescriptor(
|
|
835
|
-
new Error(),
|
|
836
|
-
"stack"
|
|
837
|
-
)?.get;
|
|
838
|
-
function nativeStack(object, getter) {
|
|
839
|
-
if (!getter || getter !== nativeStackGetter)
|
|
840
|
-
throw new Error("Custom stack accessor");
|
|
841
|
-
for (const key of ["name", "message"]) {
|
|
842
|
-
let cursor = object;
|
|
843
|
-
while (cursor) {
|
|
844
|
-
const descriptor = Object.getOwnPropertyDescriptor(cursor, key);
|
|
845
|
-
if (descriptor) {
|
|
846
|
-
if (!("value" in descriptor) || typeof descriptor.value !== "string")
|
|
847
|
-
throw new Error("Custom error formatting");
|
|
848
|
-
break;
|
|
849
|
-
}
|
|
850
|
-
cursor = Object.getPrototypeOf(cursor);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
const formatter = Object.getOwnPropertyDescriptor(Error, "prepareStackTrace");
|
|
854
|
-
if (formatter && (!("value" in formatter) || !formatter.writable))
|
|
855
|
-
throw new Error("Custom stack formatter");
|
|
856
|
-
Object.defineProperty(Error, "prepareStackTrace", {
|
|
857
|
-
...formatter ?? { configurable: true, writable: true },
|
|
858
|
-
value: void 0
|
|
859
|
-
});
|
|
860
|
-
try {
|
|
861
|
-
return getter.call(object);
|
|
862
|
-
} finally {
|
|
863
|
-
if (formatter) Object.defineProperty(Error, "prepareStackTrace", formatter);
|
|
864
|
-
else Reflect.deleteProperty(Error, "prepareStackTrace");
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
var pointer = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
868
|
-
function captureDiagnosticContent(error, project) {
|
|
869
|
-
const omissions = [];
|
|
870
|
-
const redactions = [];
|
|
871
|
-
const seen = /* @__PURE__ */ new WeakMap();
|
|
872
|
-
let nodes = 0;
|
|
873
|
-
let remaining = import_telemetry_contracts.DIAGNOSTIC_LIMITS.bytes - 64 * 1024;
|
|
874
|
-
const omit = (path, reason, extra = {}) => {
|
|
875
|
-
if (omissions.length < 1e3)
|
|
876
|
-
omissions.push({ path: path.slice(0, 4096), reason, ...extra });
|
|
877
|
-
return { $omitted: reason };
|
|
878
|
-
};
|
|
879
|
-
const visit = (value, path, depth) => {
|
|
880
|
-
if (++nodes > import_telemetry_contracts.DIAGNOSTIC_LIMITS.nodes) return omit(path, "node_limit");
|
|
881
|
-
if (depth > import_telemetry_contracts.DIAGNOSTIC_LIMITS.depth) return omit(path, "depth_limit");
|
|
882
|
-
if (typeof value === "string") {
|
|
883
|
-
const clean = (0, import_telemetry_contracts.redactDiagnosticText)(value);
|
|
884
|
-
if (clean !== value && redactions.length < 1e3)
|
|
885
|
-
redactions.push({
|
|
886
|
-
path: path.slice(0, 4096),
|
|
887
|
-
reason: "credential_text"
|
|
888
|
-
});
|
|
889
|
-
const bytes = encoder.encode(JSON.stringify(clean)).length;
|
|
890
|
-
if (bytes > remaining)
|
|
891
|
-
return omit(path, "byte_limit", {
|
|
892
|
-
originalBytes: encoder.encode(value).length
|
|
893
|
-
});
|
|
894
|
-
remaining -= bytes;
|
|
895
|
-
return clean;
|
|
896
|
-
}
|
|
897
|
-
if (value === null || typeof value === "boolean") return value;
|
|
898
|
-
if (typeof value === "number")
|
|
899
|
-
return Number.isFinite(value) ? value : { $type: "number", value: String(value) };
|
|
900
|
-
if (typeof value === "bigint")
|
|
901
|
-
return { $type: "bigint", value: value.toString() };
|
|
902
|
-
if (typeof value === "undefined") return { $type: "undefined" };
|
|
903
|
-
if (typeof value === "function" || typeof value === "symbol")
|
|
904
|
-
return omit(path, `unsupported_${typeof value}`);
|
|
905
|
-
const object = value;
|
|
906
|
-
const reference = seen.get(object);
|
|
907
|
-
if (reference) return { $ref: reference };
|
|
908
|
-
seen.set(object, path);
|
|
909
|
-
remaining -= 32;
|
|
910
|
-
if (remaining < 0) return omit(path, "byte_limit");
|
|
911
|
-
try {
|
|
912
|
-
if (value instanceof Date)
|
|
913
|
-
return { $type: "Date", value: Date.prototype.toISOString.call(value) };
|
|
914
|
-
if (value instanceof Map || value instanceof Set) {
|
|
915
|
-
const isMap = value instanceof Map;
|
|
916
|
-
const iterator = isMap ? Map.prototype.entries.call(value) : Set.prototype.values.call(value);
|
|
917
|
-
const count = Object.getOwnPropertyDescriptor(
|
|
918
|
-
isMap ? Map.prototype : Set.prototype,
|
|
919
|
-
"size"
|
|
920
|
-
)?.get?.call(value);
|
|
921
|
-
const entries = [];
|
|
922
|
-
for (let index = 0; index < Math.min(count, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties); index++)
|
|
923
|
-
entries.push(iterator.next().value);
|
|
924
|
-
if (count > entries.length)
|
|
925
|
-
omissions.push({
|
|
926
|
-
path,
|
|
927
|
-
reason: "collection_limit",
|
|
928
|
-
originalCount: count
|
|
929
|
-
});
|
|
930
|
-
return isMap ? {
|
|
931
|
-
$type: "Map",
|
|
932
|
-
entries: visit(entries, `${path}/entries`, depth + 1)
|
|
933
|
-
} : {
|
|
934
|
-
$type: "Set",
|
|
935
|
-
values: visit(entries, `${path}/values`, depth + 1)
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
if (Array.isArray(value)) {
|
|
939
|
-
const count = Math.min(value.length, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties);
|
|
940
|
-
const items = Array.from(
|
|
941
|
-
{ length: count },
|
|
942
|
-
(_, index) => read(value, String(index), `${path}/${index}`, depth)
|
|
943
|
-
);
|
|
944
|
-
if (count < value.length)
|
|
945
|
-
items.push(
|
|
946
|
-
omit(path, "collection_limit", { originalCount: value.length })
|
|
947
|
-
);
|
|
948
|
-
return items;
|
|
949
|
-
}
|
|
950
|
-
const keys = Object.getOwnPropertyNames(object);
|
|
951
|
-
const result = /* @__PURE__ */ Object.create(null);
|
|
952
|
-
if (value instanceof Error) {
|
|
953
|
-
const ownName = Object.getOwnPropertyDescriptor(object, "name");
|
|
954
|
-
let type = ownName && "value" in ownName ? ownName.value : void 0;
|
|
955
|
-
if (typeof type !== "string") {
|
|
956
|
-
const name = Object.getOwnPropertyDescriptor(
|
|
957
|
-
Object.getPrototypeOf(value),
|
|
958
|
-
"name"
|
|
959
|
-
);
|
|
960
|
-
type = name && "value" in name ? name.value : "Error";
|
|
961
|
-
}
|
|
962
|
-
result.type = visit(
|
|
963
|
-
typeof type === "string" ? type : "Error",
|
|
964
|
-
`${path}/type`,
|
|
965
|
-
depth + 1
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
for (const key of keys.slice(0, import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties)) {
|
|
969
|
-
if (["__proto__", "constructor", "prototype"].includes(key)) {
|
|
970
|
-
result[key] = omit(`${path}/${pointer(key)}`, "unsafe_key");
|
|
971
|
-
continue;
|
|
972
|
-
}
|
|
973
|
-
remaining -= encoder.encode(key).length + 4;
|
|
974
|
-
result[key] = read(object, key, `${path}/${pointer(key)}`, depth);
|
|
975
|
-
}
|
|
976
|
-
if (keys.length > import_telemetry_contracts.DIAGNOSTIC_LIMITS.properties)
|
|
977
|
-
result.$omittedProperties = omit(path, "property_limit", {
|
|
978
|
-
originalCount: keys.length
|
|
979
|
-
});
|
|
980
|
-
return result;
|
|
981
|
-
} catch {
|
|
982
|
-
return omit(path, "unreadable_object");
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
const read = (object, key, path, depth) => {
|
|
986
|
-
if ((0, import_telemetry_contracts.isPrivateDiagnosticField)(key)) {
|
|
987
|
-
if (redactions.length < 1e3)
|
|
988
|
-
redactions.push({
|
|
989
|
-
path: path.slice(0, 4096),
|
|
990
|
-
reason: "credential_field"
|
|
991
|
-
});
|
|
992
|
-
return "[REDACTED]";
|
|
993
|
-
}
|
|
994
|
-
try {
|
|
995
|
-
const property = Object.getOwnPropertyDescriptor(object, key);
|
|
996
|
-
if (property && "value" in property)
|
|
997
|
-
return visit(property.value, path, depth + 1);
|
|
998
|
-
if (key === "stack" && object instanceof Error && property?.get && property.get === nativeStackGetter)
|
|
999
|
-
return visit(nativeStack(object, property.get), path, depth + 1);
|
|
1000
|
-
return omit(
|
|
1001
|
-
path,
|
|
1002
|
-
property ? "accessor_not_evaluated" : "unavailable_property"
|
|
1003
|
-
);
|
|
1004
|
-
} catch {
|
|
1005
|
-
return omit(path, "unreadable_property");
|
|
1006
|
-
}
|
|
1007
|
-
};
|
|
1008
|
-
try {
|
|
1009
|
-
const original = project ? project(error) : error;
|
|
1010
|
-
if (project && original === null) return null;
|
|
1011
|
-
const captured = visit(original, "#/data", 0);
|
|
1012
|
-
const upstreamOmissions = (0, import_errors2.errorProperty)(original, "diagnosticOmissions");
|
|
1013
|
-
if (Array.isArray(upstreamOmissions))
|
|
1014
|
-
for (const note of upstreamOmissions.slice(0, 1e3)) {
|
|
1015
|
-
const path = (0, import_errors2.errorProperty)(note, "path");
|
|
1016
|
-
const reason = (0, import_errors2.errorProperty)(note, "reason");
|
|
1017
|
-
if (typeof path === "string" && typeof reason === "string")
|
|
1018
|
-
omissions.push({
|
|
1019
|
-
path: (0, import_telemetry_contracts.redactDiagnosticText)(path).slice(0, 4096),
|
|
1020
|
-
reason: (0, import_telemetry_contracts.redactDiagnosticText)(reason).slice(0, 128)
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
const data = captured && typeof captured === "object" && !Array.isArray(captured) ? captured : {
|
|
1024
|
-
type: "Error",
|
|
1025
|
-
message: typeof captured === "string" ? captured : "Execution reported a fault.",
|
|
1026
|
-
value: captured
|
|
1027
|
-
};
|
|
1028
|
-
if (typeof data.message !== "string" && !("message" in data))
|
|
1029
|
-
data.message = "Execution reported a fault.";
|
|
1030
|
-
if (!("type" in data))
|
|
1031
|
-
data.type = typeof data.name === "string" ? data.name : "Error";
|
|
1032
|
-
const content = {
|
|
1033
|
-
schemaVersion: 1,
|
|
1034
|
-
data,
|
|
1035
|
-
capture: {
|
|
1036
|
-
status: omissions.length ? "partial" : "complete",
|
|
1037
|
-
omissions,
|
|
1038
|
-
redactions
|
|
1039
|
-
}
|
|
1040
|
-
};
|
|
1041
|
-
if (encoder.encode(JSON.stringify(content)).length > import_telemetry_contracts.DIAGNOSTIC_LIMITS.bytes)
|
|
1042
|
-
return {
|
|
1043
|
-
schemaVersion: 1,
|
|
1044
|
-
data: {
|
|
1045
|
-
type: "Error",
|
|
1046
|
-
message: "Diagnostic exceeded the capture budget."
|
|
1047
|
-
},
|
|
1048
|
-
capture: {
|
|
1049
|
-
status: "partial",
|
|
1050
|
-
redactions: [],
|
|
1051
|
-
omissions: [{ path: "#/data", reason: "serialized_byte_limit" }]
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
return content;
|
|
1055
|
-
} catch {
|
|
1056
|
-
return {
|
|
1057
|
-
schemaVersion: 1,
|
|
1058
|
-
data: { type: "Error", message: "Diagnostic capture failed." },
|
|
1059
|
-
capture: {
|
|
1060
|
-
status: "failed",
|
|
1061
|
-
redactions: [],
|
|
1062
|
-
omissions: [{ path: "#/data", reason: "projection_or_capture_failed" }]
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
832
|
|
|
1068
833
|
// src/error-diagnostics.ts
|
|
1069
834
|
function exceptionDiagnostics(error, project) {
|
|
1070
|
-
const content = captureDiagnosticContent(error, project);
|
|
835
|
+
const content = (0, import_telemetry_contracts.captureDiagnosticContent)(error, project);
|
|
1071
836
|
return !content || content.capture.status === "failed" ? null : content.data;
|
|
1072
837
|
}
|
|
1073
838
|
function errorDiagnostics(error, project) {
|
|
@@ -1332,7 +1097,7 @@ async function executeObservedTool(args) {
|
|
|
1332
1097
|
return result;
|
|
1333
1098
|
} catch (error) {
|
|
1334
1099
|
if (args.abortSignal?.aborted) observation.outcome = "cancelled";
|
|
1335
|
-
else if (!(0,
|
|
1100
|
+
else if (!(0, import_errors2.isControlFlowError)(error)) {
|
|
1336
1101
|
const outcome = classification(
|
|
1337
1102
|
args.tool,
|
|
1338
1103
|
error,
|
|
@@ -1471,11 +1236,11 @@ var import_prompts = require("@kortyx/prompts");
|
|
|
1471
1236
|
// src/reason-engine.ts
|
|
1472
1237
|
var import_node_crypto4 = require("crypto");
|
|
1473
1238
|
var import_core3 = require("@kortyx/core");
|
|
1474
|
-
var
|
|
1239
|
+
var import_errors3 = require("@kortyx/core/errors");
|
|
1475
1240
|
function assertModelFinish(result) {
|
|
1476
1241
|
if (result.finishReason?.unified !== "content-filter") return;
|
|
1477
1242
|
throw Object.assign(
|
|
1478
|
-
new
|
|
1243
|
+
new import_errors3.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
|
|
1479
1244
|
category: "provider",
|
|
1480
1245
|
retryable: false,
|
|
1481
1246
|
safeMessage: "The provider refused the request."
|
|
@@ -2549,7 +2314,7 @@ function createStructuredOutputStreamer(args, id, opId) {
|
|
|
2549
2314
|
}
|
|
2550
2315
|
|
|
2551
2316
|
// src/reason/parsing.ts
|
|
2552
|
-
var
|
|
2317
|
+
var import_errors4 = require("@kortyx/core/errors");
|
|
2553
2318
|
var tryJsonParse = (value) => {
|
|
2554
2319
|
try {
|
|
2555
2320
|
return { ok: true, value: JSON.parse(value) };
|
|
@@ -2587,12 +2352,12 @@ var resolveOutputCandidate = (text) => {
|
|
|
2587
2352
|
}
|
|
2588
2353
|
return text;
|
|
2589
2354
|
};
|
|
2590
|
-
var createTruncatedStructuredOutputError = (label, cause) => new
|
|
2355
|
+
var createTruncatedStructuredOutputError = (label, cause) => new import_errors4.ValidationError(
|
|
2591
2356
|
"OUTPUT_TRUNCATED",
|
|
2592
2357
|
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`,
|
|
2593
2358
|
cause
|
|
2594
2359
|
);
|
|
2595
|
-
var createInvalidStructuredOutputError = (label, cause) => new
|
|
2360
|
+
var createInvalidStructuredOutputError = (label, cause) => new import_errors4.ValidationError(
|
|
2596
2361
|
"INVALID_MODEL_JSON",
|
|
2597
2362
|
`${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.`,
|
|
2598
2363
|
cause
|
|
@@ -2604,7 +2369,7 @@ var parseReasonOutputWithSchema = (args) => {
|
|
|
2604
2369
|
try {
|
|
2605
2370
|
return parseWithSchema(args.schema, candidate, args.label);
|
|
2606
2371
|
} catch (error) {
|
|
2607
|
-
if (!(error instanceof
|
|
2372
|
+
if (!(error instanceof import_errors4.ValidationError)) throw error;
|
|
2608
2373
|
const failure = args.finishReason?.unified === "length" ? createTruncatedStructuredOutputError(args.label, error) : typeof candidate === "string" && !validJson ? createInvalidStructuredOutputError(args.label, error) : error;
|
|
2609
2374
|
if (failure && typeof failure === "object")
|
|
2610
2375
|
Object.assign(failure, {
|
|
@@ -2624,7 +2389,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2624
2389
|
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
2625
2390
|
throw createInvalidStructuredOutputError("useReason output");
|
|
2626
2391
|
}
|
|
2627
|
-
throw new
|
|
2392
|
+
throw new import_errors4.ValidationError(
|
|
2628
2393
|
"MODEL_OUTPUT_SCHEMA",
|
|
2629
2394
|
"useReason first pass with interrupt must return a JSON object."
|
|
2630
2395
|
);
|
|
@@ -2633,7 +2398,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2633
2398
|
let interruptRequired = true;
|
|
2634
2399
|
if (mode === "optional") {
|
|
2635
2400
|
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
2636
|
-
throw new
|
|
2401
|
+
throw new import_errors4.ValidationError(
|
|
2637
2402
|
"MODEL_OUTPUT_SCHEMA",
|
|
2638
2403
|
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
2639
2404
|
);
|
|
@@ -2646,7 +2411,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2646
2411
|
candidate.interrupt
|
|
2647
2412
|
].some((value) => value !== null && value !== void 0);
|
|
2648
2413
|
if (hasUnexpectedRequest) {
|
|
2649
|
-
throw new
|
|
2414
|
+
throw new import_errors4.ValidationError(
|
|
2650
2415
|
"MODEL_OUTPUT_SCHEMA",
|
|
2651
2416
|
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
2652
2417
|
);
|
|
@@ -2680,7 +2445,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2680
2445
|
"useReason output"
|
|
2681
2446
|
);
|
|
2682
2447
|
} catch (error) {
|
|
2683
|
-
if (!(error instanceof
|
|
2448
|
+
if (!(error instanceof import_errors4.ValidationError)) throw error;
|
|
2684
2449
|
if (args.finishReason?.unified === "length") {
|
|
2685
2450
|
throw createTruncatedStructuredOutputError("useReason output", error);
|
|
2686
2451
|
}
|
|
@@ -2689,7 +2454,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2689
2454
|
}
|
|
2690
2455
|
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
2691
2456
|
if (!args.outputSchema && !explicitDraft) {
|
|
2692
|
-
throw new
|
|
2457
|
+
throw new import_errors4.ValidationError(
|
|
2693
2458
|
"MODEL_OUTPUT_SCHEMA",
|
|
2694
2459
|
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
2695
2460
|
);
|
|
@@ -2963,8 +2728,42 @@ var emitReasonStructuredOutput = (args) => {
|
|
|
2963
2728
|
|
|
2964
2729
|
// src/reason/tools.ts
|
|
2965
2730
|
var import_core5 = require("@kortyx/core");
|
|
2966
|
-
var
|
|
2731
|
+
var import_errors5 = require("@kortyx/core/errors");
|
|
2967
2732
|
var import_zod5 = require("zod");
|
|
2733
|
+
|
|
2734
|
+
// src/reason/input-feedback.ts
|
|
2735
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2736
|
+
function schemaPath(schema, path) {
|
|
2737
|
+
let current = schema;
|
|
2738
|
+
let label = "";
|
|
2739
|
+
for (const part of path.slice(0, 8)) {
|
|
2740
|
+
if (!record(current)) break;
|
|
2741
|
+
if (typeof part === "number" && Number.isSafeInteger(part) && part >= 0) {
|
|
2742
|
+
if (!record(current.items)) break;
|
|
2743
|
+
current = current.items;
|
|
2744
|
+
label += `[${part}]`;
|
|
2745
|
+
} else if (typeof part === "string" && record(current.properties) && Object.hasOwn(current.properties, part)) {
|
|
2746
|
+
current = current.properties[part];
|
|
2747
|
+
label += `${label ? "." : ""}${part.slice(0, 64)}`;
|
|
2748
|
+
} else break;
|
|
2749
|
+
}
|
|
2750
|
+
return label || "input";
|
|
2751
|
+
}
|
|
2752
|
+
function interruptInputFeedback(error, schema, name, required) {
|
|
2753
|
+
const issues = Array.isArray(error.issues) ? error.issues : [];
|
|
2754
|
+
const problems = issues.slice(0, 5).map((issue) => {
|
|
2755
|
+
const path = record(issue) && Array.isArray(issue.path) ? issue.path : [];
|
|
2756
|
+
return `- ${schemaPath(schema, path)} must satisfy its declared input schema.`;
|
|
2757
|
+
});
|
|
2758
|
+
return [
|
|
2759
|
+
`Invalid input for ${name}:`,
|
|
2760
|
+
...problems,
|
|
2761
|
+
...issues.length > 5 ? ["- Additional input fields are invalid."] : [],
|
|
2762
|
+
required ? "Correct the input and call the tool again. Human input is required before finishing." : "Correct the input and call the tool again, or finish without requesting human input when none is needed."
|
|
2763
|
+
].join("\n");
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
// src/reason/tools.ts
|
|
2968
2767
|
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
2969
2768
|
name: tool.name,
|
|
2970
2769
|
...tool.title ? { title: tool.title } : {},
|
|
@@ -3169,7 +2968,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3169
2968
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
3170
2969
|
const missingReturnCorrections = useReasonArgs.toolExecution?.missingReturnCorrections ?? 0;
|
|
3171
2970
|
if (!Number.isSafeInteger(missingReturnCorrections) || missingReturnCorrections < 0)
|
|
3172
|
-
throw new
|
|
2971
|
+
throw new import_errors5.KortyxError(
|
|
3173
2972
|
"INVALID_REASON_CORRECTION_POLICY",
|
|
3174
2973
|
"Invalid missing return correction budget.",
|
|
3175
2974
|
{
|
|
@@ -3230,7 +3029,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3230
3029
|
let outputContractCorrections = checkpoint?.outputContractCorrections ?? 0;
|
|
3231
3030
|
const correctionExhausted = () => {
|
|
3232
3031
|
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.`;
|
|
3233
|
-
return new
|
|
3032
|
+
return new import_errors5.KortyxError(
|
|
3234
3033
|
"REASON_OUTPUT_CONTRACT_CORRECTION_EXHAUSTED",
|
|
3235
3034
|
message,
|
|
3236
3035
|
{
|
|
@@ -3327,7 +3126,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3327
3126
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
3328
3127
|
const toolCalls = step.toolCalls ?? [];
|
|
3329
3128
|
if (missingReturnAttempts > 0 && toolCalls.some((call) => !outputByToolName.has(call.name)))
|
|
3330
|
-
throw new
|
|
3129
|
+
throw new import_errors5.KortyxError(
|
|
3331
3130
|
"REASON_OUTPUT_CORRECTION_TOOL_DISALLOWED",
|
|
3332
3131
|
"A return correction requested a non-output tool.",
|
|
3333
3132
|
{
|
|
@@ -3370,7 +3169,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3370
3169
|
save();
|
|
3371
3170
|
continue;
|
|
3372
3171
|
}
|
|
3373
|
-
throw new
|
|
3172
|
+
throw new import_errors5.KortyxError(
|
|
3374
3173
|
"REASON_TERMINAL_OUTPUT_MISSING",
|
|
3375
3174
|
"useReason outputs.return requires one terminal output contract call.",
|
|
3376
3175
|
{
|
|
@@ -3504,7 +3303,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3504
3303
|
);
|
|
3505
3304
|
if (steps.length >= maxSteps) {
|
|
3506
3305
|
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.`;
|
|
3507
|
-
throw new
|
|
3306
|
+
throw new import_errors5.ProviderRequestError(message, {
|
|
3508
3307
|
code: "REASON_OUTPUT_BUDGET_EXHAUSTED",
|
|
3509
3308
|
operation: "useReason",
|
|
3510
3309
|
safeMessage: message,
|
|
@@ -3691,11 +3490,35 @@ var runReasonToolLoop = async (args) => {
|
|
|
3691
3490
|
throw new Error(
|
|
3692
3491
|
`useReason reached interrupts.maxRequests (${interruptConfig?.maxRequests ?? 1}).`
|
|
3693
3492
|
);
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3493
|
+
let request;
|
|
3494
|
+
try {
|
|
3495
|
+
request = parseWithSchema(
|
|
3496
|
+
resolved.contract.requestSchema,
|
|
3497
|
+
toolCall.input,
|
|
3498
|
+
`useReason interrupt contract "${resolved.name}" request`
|
|
3499
|
+
);
|
|
3500
|
+
} catch (error) {
|
|
3501
|
+
if (!(error instanceof import_errors5.ValidationError)) throw error;
|
|
3502
|
+
const result3 = {
|
|
3503
|
+
toolCallId: toolCall.id,
|
|
3504
|
+
name: toolCall.name,
|
|
3505
|
+
isError: true,
|
|
3506
|
+
content: interruptInputFeedback(
|
|
3507
|
+
error,
|
|
3508
|
+
interruptDefinitions.find((tool2) => tool2.name === toolCall.name)?.inputSchema,
|
|
3509
|
+
toolCall.name,
|
|
3510
|
+
interruptConfig?.mode === "required"
|
|
3511
|
+
)
|
|
3512
|
+
};
|
|
3513
|
+
toolResults.push(result3);
|
|
3514
|
+
messages.push({ role: "tool", ...result3 });
|
|
3515
|
+
traceSpan?.addEvent?.("useReason.interrupt.invalid", {
|
|
3516
|
+
checkpointKey,
|
|
3517
|
+
contract: resolved.name
|
|
3518
|
+
});
|
|
3519
|
+
save();
|
|
3520
|
+
continue;
|
|
3521
|
+
}
|
|
3699
3522
|
traceSpan?.addEvent?.("useReason.interrupt.requested", {
|
|
3700
3523
|
checkpointKey,
|
|
3701
3524
|
contract: resolved.name,
|
|
@@ -3885,12 +3708,12 @@ var runReasonToolLoop = async (args) => {
|
|
|
3885
3708
|
}
|
|
3886
3709
|
} catch (error) {
|
|
3887
3710
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
3888
|
-
if ((0,
|
|
3711
|
+
if ((0, import_errors5.isControlFlowError)(error)) throw error;
|
|
3889
3712
|
const result2 = {
|
|
3890
3713
|
toolCallId: toolCall.id,
|
|
3891
3714
|
name: tool.name,
|
|
3892
|
-
content: (0,
|
|
3893
|
-
failure: (0,
|
|
3715
|
+
content: (0, import_errors5.serializeFailure)(error).message,
|
|
3716
|
+
failure: (0, import_errors5.serializeFailure)(error),
|
|
3894
3717
|
isError: true
|
|
3895
3718
|
};
|
|
3896
3719
|
toolResults.push(result2);
|
|
@@ -4493,7 +4316,7 @@ function useWorkflowState(key, ...rest) {
|
|
|
4493
4316
|
|
|
4494
4317
|
// src/parallel.ts
|
|
4495
4318
|
var import_core7 = require("@kortyx/core");
|
|
4496
|
-
var
|
|
4319
|
+
var import_errors6 = require("@kortyx/core/errors");
|
|
4497
4320
|
var ParallelChildWaiting = class extends Error {
|
|
4498
4321
|
name = "ParallelChildWaiting";
|
|
4499
4322
|
};
|
|
@@ -4507,14 +4330,14 @@ var ParallelError = class extends AggregateError {
|
|
|
4507
4330
|
);
|
|
4508
4331
|
this.results = results;
|
|
4509
4332
|
this.name = "ParallelError";
|
|
4510
|
-
this.failure = (0,
|
|
4333
|
+
this.failure = (0, import_errors6.serializeFailure)({
|
|
4511
4334
|
version: 1,
|
|
4512
4335
|
code: this.code,
|
|
4513
4336
|
category: "workflow",
|
|
4514
4337
|
message: this.message,
|
|
4515
4338
|
retryable: null,
|
|
4516
4339
|
results: results.map(
|
|
4517
|
-
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0,
|
|
4340
|
+
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors6.serializeFailure)(result.reason) }
|
|
4518
4341
|
)
|
|
4519
4342
|
});
|
|
4520
4343
|
}
|
|
@@ -4522,7 +4345,7 @@ var ParallelError = class extends AggregateError {
|
|
|
4522
4345
|
code = "PARALLEL_FAILED";
|
|
4523
4346
|
failure;
|
|
4524
4347
|
toJSON() {
|
|
4525
|
-
return (0,
|
|
4348
|
+
return (0, import_errors6.serializeFailure)(this);
|
|
4526
4349
|
}
|
|
4527
4350
|
};
|
|
4528
4351
|
function registerWorkflowTask(id) {
|
|
@@ -4635,10 +4458,10 @@ function parallel(values) {
|
|
|
4635
4458
|
throw new Error("Parallel call membership changed during replay.");
|
|
4636
4459
|
}
|
|
4637
4460
|
for (const callId of group.record.calls) {
|
|
4638
|
-
const
|
|
4639
|
-
if (
|
|
4640
|
-
const index =
|
|
4641
|
-
if (
|
|
4461
|
+
const record2 = callRecord(callId);
|
|
4462
|
+
if (record2?.status !== "interrupted") continue;
|
|
4463
|
+
const index = record2.interrupts.length - 1;
|
|
4464
|
+
if (record2.interrupts[index]?.response !== void 0) continue;
|
|
4642
4465
|
group.record.interrupts.push({ callId, index });
|
|
4643
4466
|
await bridge(callId, index);
|
|
4644
4467
|
throw new Error(
|
|
@@ -4703,11 +4526,11 @@ async function usePrompt(reference, options) {
|
|
|
4703
4526
|
// src/workflow.ts
|
|
4704
4527
|
var import_node_crypto6 = require("crypto");
|
|
4705
4528
|
var import_core8 = require("@kortyx/core");
|
|
4706
|
-
var
|
|
4707
|
-
var WorkflowCallError = class extends
|
|
4529
|
+
var import_errors7 = require("@kortyx/core/errors");
|
|
4530
|
+
var WorkflowCallError = class extends import_errors7.KortyxError {
|
|
4708
4531
|
constructor(workflow, message, cause, failure) {
|
|
4709
|
-
const detail = (0,
|
|
4710
|
-
failure ?? cause ?? new
|
|
4532
|
+
const detail = (0, import_errors7.serializeFailure)(
|
|
4533
|
+
failure ?? cause ?? new import_errors7.WorkflowContractError(message)
|
|
4711
4534
|
);
|
|
4712
4535
|
super(detail.code, `Workflow '${workflow}': ${message}`, {
|
|
4713
4536
|
...detail,
|
|
@@ -4728,12 +4551,12 @@ function workflowCallFingerprint(value) {
|
|
|
4728
4551
|
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
4729
4552
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
4730
4553
|
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
4731
|
-
throw new
|
|
4554
|
+
throw new import_errors7.WorkflowContractError(
|
|
4732
4555
|
"Workflow input and output must be JSON serializable."
|
|
4733
4556
|
);
|
|
4734
4557
|
}
|
|
4735
4558
|
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
4736
|
-
throw new
|
|
4559
|
+
throw new import_errors7.WorkflowContractError(
|
|
4737
4560
|
"Workflow input and output must contain plain JSON objects."
|
|
4738
4561
|
);
|
|
4739
4562
|
}
|
|
@@ -4754,7 +4577,7 @@ function createWorkflowHooks(workflows) {
|
|
|
4754
4577
|
useWorkflow: (args) => {
|
|
4755
4578
|
const workflow = workflows[args.workflow];
|
|
4756
4579
|
if (!workflow || workflow.id !== args.workflow)
|
|
4757
|
-
throw new
|
|
4580
|
+
throw new import_errors7.WorkflowContractError(
|
|
4758
4581
|
"Workflow registry keys must match definition ids."
|
|
4759
4582
|
);
|
|
4760
4583
|
return useWorkflow({ ...args, workflow });
|
|
@@ -4780,23 +4603,23 @@ async function runWorkflowCall(args, task) {
|
|
|
4780
4603
|
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
4781
4604
|
const service = ctx.node.callWorkflow;
|
|
4782
4605
|
if (!service)
|
|
4783
|
-
throw new
|
|
4606
|
+
throw new import_errors7.WorkflowContractError(
|
|
4784
4607
|
"useWorkflow requires an agent workflow registry."
|
|
4785
4608
|
);
|
|
4786
4609
|
if (!args.id.trim())
|
|
4787
|
-
throw new
|
|
4610
|
+
throw new import_errors7.WorkflowContractError(
|
|
4788
4611
|
"useWorkflow requires a stable, nonempty id."
|
|
4789
4612
|
);
|
|
4790
4613
|
if (ctx.workflowCallActive && !task.group)
|
|
4791
|
-
throw new
|
|
4614
|
+
throw new import_errors7.WorkflowContractError(
|
|
4792
4615
|
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
4793
4616
|
);
|
|
4794
4617
|
if (ctx.workflowCallIds.has(args.id))
|
|
4795
|
-
throw new
|
|
4618
|
+
throw new import_errors7.WorkflowContractError(
|
|
4796
4619
|
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
4797
4620
|
);
|
|
4798
4621
|
if (ctx.workflowCallIds.size >= 64)
|
|
4799
|
-
throw new
|
|
4622
|
+
throw new import_errors7.WorkflowContractError(
|
|
4800
4623
|
"A node may call at most 64 child workflows per activation."
|
|
4801
4624
|
);
|
|
4802
4625
|
ctx.workflowCallIds.add(args.id);
|
|
@@ -4809,26 +4632,26 @@ async function runWorkflowCall(args, task) {
|
|
|
4809
4632
|
version: definition?.version ?? null
|
|
4810
4633
|
});
|
|
4811
4634
|
const key = `__useWorkflow:${args.id}`;
|
|
4812
|
-
let
|
|
4813
|
-
if (
|
|
4635
|
+
let record2 = ctx.currentNodeState.byKey[key];
|
|
4636
|
+
if (record2 && record2.parallelKey !== task.group?.key)
|
|
4814
4637
|
throw new WorkflowCallError(
|
|
4815
4638
|
workflow,
|
|
4816
4639
|
"parallel call membership changed during replay"
|
|
4817
4640
|
);
|
|
4818
|
-
if (
|
|
4641
|
+
if (record2 && record2.fingerprint !== fingerprint)
|
|
4819
4642
|
throw new WorkflowCallError(
|
|
4820
4643
|
workflow,
|
|
4821
4644
|
"call input or target changed during replay"
|
|
4822
4645
|
);
|
|
4823
|
-
if (!
|
|
4824
|
-
|
|
4646
|
+
if (!record2) ctx.node.consumeExecution?.("maxChildInvocations");
|
|
4647
|
+
record2 ??= {
|
|
4825
4648
|
fingerprint,
|
|
4826
4649
|
invocationId: (0, import_node_crypto6.randomUUID)(),
|
|
4827
4650
|
status: "running",
|
|
4828
4651
|
interrupts: [],
|
|
4829
4652
|
...task.group ? { parallelKey: task.group.key } : {}
|
|
4830
4653
|
};
|
|
4831
|
-
ctx.currentNodeState.byKey[key] =
|
|
4654
|
+
ctx.currentNodeState.byKey[key] = record2;
|
|
4832
4655
|
ctx.stateDirty = true;
|
|
4833
4656
|
if (!task.group) ctx.workflowCallActive = true;
|
|
4834
4657
|
const telemetry = ctx.node.workflowCallTelemetry;
|
|
@@ -4836,7 +4659,7 @@ async function runWorkflowCall(args, task) {
|
|
|
4836
4659
|
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto6.randomUUID)();
|
|
4837
4660
|
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
4838
4661
|
const metadata = {
|
|
4839
|
-
invocationId:
|
|
4662
|
+
invocationId: record2.invocationId,
|
|
4840
4663
|
parentInvocationId: telemetry?.correlation?.invocationId ?? null,
|
|
4841
4664
|
branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
|
|
4842
4665
|
callId: args.id,
|
|
@@ -4846,46 +4669,49 @@ async function runWorkflowCall(args, task) {
|
|
|
4846
4669
|
targetWorkflowId: workflow,
|
|
4847
4670
|
targetVersion: definition?.version ?? null
|
|
4848
4671
|
};
|
|
4849
|
-
|
|
4672
|
+
record2.telemetry = metadata;
|
|
4850
4673
|
const report = (status, content = {}) => {
|
|
4851
|
-
|
|
4674
|
+
record2.sequence = (record2.sequence ?? 0) + 1;
|
|
4852
4675
|
emitWorkflowCall(telemetry, `workflow.call.${status}`, {
|
|
4853
4676
|
...metadata,
|
|
4854
|
-
sequence:
|
|
4677
|
+
sequence: record2.sequence,
|
|
4855
4678
|
...content
|
|
4856
4679
|
});
|
|
4857
4680
|
};
|
|
4858
4681
|
const recordUsage = (usage2) => {
|
|
4859
4682
|
if (!usage2) return;
|
|
4860
4683
|
accumulateTokenUsage({
|
|
4861
|
-
input: usage2.input - (
|
|
4862
|
-
output: usage2.output - (
|
|
4863
|
-
total: usage2.total - (
|
|
4684
|
+
input: usage2.input - (record2.usage?.input ?? 0),
|
|
4685
|
+
output: usage2.output - (record2.usage?.output ?? 0),
|
|
4686
|
+
total: usage2.total - (record2.usage?.total ?? 0)
|
|
4864
4687
|
});
|
|
4865
|
-
|
|
4688
|
+
record2.usage = usage2;
|
|
4866
4689
|
};
|
|
4867
|
-
const cached =
|
|
4690
|
+
const cached = record2.status === "completed";
|
|
4868
4691
|
try {
|
|
4869
|
-
for (const interruption of task.group ? [] :
|
|
4692
|
+
for (const interruption of task.group ? [] : record2.interrupts) {
|
|
4870
4693
|
interruption.response = await awaitInterruptInternal({
|
|
4871
4694
|
request: interruption.request,
|
|
4872
4695
|
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
4873
4696
|
});
|
|
4874
4697
|
}
|
|
4875
|
-
if (
|
|
4698
|
+
if (record2.status === "failed")
|
|
4876
4699
|
throw new WorkflowCallError(
|
|
4877
4700
|
workflow,
|
|
4878
|
-
|
|
4701
|
+
record2.error ?? "child failed",
|
|
4879
4702
|
void 0,
|
|
4880
|
-
|
|
4703
|
+
record2.failure ?? (0, import_errors7.serializeFailure)(void 0)
|
|
4881
4704
|
);
|
|
4882
|
-
if (task.group &&
|
|
4705
|
+
if (task.group && record2.status === "interrupted" && record2.interrupts.at(-1)?.response === void 0)
|
|
4883
4706
|
throw new ParallelChildWaiting();
|
|
4884
|
-
while (
|
|
4707
|
+
while (record2.status !== "completed") {
|
|
4885
4708
|
let outcome;
|
|
4886
4709
|
try {
|
|
4710
|
+
if (Array.isArray(record2.snapshot) && record2.snapshot.length !== 1)
|
|
4711
|
+
throw new import_errors7.WorkflowContractError("Invalid child workflow snapshot.");
|
|
4712
|
+
const snapshot = Array.isArray(record2.snapshot) ? record2.snapshot[0] : record2.snapshot;
|
|
4887
4713
|
report(
|
|
4888
|
-
|
|
4714
|
+
snapshot ? "resumed" : "started",
|
|
4889
4715
|
workflowCallContent(telemetry, "input", input)
|
|
4890
4716
|
);
|
|
4891
4717
|
outcome = await service({
|
|
@@ -4894,9 +4720,9 @@ async function runWorkflowCall(args, task) {
|
|
|
4894
4720
|
workflow,
|
|
4895
4721
|
input,
|
|
4896
4722
|
definition,
|
|
4897
|
-
invocationId:
|
|
4898
|
-
...
|
|
4899
|
-
...
|
|
4723
|
+
invocationId: record2.invocationId,
|
|
4724
|
+
...snapshot ? { snapshot } : {},
|
|
4725
|
+
...record2.interrupts.length ? { response: record2.interrupts.at(-1)?.response } : {}
|
|
4900
4726
|
});
|
|
4901
4727
|
} catch (error) {
|
|
4902
4728
|
const child = error;
|
|
@@ -4905,48 +4731,48 @@ async function runWorkflowCall(args, task) {
|
|
|
4905
4731
|
if ((0, import_core8.isExecutionCancelled)(error)) throw error;
|
|
4906
4732
|
if ((0, import_core8.isExecutionLimitReached)(error)) {
|
|
4907
4733
|
if (child?.__kortyxChildSnapshot)
|
|
4908
|
-
|
|
4734
|
+
record2.snapshot = child.__kortyxChildSnapshot;
|
|
4909
4735
|
report("suspended", {
|
|
4910
4736
|
reason: "limit_reached",
|
|
4911
4737
|
limit: error.limitReached
|
|
4912
4738
|
});
|
|
4913
4739
|
throw error;
|
|
4914
4740
|
}
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4741
|
+
record2.status = "failed";
|
|
4742
|
+
record2.failure = (0, import_errors7.serializeFailure)(error);
|
|
4743
|
+
record2.error = record2.failure.message;
|
|
4918
4744
|
report("failed", { error: "Child workflow failed" });
|
|
4919
4745
|
throw new WorkflowCallError(
|
|
4920
4746
|
workflow,
|
|
4921
|
-
|
|
4747
|
+
record2.error,
|
|
4922
4748
|
error,
|
|
4923
|
-
|
|
4749
|
+
record2.failure
|
|
4924
4750
|
);
|
|
4925
4751
|
}
|
|
4926
4752
|
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
4927
4753
|
recordUsage(outcome.usage);
|
|
4928
4754
|
if (outcome.status === "completed") {
|
|
4929
|
-
|
|
4930
|
-
workflowCallFingerprint(
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
for (const observation of
|
|
4755
|
+
record2.data = outcome.data;
|
|
4756
|
+
workflowCallFingerprint(record2.data);
|
|
4757
|
+
record2.status = "completed";
|
|
4758
|
+
record2.toolObservations = outcome.toolObservations ?? [];
|
|
4759
|
+
for (const observation of record2.toolObservations)
|
|
4934
4760
|
rememberToolObservation(observation);
|
|
4935
|
-
delete
|
|
4761
|
+
delete record2.snapshot;
|
|
4936
4762
|
report(
|
|
4937
4763
|
"completed",
|
|
4938
|
-
workflowCallContent(telemetry, "output",
|
|
4764
|
+
workflowCallContent(telemetry, "output", record2.data)
|
|
4939
4765
|
);
|
|
4940
4766
|
} else {
|
|
4941
|
-
|
|
4942
|
-
|
|
4767
|
+
record2.status = "interrupted";
|
|
4768
|
+
record2.snapshot = outcome.snapshot;
|
|
4943
4769
|
report("suspended", {
|
|
4944
4770
|
leaf: outcome.request.meta?.workflowCall ?? null
|
|
4945
4771
|
});
|
|
4946
4772
|
const interruption = {
|
|
4947
4773
|
request: outcome.request
|
|
4948
4774
|
};
|
|
4949
|
-
|
|
4775
|
+
record2.interrupts.push(interruption);
|
|
4950
4776
|
if (task.group) throw new ParallelChildWaiting();
|
|
4951
4777
|
interruption.response = await awaitInterruptInternal({
|
|
4952
4778
|
request: interruption.request,
|
|
@@ -4955,12 +4781,12 @@ async function runWorkflowCall(args, task) {
|
|
|
4955
4781
|
}
|
|
4956
4782
|
}
|
|
4957
4783
|
if (cached) {
|
|
4958
|
-
report("reused", workflowCallContent(telemetry, "output",
|
|
4959
|
-
if (
|
|
4960
|
-
await replayToolObservations(
|
|
4961
|
-
delete
|
|
4784
|
+
report("reused", workflowCallContent(telemetry, "output", record2.data));
|
|
4785
|
+
if (record2.restoredToolBranch !== telemetry?.correlation?.branchId)
|
|
4786
|
+
await replayToolObservations(record2.toolObservations ?? [], true);
|
|
4787
|
+
delete record2.restoredToolBranch;
|
|
4962
4788
|
}
|
|
4963
|
-
const data =
|
|
4789
|
+
const data = record2.data ?? {};
|
|
4964
4790
|
return {
|
|
4965
4791
|
data: JSON.parse(JSON.stringify(data))
|
|
4966
4792
|
};
|