@kortyx/agent 0.20.1 → 0.22.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 +41 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.js +598 -244
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -51,10 +51,12 @@ var require_dist = __commonJS({
|
|
|
51
51
|
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
52
52
|
var index_exports2 = {};
|
|
53
53
|
__export2(index_exports2, {
|
|
54
|
+
ParallelError: () => ParallelError,
|
|
54
55
|
WorkflowCallError: () => WorkflowCallError,
|
|
55
56
|
completeResponse: () => completeResponse,
|
|
56
57
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
57
58
|
emitWorkflowCall: () => emitWorkflowCall2,
|
|
59
|
+
parallel: () => parallel,
|
|
58
60
|
runWithHookContext: () => runWithHookContext,
|
|
59
61
|
useAbortSignal: () => useAbortSignal,
|
|
60
62
|
useInterrupt: () => useInterrupt,
|
|
@@ -118,6 +120,11 @@ var require_dist = __commonJS({
|
|
|
118
120
|
...ctx,
|
|
119
121
|
workflowCallActive: false,
|
|
120
122
|
workflowCallIds: /* @__PURE__ */ new Set(),
|
|
123
|
+
workflowTasks: [],
|
|
124
|
+
parallelJoins: [],
|
|
125
|
+
parallelGroup: void 0,
|
|
126
|
+
parallelIndex: 0,
|
|
127
|
+
workflowContextClosed: false,
|
|
121
128
|
nodeStateIndex: 0,
|
|
122
129
|
reasonCallIndex: 0,
|
|
123
130
|
currentNodeState,
|
|
@@ -189,10 +196,29 @@ var require_dist = __commonJS({
|
|
|
189
196
|
async function runWithHookContext(ctx, fn) {
|
|
190
197
|
const internal = createInternalContext(ctx);
|
|
191
198
|
try {
|
|
192
|
-
const result = await storage.run(internal,
|
|
199
|
+
const result = await storage.run(internal, async () => {
|
|
200
|
+
const drain = async () => {
|
|
201
|
+
let drained = -1;
|
|
202
|
+
while (drained !== internal.parallelJoins.length + internal.workflowTasks.length) {
|
|
203
|
+
drained = internal.parallelJoins.length + internal.workflowTasks.length;
|
|
204
|
+
await Promise.allSettled(internal.parallelJoins);
|
|
205
|
+
await Promise.all(internal.workflowTasks.map((task) => task.done));
|
|
206
|
+
}
|
|
207
|
+
internal.workflowContextClosed = true;
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const result2 = await fn();
|
|
211
|
+
await drain();
|
|
212
|
+
if (internal.workflowControlError) throw internal.workflowControlError;
|
|
213
|
+
return result2;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
await drain();
|
|
216
|
+
throw internal.workflowControlError ?? error;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
193
219
|
cleanupCompletedReasonCheckpoints(internal);
|
|
194
220
|
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
195
|
-
if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
|
|
221
|
+
if (key.startsWith("__useWorkflow:") || key.startsWith("__parallel:") || key === "__useWorkflowActivation") {
|
|
196
222
|
internal.currentNodeState.byKey[key] = null;
|
|
197
223
|
internal.stateDirty = true;
|
|
198
224
|
}
|
|
@@ -283,13 +309,18 @@ var require_dist = __commonJS({
|
|
|
283
309
|
workflowState: args.workflowState
|
|
284
310
|
}
|
|
285
311
|
});
|
|
312
|
+
var import_errors6 = require("@kortyx/core/errors");
|
|
286
313
|
var parseWithSchema = (schema, value, label) => {
|
|
287
314
|
if (!schema) return value;
|
|
288
315
|
const parsed = schema.safeParse(value);
|
|
289
316
|
if (parsed.success) return parsed.data;
|
|
290
317
|
const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
|
|
291
318
|
const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
|
|
292
|
-
throw new
|
|
319
|
+
throw new import_errors6.ValidationError(
|
|
320
|
+
label === "useReason output" ? "MODEL_OUTPUT_SCHEMA" : "SCHEMA_VALIDATION",
|
|
321
|
+
`${label} validation failed: ${reason}`,
|
|
322
|
+
parsedError
|
|
323
|
+
);
|
|
293
324
|
};
|
|
294
325
|
var awaitInterruptInternal = (args) => {
|
|
295
326
|
const ctx = getHookContext();
|
|
@@ -417,6 +448,21 @@ var require_dist = __commonJS({
|
|
|
417
448
|
var import_core32 = require("@kortyx/core");
|
|
418
449
|
var import_node_crypto22 = require("crypto");
|
|
419
450
|
var import_core22 = require("@kortyx/core");
|
|
451
|
+
var import_errors22 = require("@kortyx/core/errors");
|
|
452
|
+
function assertModelFinish(result) {
|
|
453
|
+
if (result.finishReason?.unified !== "content-filter") return;
|
|
454
|
+
throw Object.assign(
|
|
455
|
+
new import_errors22.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
|
|
456
|
+
category: "provider",
|
|
457
|
+
retryable: false,
|
|
458
|
+
safeMessage: "The provider refused the request."
|
|
459
|
+
}),
|
|
460
|
+
{
|
|
461
|
+
...result.usage ? { usage: result.usage } : {},
|
|
462
|
+
finishReason: result.finishReason
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
}
|
|
420
466
|
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
421
467
|
var createRuntimeId = () => {
|
|
422
468
|
try {
|
|
@@ -530,15 +576,18 @@ var require_dist = __commonJS({
|
|
|
530
576
|
};
|
|
531
577
|
const failTrace = (span, error) => {
|
|
532
578
|
const detail = error;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
579
|
+
try {
|
|
580
|
+
span?.fail?.(error, {
|
|
581
|
+
...detail?.usage ? { usage: detail.usage } : {},
|
|
582
|
+
...detail?.finishReason ? { finishReason: detail.finishReason } : {},
|
|
583
|
+
...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
|
|
584
|
+
attributes: {
|
|
585
|
+
providerId: args.model.provider.id,
|
|
586
|
+
modelId: args.model.modelId
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
} catch {
|
|
590
|
+
}
|
|
542
591
|
};
|
|
543
592
|
try {
|
|
544
593
|
if (stream) {
|
|
@@ -623,6 +672,8 @@ var require_dist = __commonJS({
|
|
|
623
672
|
}
|
|
624
673
|
}
|
|
625
674
|
}
|
|
675
|
+
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
676
|
+
assertModelFinish({ finishReason, usage });
|
|
626
677
|
if (emit) {
|
|
627
678
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
628
679
|
...commonMeta
|
|
@@ -644,6 +695,7 @@ var require_dist = __commonJS({
|
|
|
644
695
|
}
|
|
645
696
|
const response = await model.invoke(messages);
|
|
646
697
|
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
698
|
+
assertModelFinish(response);
|
|
647
699
|
if (response.content) args.onTextChunk?.(response.content);
|
|
648
700
|
if (emit) {
|
|
649
701
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
@@ -1433,6 +1485,7 @@ var require_dist = __commonJS({
|
|
|
1433
1485
|
}
|
|
1434
1486
|
};
|
|
1435
1487
|
}
|
|
1488
|
+
var import_errors32 = require("@kortyx/core/errors");
|
|
1436
1489
|
var tryJsonParse = (value) => {
|
|
1437
1490
|
try {
|
|
1438
1491
|
return { ok: true, value: JSON.parse(value) };
|
|
@@ -1470,132 +1523,135 @@ var require_dist = __commonJS({
|
|
|
1470
1523
|
}
|
|
1471
1524
|
return text;
|
|
1472
1525
|
};
|
|
1473
|
-
var
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
};
|
|
1478
|
-
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
1479
|
-
const trimmed = text.trim();
|
|
1480
|
-
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
1481
|
-
const direct = tryJsonParse(trimmed);
|
|
1482
|
-
if (!direct.ok && direct.error) return true;
|
|
1483
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
1484
|
-
if (!block) return false;
|
|
1485
|
-
const parsedBlock = tryJsonParse(block);
|
|
1486
|
-
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
1487
|
-
};
|
|
1488
|
-
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
1489
|
-
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
1526
|
+
var createTruncatedStructuredOutputError = (label, cause) => new import_errors32.ValidationError(
|
|
1527
|
+
"OUTPUT_TRUNCATED",
|
|
1528
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`,
|
|
1529
|
+
cause
|
|
1490
1530
|
);
|
|
1491
|
-
var createInvalidStructuredOutputError = (label) => new
|
|
1492
|
-
|
|
1531
|
+
var createInvalidStructuredOutputError = (label, cause) => new import_errors32.ValidationError(
|
|
1532
|
+
"INVALID_MODEL_JSON",
|
|
1533
|
+
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check outputSchema and provider structured-output settings.`,
|
|
1534
|
+
cause
|
|
1493
1535
|
);
|
|
1494
1536
|
var parseReasonOutputWithSchema = (args) => {
|
|
1495
1537
|
const candidate = resolveOutputCandidate(args.text);
|
|
1538
|
+
const block = extractJsonCodeBlock(args.text.trim());
|
|
1539
|
+
const validJson = tryJsonParse(args.text.trim()).ok || block !== null && tryJsonParse(block).ok;
|
|
1496
1540
|
try {
|
|
1497
1541
|
return parseWithSchema(args.schema, candidate, args.label);
|
|
1498
1542
|
} catch (error) {
|
|
1499
|
-
if (
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1543
|
+
if (!(error instanceof import_errors32.ValidationError)) throw error;
|
|
1544
|
+
const failure = args.finishReason?.unified === "length" ? createTruncatedStructuredOutputError(args.label, error) : typeof candidate === "string" && !validJson ? createInvalidStructuredOutputError(args.label, error) : error;
|
|
1545
|
+
if (failure && typeof failure === "object")
|
|
1546
|
+
Object.assign(failure, {
|
|
1547
|
+
...args.usage ? { usage: args.usage } : {},
|
|
1548
|
+
...args.finishReason ? { finishReason: args.finishReason } : {}
|
|
1549
|
+
});
|
|
1550
|
+
throw failure;
|
|
1506
1551
|
}
|
|
1507
1552
|
};
|
|
1508
1553
|
var parseInterruptFirstPassResult = (args) => {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
if (
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
const mode = args.mode ?? "required";
|
|
1522
|
-
let interruptRequired = true;
|
|
1523
|
-
if (mode === "optional") {
|
|
1524
|
-
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1525
|
-
throw new Error(
|
|
1526
|
-
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1554
|
+
try {
|
|
1555
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1556
|
+
if (!isRecord22(candidate)) {
|
|
1557
|
+
if (args.finishReason?.unified === "length") {
|
|
1558
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1559
|
+
}
|
|
1560
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1561
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1562
|
+
}
|
|
1563
|
+
throw new import_errors32.ValidationError(
|
|
1564
|
+
"MODEL_OUTPUT_SCHEMA",
|
|
1565
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
1527
1566
|
);
|
|
1528
1567
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1568
|
+
const mode = args.mode ?? "required";
|
|
1569
|
+
let interruptRequired = true;
|
|
1570
|
+
if (mode === "optional") {
|
|
1571
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1572
|
+
throw new import_errors32.ValidationError(
|
|
1573
|
+
"MODEL_OUTPUT_SCHEMA",
|
|
1574
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
1578
|
+
if (!interruptRequired) {
|
|
1579
|
+
const hasUnexpectedRequest = [
|
|
1580
|
+
candidate.interruptRequest,
|
|
1581
|
+
candidate.request,
|
|
1582
|
+
candidate.interrupt
|
|
1583
|
+
].some((value) => value !== null && value !== void 0);
|
|
1584
|
+
if (hasUnexpectedRequest) {
|
|
1585
|
+
throw new import_errors32.ValidationError(
|
|
1586
|
+
"MODEL_OUTPUT_SCHEMA",
|
|
1587
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
let request;
|
|
1593
|
+
if (interruptRequired) {
|
|
1594
|
+
const requestCandidates = [
|
|
1532
1595
|
candidate.interruptRequest,
|
|
1533
1596
|
candidate.request,
|
|
1534
|
-
candidate.interrupt
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1597
|
+
candidate.interrupt,
|
|
1598
|
+
mode === "required" ? candidate : void 0
|
|
1599
|
+
];
|
|
1600
|
+
const requestPayload = requestCandidates.find(
|
|
1601
|
+
(value) => value !== null && value !== void 0
|
|
1602
|
+
);
|
|
1603
|
+
request = parseWithSchema(
|
|
1604
|
+
args.requestSchema,
|
|
1605
|
+
requestPayload,
|
|
1606
|
+
"useReason interrupt.request"
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
let output;
|
|
1610
|
+
if (args.outputSchema) {
|
|
1611
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
1612
|
+
try {
|
|
1613
|
+
output = parseWithSchema(
|
|
1614
|
+
args.outputSchema,
|
|
1615
|
+
outputPayload,
|
|
1616
|
+
"useReason output"
|
|
1539
1617
|
);
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
if (!(error instanceof import_errors32.ValidationError)) throw error;
|
|
1620
|
+
if (args.finishReason?.unified === "length") {
|
|
1621
|
+
throw createTruncatedStructuredOutputError("useReason output", error);
|
|
1622
|
+
}
|
|
1623
|
+
throw error;
|
|
1540
1624
|
}
|
|
1541
1625
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
candidate.request,
|
|
1548
|
-
candidate.interrupt,
|
|
1549
|
-
mode === "required" ? candidate : void 0
|
|
1550
|
-
];
|
|
1551
|
-
const requestPayload = requestCandidates.find(
|
|
1552
|
-
(value) => value !== null && value !== void 0
|
|
1553
|
-
);
|
|
1554
|
-
request = parseWithSchema(
|
|
1555
|
-
args.requestSchema,
|
|
1556
|
-
requestPayload,
|
|
1557
|
-
"useReason interrupt.request"
|
|
1558
|
-
);
|
|
1559
|
-
}
|
|
1560
|
-
let output;
|
|
1561
|
-
if (args.outputSchema) {
|
|
1562
|
-
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
1563
|
-
try {
|
|
1564
|
-
output = parseWithSchema(
|
|
1565
|
-
args.outputSchema,
|
|
1566
|
-
outputPayload,
|
|
1567
|
-
"useReason output"
|
|
1626
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
1627
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
1628
|
+
throw new import_errors32.ValidationError(
|
|
1629
|
+
"MODEL_OUTPUT_SCHEMA",
|
|
1630
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1568
1631
|
);
|
|
1569
|
-
} catch (error) {
|
|
1570
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1571
|
-
throw createTruncatedStructuredOutputError("useReason output");
|
|
1572
|
-
}
|
|
1573
|
-
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
1574
|
-
throw createInvalidStructuredOutputError("useReason output");
|
|
1575
|
-
}
|
|
1576
|
-
throw error;
|
|
1577
1632
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
if (!interruptRequired) {
|
|
1633
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
1634
|
+
if (!interruptRequired) {
|
|
1635
|
+
return {
|
|
1636
|
+
draftText,
|
|
1637
|
+
interruptRequired: false,
|
|
1638
|
+
...output !== void 0 ? { output } : {}
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1587
1641
|
return {
|
|
1588
1642
|
draftText,
|
|
1589
|
-
interruptRequired:
|
|
1643
|
+
interruptRequired: true,
|
|
1644
|
+
request,
|
|
1590
1645
|
...output !== void 0 ? { output } : {}
|
|
1591
1646
|
};
|
|
1647
|
+
} catch (error) {
|
|
1648
|
+
if (error && typeof error === "object")
|
|
1649
|
+
Object.assign(error, {
|
|
1650
|
+
...args.usage ? { usage: args.usage } : {},
|
|
1651
|
+
...args.finishReason ? { finishReason: args.finishReason } : {}
|
|
1652
|
+
});
|
|
1653
|
+
throw error;
|
|
1592
1654
|
}
|
|
1593
|
-
return {
|
|
1594
|
-
draftText,
|
|
1595
|
-
interruptRequired: true,
|
|
1596
|
-
request,
|
|
1597
|
-
...output !== void 0 ? { output } : {}
|
|
1598
|
-
};
|
|
1599
1655
|
};
|
|
1600
1656
|
var import_zod32 = require("zod");
|
|
1601
1657
|
var tryStringify2 = (value) => {
|
|
@@ -1803,7 +1859,7 @@ ${tryStringify2(args.response)}`
|
|
|
1803
1859
|
});
|
|
1804
1860
|
};
|
|
1805
1861
|
var import_core42 = require("@kortyx/core");
|
|
1806
|
-
var
|
|
1862
|
+
var import_errors42 = require("@kortyx/core/errors");
|
|
1807
1863
|
var toToolDefinitions = (tools) => tools.map((tool) => ({
|
|
1808
1864
|
name: tool.name,
|
|
1809
1865
|
...tool.title ? { title: tool.title } : {},
|
|
@@ -1837,17 +1893,27 @@ ${tryStringify2(args.response)}`
|
|
|
1837
1893
|
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1838
1894
|
};
|
|
1839
1895
|
};
|
|
1840
|
-
var closeOwnedTools = async (tools) => {
|
|
1896
|
+
var closeOwnedTools = async (tools, preserveFailure = false, traceSpan) => {
|
|
1841
1897
|
const closeFns = /* @__PURE__ */ new Set();
|
|
1842
1898
|
for (const tool of tools) {
|
|
1843
1899
|
if (tool.closeAfterUse === false || !tool.close) continue;
|
|
1844
1900
|
closeFns.add(tool.close);
|
|
1845
1901
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1902
|
+
try {
|
|
1903
|
+
await Promise.all(
|
|
1904
|
+
[...closeFns].map(async (close) => {
|
|
1905
|
+
await close();
|
|
1906
|
+
})
|
|
1907
|
+
);
|
|
1908
|
+
} catch (error) {
|
|
1909
|
+
if (!preserveFailure) throw error;
|
|
1910
|
+
try {
|
|
1911
|
+
traceSpan?.addEvent?.("useReason.cleanup.failed", {
|
|
1912
|
+
failure: (0, import_errors42.serializeFailure)(error)
|
|
1913
|
+
});
|
|
1914
|
+
} catch {
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1851
1917
|
};
|
|
1852
1918
|
var createInitialMessages = (args) => [
|
|
1853
1919
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
@@ -1864,6 +1930,7 @@ ${tryStringify2(args.response)}`
|
|
|
1864
1930
|
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1865
1931
|
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1866
1932
|
const opId = checkpoint?.opId ?? args.opId;
|
|
1933
|
+
let primaryFailure = false;
|
|
1867
1934
|
const tools = useReasonArgs.tools ?? [];
|
|
1868
1935
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1869
1936
|
let validationCompleted = false;
|
|
@@ -1896,7 +1963,7 @@ ${tryStringify2(args.response)}`
|
|
|
1896
1963
|
validationCompleted = true;
|
|
1897
1964
|
} finally {
|
|
1898
1965
|
if (!validationCompleted) {
|
|
1899
|
-
await closeOwnedTools(tools);
|
|
1966
|
+
await closeOwnedTools(tools, true, traceSpan);
|
|
1900
1967
|
}
|
|
1901
1968
|
}
|
|
1902
1969
|
const toolDefinitions = toToolDefinitions(tools);
|
|
@@ -2015,6 +2082,7 @@ ${tryStringify2(args.response)}`
|
|
|
2015
2082
|
text: step.text,
|
|
2016
2083
|
schema: useReasonArgs.outputSchema,
|
|
2017
2084
|
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
2085
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
2018
2086
|
label: "useReason output"
|
|
2019
2087
|
});
|
|
2020
2088
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, [
|
|
@@ -2102,12 +2170,12 @@ ${tryStringify2(args.response)}`
|
|
|
2102
2170
|
options: [
|
|
2103
2171
|
{ id: "approve", label: "Approve" },
|
|
2104
2172
|
{ id: "deny", label: "Deny" }
|
|
2105
|
-
]
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2173
|
+
]
|
|
2174
|
+
},
|
|
2175
|
+
meta: {
|
|
2176
|
+
tool: tool.name,
|
|
2177
|
+
toolCallId: toolCall.id,
|
|
2178
|
+
input: toolCall.input
|
|
2111
2179
|
}
|
|
2112
2180
|
});
|
|
2113
2181
|
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
@@ -2169,12 +2237,12 @@ ${tryStringify2(args.response)}`
|
|
|
2169
2237
|
});
|
|
2170
2238
|
} catch (error) {
|
|
2171
2239
|
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
2172
|
-
if ((0,
|
|
2173
|
-
throw error;
|
|
2240
|
+
if ((0, import_errors42.isControlFlowError)(error)) throw error;
|
|
2174
2241
|
const result2 = {
|
|
2175
2242
|
toolCallId: toolCall.id,
|
|
2176
2243
|
name: tool.name,
|
|
2177
|
-
content:
|
|
2244
|
+
content: (0, import_errors42.serializeFailure)(error).message,
|
|
2245
|
+
failure: (0, import_errors42.serializeFailure)(error),
|
|
2178
2246
|
isError: true
|
|
2179
2247
|
};
|
|
2180
2248
|
toolResults.push(result2);
|
|
@@ -2183,14 +2251,16 @@ ${tryStringify2(args.response)}`
|
|
|
2183
2251
|
emitToolEvent("tool-call-error", {
|
|
2184
2252
|
tool: tool.name,
|
|
2185
2253
|
toolCallId: toolCall.id,
|
|
2186
|
-
message: result2.content
|
|
2254
|
+
message: result2.content,
|
|
2255
|
+
failure: result2.failure
|
|
2187
2256
|
});
|
|
2188
2257
|
}
|
|
2189
2258
|
traceSpan?.addEvent?.("useReason.tool-call.error", {
|
|
2190
2259
|
stepIndex,
|
|
2191
2260
|
tool: tool.name,
|
|
2192
2261
|
toolCallId: toolCall.id,
|
|
2193
|
-
message: result2.content
|
|
2262
|
+
message: result2.content,
|
|
2263
|
+
failure: result2.failure
|
|
2194
2264
|
});
|
|
2195
2265
|
}
|
|
2196
2266
|
const completed2 = toolResults.at(-1);
|
|
@@ -2217,6 +2287,7 @@ ${tryStringify2(args.response)}`
|
|
|
2217
2287
|
text: finalText,
|
|
2218
2288
|
schema: useReasonArgs.outputSchema,
|
|
2219
2289
|
...finalFinishReason ? { finishReason: finalFinishReason } : {},
|
|
2290
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
2220
2291
|
label: "useReason output"
|
|
2221
2292
|
});
|
|
2222
2293
|
}
|
|
@@ -2263,15 +2334,19 @@ ${tryStringify2(args.response)}`
|
|
|
2263
2334
|
ctx.stateDirty = true;
|
|
2264
2335
|
return result;
|
|
2265
2336
|
} catch (error) {
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2337
|
+
primaryFailure = true;
|
|
2338
|
+
try {
|
|
2339
|
+
traceSpan?.fail?.(error, {
|
|
2340
|
+
attributes: {
|
|
2341
|
+
toolStepCount: steps.length,
|
|
2342
|
+
toolCallCount: allToolCalls.length
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
} catch {
|
|
2346
|
+
}
|
|
2272
2347
|
throw error;
|
|
2273
2348
|
} finally {
|
|
2274
|
-
await closeOwnedTools(tools);
|
|
2349
|
+
await closeOwnedTools(tools, primaryFailure, traceSpan);
|
|
2275
2350
|
}
|
|
2276
2351
|
};
|
|
2277
2352
|
var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
|
|
@@ -2434,6 +2509,7 @@ ${tryStringify2(args.response)}`
|
|
|
2434
2509
|
text: first.text,
|
|
2435
2510
|
requestSchema: args.interrupt.requestSchema,
|
|
2436
2511
|
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2512
|
+
...first.usage ? { usage: first.usage } : {},
|
|
2437
2513
|
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
2438
2514
|
mode: args.interrupt.mode ?? "required"
|
|
2439
2515
|
});
|
|
@@ -2451,6 +2527,7 @@ ${tryStringify2(args.response)}`
|
|
|
2451
2527
|
text: first.text,
|
|
2452
2528
|
schema: args.outputSchema,
|
|
2453
2529
|
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2530
|
+
...first.usage ? { usage: first.usage } : {},
|
|
2454
2531
|
label: "useReason output"
|
|
2455
2532
|
});
|
|
2456
2533
|
}
|
|
@@ -2545,6 +2622,7 @@ ${tryStringify2(args.response)}`
|
|
|
2545
2622
|
text: finalText,
|
|
2546
2623
|
schema: args.outputSchema,
|
|
2547
2624
|
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
2625
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
2548
2626
|
label: "useReason output"
|
|
2549
2627
|
});
|
|
2550
2628
|
}
|
|
@@ -2647,8 +2725,176 @@ ${tryStringify2(args.response)}`
|
|
|
2647
2725
|
};
|
|
2648
2726
|
return [getValue(), setValue];
|
|
2649
2727
|
}
|
|
2650
|
-
var import_node_crypto5 = require("crypto");
|
|
2651
2728
|
var import_core62 = require("@kortyx/core");
|
|
2729
|
+
var import_errors52 = require("@kortyx/core/errors");
|
|
2730
|
+
var ParallelChildWaiting = class extends Error {
|
|
2731
|
+
name = "ParallelChildWaiting";
|
|
2732
|
+
};
|
|
2733
|
+
var ParallelError = class extends AggregateError {
|
|
2734
|
+
constructor(results) {
|
|
2735
|
+
super(
|
|
2736
|
+
results.flatMap(
|
|
2737
|
+
(result) => result.status === "rejected" ? [result.reason] : []
|
|
2738
|
+
),
|
|
2739
|
+
"One or more parallel operations failed."
|
|
2740
|
+
);
|
|
2741
|
+
this.results = results;
|
|
2742
|
+
this.name = "ParallelError";
|
|
2743
|
+
this.failure = (0, import_errors52.serializeFailure)({
|
|
2744
|
+
version: 1,
|
|
2745
|
+
code: this.code,
|
|
2746
|
+
category: "workflow",
|
|
2747
|
+
message: this.message,
|
|
2748
|
+
retryable: null,
|
|
2749
|
+
results: results.map(
|
|
2750
|
+
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors52.serializeFailure)(result.reason) }
|
|
2751
|
+
)
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
results;
|
|
2755
|
+
code = "PARALLEL_FAILED";
|
|
2756
|
+
failure;
|
|
2757
|
+
toJSON() {
|
|
2758
|
+
return (0, import_errors52.serializeFailure)(this);
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
function registerWorkflowTask(id) {
|
|
2762
|
+
const ctx = getHookContext();
|
|
2763
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2764
|
+
if (ctx.workflowContextClosed)
|
|
2765
|
+
throw new Error("The workflow node activation has finished.");
|
|
2766
|
+
const group = ctx.parallelGroup;
|
|
2767
|
+
if (group?.restored && !group.record.calls.includes(id)) {
|
|
2768
|
+
throw new Error(`Parallel call membership changed during replay: '${id}'.`);
|
|
2769
|
+
}
|
|
2770
|
+
let finish;
|
|
2771
|
+
const task = {
|
|
2772
|
+
id,
|
|
2773
|
+
...group ? { group } : {},
|
|
2774
|
+
completed: false,
|
|
2775
|
+
done: new Promise((resolve2) => {
|
|
2776
|
+
finish = resolve2;
|
|
2777
|
+
}),
|
|
2778
|
+
finish: () => {
|
|
2779
|
+
task.completed = true;
|
|
2780
|
+
finish();
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
ctx.workflowTasks.push(task);
|
|
2784
|
+
if (group && !group.record.calls.includes(id)) group.record.calls.push(id);
|
|
2785
|
+
return task;
|
|
2786
|
+
}
|
|
2787
|
+
function parallel(values) {
|
|
2788
|
+
const settled = Promise.allSettled(values);
|
|
2789
|
+
let ctx;
|
|
2790
|
+
try {
|
|
2791
|
+
ctx = getHookContext();
|
|
2792
|
+
} catch (error) {
|
|
2793
|
+
return Promise.reject(error);
|
|
2794
|
+
}
|
|
2795
|
+
const join = async () => {
|
|
2796
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2797
|
+
if (ctx.parallelGroup)
|
|
2798
|
+
throw new Error(
|
|
2799
|
+
"Await each parallel group before starting another in the same node."
|
|
2800
|
+
);
|
|
2801
|
+
const key = `__parallel:${ctx.parallelIndex++}`;
|
|
2802
|
+
const saved = ctx.currentNodeState.byKey[key];
|
|
2803
|
+
const tasks = ctx.workflowTasks.filter(
|
|
2804
|
+
(task) => !task.completed && !task.group
|
|
2805
|
+
);
|
|
2806
|
+
const ids = tasks.map((task) => task.id);
|
|
2807
|
+
if (saved && (saved.version !== 1 || ids.some((id) => !saved.calls.includes(id)))) {
|
|
2808
|
+
throw new Error("Parallel call membership changed during replay.");
|
|
2809
|
+
}
|
|
2810
|
+
const group = {
|
|
2811
|
+
key,
|
|
2812
|
+
record: saved ?? { version: 1, calls: ids, interrupts: [] },
|
|
2813
|
+
restored: Boolean(saved),
|
|
2814
|
+
ready: Promise.resolve()
|
|
2815
|
+
};
|
|
2816
|
+
ctx.currentNodeState.byKey[key] = group.record;
|
|
2817
|
+
ctx.stateDirty = true;
|
|
2818
|
+
ctx.parallelGroup = group;
|
|
2819
|
+
for (const task of tasks) task.group = group;
|
|
2820
|
+
const callRecord = (id) => ctx.currentNodeState.byKey[`__useWorkflow:${id}`];
|
|
2821
|
+
const bridge = async (callId, index) => {
|
|
2822
|
+
const interruption = callRecord(callId)?.interrupts[index];
|
|
2823
|
+
if (!interruption)
|
|
2824
|
+
throw new Error(
|
|
2825
|
+
"Parallel interrupt history does not match the saved child."
|
|
2826
|
+
);
|
|
2827
|
+
try {
|
|
2828
|
+
interruption.response = await awaitInterruptInternal({
|
|
2829
|
+
request: interruption.request,
|
|
2830
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2831
|
+
});
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
ctx.workflowControlError = error;
|
|
2834
|
+
throw error;
|
|
2835
|
+
}
|
|
2836
|
+
};
|
|
2837
|
+
group.ready = (async () => {
|
|
2838
|
+
for (const entry of group.record.interrupts)
|
|
2839
|
+
await bridge(entry.callId, entry.index);
|
|
2840
|
+
})();
|
|
2841
|
+
try {
|
|
2842
|
+
const [ready, outcomes] = await Promise.all([
|
|
2843
|
+
Promise.allSettled([group.ready]),
|
|
2844
|
+
settled
|
|
2845
|
+
]);
|
|
2846
|
+
let drained = -1;
|
|
2847
|
+
while (drained !== ctx.workflowTasks.length) {
|
|
2848
|
+
drained = ctx.workflowTasks.length;
|
|
2849
|
+
await Promise.all(
|
|
2850
|
+
ctx.workflowTasks.filter((task) => task.group === group).map((task) => task.done)
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
if (ready[0]?.status === "rejected") throw ready[0].reason;
|
|
2854
|
+
(0, import_core62.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2855
|
+
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2856
|
+
const rejected = outcomes.filter(
|
|
2857
|
+
(outcome) => outcome.status === "rejected"
|
|
2858
|
+
);
|
|
2859
|
+
const control = rejected.find(
|
|
2860
|
+
(outcome) => (0, import_core62.isExecutionCancelled)(outcome.reason) || (0, import_core62.isExecutionLimitReached)(outcome.reason)
|
|
2861
|
+
);
|
|
2862
|
+
if (control) {
|
|
2863
|
+
ctx.workflowControlError = control.reason;
|
|
2864
|
+
throw control.reason;
|
|
2865
|
+
}
|
|
2866
|
+
const actual = ctx.workflowTasks.filter((task) => task.group === group).map((task) => task.id);
|
|
2867
|
+
if (group.record.calls.length !== actual.length || group.record.calls.some((id) => !actual.includes(id))) {
|
|
2868
|
+
throw new Error("Parallel call membership changed during replay.");
|
|
2869
|
+
}
|
|
2870
|
+
for (const callId of group.record.calls) {
|
|
2871
|
+
const record = callRecord(callId);
|
|
2872
|
+
if (record?.status !== "interrupted") continue;
|
|
2873
|
+
const index = record.interrupts.length - 1;
|
|
2874
|
+
if (record.interrupts[index]?.response !== void 0) continue;
|
|
2875
|
+
group.record.interrupts.push({ callId, index });
|
|
2876
|
+
await bridge(callId, index);
|
|
2877
|
+
throw new Error(
|
|
2878
|
+
"Parallel interrupt unexpectedly resumed without replay."
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
if (rejected.length) throw new ParallelError(outcomes);
|
|
2882
|
+
return outcomes.map(
|
|
2883
|
+
(outcome) => outcome.value
|
|
2884
|
+
);
|
|
2885
|
+
} finally {
|
|
2886
|
+
ctx.parallelGroup = void 0;
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
const promise = join();
|
|
2890
|
+
ctx.parallelJoins.push(promise);
|
|
2891
|
+
void promise.catch(() => {
|
|
2892
|
+
});
|
|
2893
|
+
return promise;
|
|
2894
|
+
}
|
|
2895
|
+
var import_node_crypto5 = require("crypto");
|
|
2896
|
+
var import_core7 = require("@kortyx/core");
|
|
2897
|
+
var import_errors62 = require("@kortyx/core/errors");
|
|
2652
2898
|
var import_node_crypto42 = require("crypto");
|
|
2653
2899
|
function emitWorkflowCall2(telemetry, type, payload) {
|
|
2654
2900
|
const base = telemetry?.correlation;
|
|
@@ -2686,10 +2932,20 @@ ${tryStringify2(args.response)}`
|
|
|
2686
2932
|
const serialized = JSON.stringify(value);
|
|
2687
2933
|
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
2688
2934
|
}
|
|
2689
|
-
var WorkflowCallError = class extends
|
|
2690
|
-
constructor(workflow, message) {
|
|
2691
|
-
|
|
2935
|
+
var WorkflowCallError = class extends import_errors62.KortyxError {
|
|
2936
|
+
constructor(workflow, message, cause, failure) {
|
|
2937
|
+
const detail = (0, import_errors62.serializeFailure)(
|
|
2938
|
+
failure ?? cause ?? new import_errors62.WorkflowContractError(message)
|
|
2939
|
+
);
|
|
2940
|
+
super(detail.code, `Workflow '${workflow}': ${message}`, {
|
|
2941
|
+
...detail,
|
|
2942
|
+
safeMessage: detail.message,
|
|
2943
|
+
...cause !== void 0 ? { cause } : {}
|
|
2944
|
+
});
|
|
2692
2945
|
this.workflow = workflow;
|
|
2946
|
+
Object.assign(this.failure, detail, {
|
|
2947
|
+
context: { ...detail.context, workflow }
|
|
2948
|
+
});
|
|
2693
2949
|
this.name = "WorkflowCallError";
|
|
2694
2950
|
}
|
|
2695
2951
|
workflow;
|
|
@@ -2700,10 +2956,12 @@ ${tryStringify2(args.response)}`
|
|
|
2700
2956
|
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2701
2957
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2702
2958
|
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2703
|
-
throw new
|
|
2959
|
+
throw new import_errors62.WorkflowContractError(
|
|
2960
|
+
"Workflow input and output must be JSON serializable."
|
|
2961
|
+
);
|
|
2704
2962
|
}
|
|
2705
2963
|
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2706
|
-
throw new
|
|
2964
|
+
throw new import_errors62.WorkflowContractError(
|
|
2707
2965
|
"Workflow input and output must contain plain JSON objects."
|
|
2708
2966
|
);
|
|
2709
2967
|
}
|
|
@@ -2724,29 +2982,49 @@ ${tryStringify2(args.response)}`
|
|
|
2724
2982
|
useWorkflow: (args) => {
|
|
2725
2983
|
const workflow = workflows[args.workflow];
|
|
2726
2984
|
if (!workflow || workflow.id !== args.workflow)
|
|
2727
|
-
throw new
|
|
2985
|
+
throw new import_errors62.WorkflowContractError(
|
|
2986
|
+
"Workflow registry keys must match definition ids."
|
|
2987
|
+
);
|
|
2728
2988
|
return useWorkflow({ ...args, workflow });
|
|
2729
2989
|
}
|
|
2730
2990
|
};
|
|
2731
2991
|
}
|
|
2732
2992
|
async function useWorkflow(args) {
|
|
2993
|
+
const task = registerWorkflowTask(args.id);
|
|
2994
|
+
try {
|
|
2995
|
+
await Promise.resolve();
|
|
2996
|
+
if (task.group) await task.group.ready;
|
|
2997
|
+
return await runWorkflowCall(args, task);
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
if ((0, import_core7.isExecutionCancelled)(error) || (0, import_core7.isExecutionLimitReached)(error))
|
|
3000
|
+
getHookContext().workflowControlError = error;
|
|
3001
|
+
throw error;
|
|
3002
|
+
} finally {
|
|
3003
|
+
task.finish();
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
async function runWorkflowCall(args, task) {
|
|
2733
3007
|
const ctx = getHookContext();
|
|
2734
|
-
(0,
|
|
3008
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2735
3009
|
const service = ctx.node.callWorkflow;
|
|
2736
3010
|
if (!service)
|
|
2737
|
-
throw new
|
|
3011
|
+
throw new import_errors62.WorkflowContractError(
|
|
3012
|
+
"useWorkflow requires an agent workflow registry."
|
|
3013
|
+
);
|
|
2738
3014
|
if (!args.id.trim())
|
|
2739
|
-
throw new
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
3015
|
+
throw new import_errors62.WorkflowContractError(
|
|
3016
|
+
"useWorkflow requires a stable, nonempty id."
|
|
3017
|
+
);
|
|
3018
|
+
if (ctx.workflowCallActive && !task.group)
|
|
3019
|
+
throw new import_errors62.WorkflowContractError(
|
|
3020
|
+
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
2743
3021
|
);
|
|
2744
3022
|
if (ctx.workflowCallIds.has(args.id))
|
|
2745
|
-
throw new
|
|
3023
|
+
throw new import_errors62.WorkflowContractError(
|
|
2746
3024
|
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
2747
3025
|
);
|
|
2748
3026
|
if (ctx.workflowCallIds.size >= 64)
|
|
2749
|
-
throw new
|
|
3027
|
+
throw new import_errors62.WorkflowContractError(
|
|
2750
3028
|
"A node may call at most 64 child workflows per activation."
|
|
2751
3029
|
);
|
|
2752
3030
|
ctx.workflowCallIds.add(args.id);
|
|
@@ -2760,6 +3038,11 @@ ${tryStringify2(args.response)}`
|
|
|
2760
3038
|
});
|
|
2761
3039
|
const key = `__useWorkflow:${args.id}`;
|
|
2762
3040
|
let record = ctx.currentNodeState.byKey[key];
|
|
3041
|
+
if (record && record.parallelKey !== task.group?.key)
|
|
3042
|
+
throw new WorkflowCallError(
|
|
3043
|
+
workflow,
|
|
3044
|
+
"parallel call membership changed during replay"
|
|
3045
|
+
);
|
|
2763
3046
|
if (record && record.fingerprint !== fingerprint)
|
|
2764
3047
|
throw new WorkflowCallError(
|
|
2765
3048
|
workflow,
|
|
@@ -2770,11 +3053,12 @@ ${tryStringify2(args.response)}`
|
|
|
2770
3053
|
fingerprint,
|
|
2771
3054
|
invocationId: (0, import_node_crypto5.randomUUID)(),
|
|
2772
3055
|
status: "running",
|
|
2773
|
-
interrupts: []
|
|
3056
|
+
interrupts: [],
|
|
3057
|
+
...task.group ? { parallelKey: task.group.key } : {}
|
|
2774
3058
|
};
|
|
2775
3059
|
ctx.currentNodeState.byKey[key] = record;
|
|
2776
3060
|
ctx.stateDirty = true;
|
|
2777
|
-
ctx.workflowCallActive = true;
|
|
3061
|
+
if (!task.group) ctx.workflowCallActive = true;
|
|
2778
3062
|
const telemetry = ctx.node.workflowCallTelemetry;
|
|
2779
3063
|
const activationKey = "__useWorkflowActivation";
|
|
2780
3064
|
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
|
|
@@ -2810,14 +3094,21 @@ ${tryStringify2(args.response)}`
|
|
|
2810
3094
|
};
|
|
2811
3095
|
const cached = record.status === "completed";
|
|
2812
3096
|
try {
|
|
2813
|
-
for (const interruption of record.interrupts) {
|
|
3097
|
+
for (const interruption of task.group ? [] : record.interrupts) {
|
|
2814
3098
|
interruption.response = await awaitInterruptInternal({
|
|
2815
3099
|
request: interruption.request,
|
|
2816
3100
|
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2817
3101
|
});
|
|
2818
3102
|
}
|
|
2819
3103
|
if (record.status === "failed")
|
|
2820
|
-
throw new WorkflowCallError(
|
|
3104
|
+
throw new WorkflowCallError(
|
|
3105
|
+
workflow,
|
|
3106
|
+
record.error ?? "child failed",
|
|
3107
|
+
void 0,
|
|
3108
|
+
record.failure ?? (0, import_errors62.serializeFailure)(void 0)
|
|
3109
|
+
);
|
|
3110
|
+
if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
|
|
3111
|
+
throw new ParallelChildWaiting();
|
|
2821
3112
|
while (record.status !== "completed") {
|
|
2822
3113
|
let outcome;
|
|
2823
3114
|
try {
|
|
@@ -2836,13 +3127,13 @@ ${tryStringify2(args.response)}`
|
|
|
2836
3127
|
...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
|
|
2837
3128
|
});
|
|
2838
3129
|
} catch (error) {
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
3130
|
+
const child = error;
|
|
3131
|
+
recordUsage(child?.__kortyxChildUsage);
|
|
3132
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3133
|
+
if ((0, import_core7.isExecutionCancelled)(error)) throw error;
|
|
3134
|
+
if ((0, import_core7.isExecutionLimitReached)(error)) {
|
|
3135
|
+
if (child?.__kortyxChildSnapshot)
|
|
2844
3136
|
record.snapshot = child.__kortyxChildSnapshot;
|
|
2845
|
-
recordUsage(child.__kortyxChildUsage);
|
|
2846
3137
|
report("suspended", {
|
|
2847
3138
|
reason: "limit_reached",
|
|
2848
3139
|
limit: error.limitReached
|
|
@@ -2850,11 +3141,17 @@ ${tryStringify2(args.response)}`
|
|
|
2850
3141
|
throw error;
|
|
2851
3142
|
}
|
|
2852
3143
|
record.status = "failed";
|
|
2853
|
-
record.
|
|
3144
|
+
record.failure = (0, import_errors62.serializeFailure)(error);
|
|
3145
|
+
record.error = record.failure.message;
|
|
2854
3146
|
report("failed", { error: "Child workflow failed" });
|
|
2855
|
-
throw new WorkflowCallError(
|
|
3147
|
+
throw new WorkflowCallError(
|
|
3148
|
+
workflow,
|
|
3149
|
+
record.error,
|
|
3150
|
+
error,
|
|
3151
|
+
record.failure
|
|
3152
|
+
);
|
|
2856
3153
|
}
|
|
2857
|
-
(0,
|
|
3154
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2858
3155
|
recordUsage(outcome.usage);
|
|
2859
3156
|
if (outcome.status === "completed") {
|
|
2860
3157
|
record.data = outcome.data;
|
|
@@ -2875,6 +3172,7 @@ ${tryStringify2(args.response)}`
|
|
|
2875
3172
|
request: outcome.request
|
|
2876
3173
|
};
|
|
2877
3174
|
record.interrupts.push(interruption);
|
|
3175
|
+
if (task.group) throw new ParallelChildWaiting();
|
|
2878
3176
|
interruption.response = await awaitInterruptInternal({
|
|
2879
3177
|
request: interruption.request,
|
|
2880
3178
|
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
@@ -2888,7 +3186,7 @@ ${tryStringify2(args.response)}`
|
|
|
2888
3186
|
data: JSON.parse(JSON.stringify(data))
|
|
2889
3187
|
};
|
|
2890
3188
|
} finally {
|
|
2891
|
-
ctx.workflowCallActive = false;
|
|
3189
|
+
if (!task.group) ctx.workflowCallActive = false;
|
|
2892
3190
|
}
|
|
2893
3191
|
}
|
|
2894
3192
|
}
|
|
@@ -2901,6 +3199,7 @@ __export(index_exports, {
|
|
|
2901
3199
|
createAgent: () => createAgent,
|
|
2902
3200
|
createChatRouteHandler: () => createChatRouteHandler,
|
|
2903
3201
|
createCheckpointRouteHandler: () => createCheckpointRouteHandler,
|
|
3202
|
+
createFailureResponse: () => createFailureResponse,
|
|
2904
3203
|
extractLatestUserMessage: () => extractLatestUserMessage,
|
|
2905
3204
|
handleChatRequestBody: () => handleChatRequestBody,
|
|
2906
3205
|
handleCheckpointRequestBody: () => handleCheckpointRequestBody,
|
|
@@ -2909,6 +3208,7 @@ __export(index_exports, {
|
|
|
2909
3208
|
parseCheckpointRequestBody: () => parseCheckpointRequestBody,
|
|
2910
3209
|
parseResumeMeta: () => parseResumeMeta,
|
|
2911
3210
|
projectWorkflowTopology: () => projectWorkflowTopology,
|
|
3211
|
+
readRequestJson: () => readRequestJson,
|
|
2912
3212
|
streamChat: () => streamChat,
|
|
2913
3213
|
streamChatFromRoute: () => streamChatFromRoute,
|
|
2914
3214
|
transformGraphStreamForUI: () => transformGraphStreamForUI,
|
|
@@ -2917,6 +3217,7 @@ __export(index_exports, {
|
|
|
2917
3217
|
module.exports = __toCommonJS(index_exports);
|
|
2918
3218
|
|
|
2919
3219
|
// src/adapters/http.ts
|
|
3220
|
+
var import_errors = require("@kortyx/core/errors");
|
|
2920
3221
|
var import_stream = require("@kortyx/stream");
|
|
2921
3222
|
var import_zod = require("zod");
|
|
2922
3223
|
var chatMessageSchema = import_zod.z.object({
|
|
@@ -2952,12 +3253,41 @@ var checkpointRequestBodySchema = import_zod.z.discriminatedUnion("action", [
|
|
|
2952
3253
|
newSessionId: import_zod.z.string().min(1).optional()
|
|
2953
3254
|
}).strict()
|
|
2954
3255
|
]);
|
|
2955
|
-
|
|
3256
|
+
async function readRequestJson(request) {
|
|
3257
|
+
try {
|
|
3258
|
+
const value = await request.json();
|
|
3259
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3260
|
+
throw new Error("Expected a JSON object.");
|
|
3261
|
+
return value;
|
|
3262
|
+
} catch (cause) {
|
|
3263
|
+
throw new import_errors.KortyxError(
|
|
3264
|
+
"INVALID_REQUEST",
|
|
3265
|
+
"Request body is not valid JSON.",
|
|
3266
|
+
{
|
|
3267
|
+
category: "request",
|
|
3268
|
+
retryable: false,
|
|
3269
|
+
safeMessage: "The request body must contain a valid JSON object.",
|
|
3270
|
+
cause
|
|
3271
|
+
}
|
|
3272
|
+
);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
function createFailureResponse(error, status) {
|
|
3276
|
+
const failure = (0, import_errors.serializeFailure)(error);
|
|
3277
|
+
return Response.json(
|
|
3278
|
+
{ error: failure.message, failure },
|
|
3279
|
+
{ status: status ?? (0, import_errors.failureHttpStatus)(failure) }
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
2956
3282
|
function parseChatRequestBody(value) {
|
|
2957
3283
|
const parsed = chatRequestBodySchema.safeParse(value);
|
|
2958
3284
|
if (!parsed.success) {
|
|
2959
3285
|
const [firstIssue] = parsed.error.issues;
|
|
2960
|
-
throw new
|
|
3286
|
+
throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
3287
|
+
category: "request",
|
|
3288
|
+
retryable: false,
|
|
3289
|
+
safeMessage: "The request body is invalid."
|
|
3290
|
+
});
|
|
2961
3291
|
}
|
|
2962
3292
|
const sessionId = parsed.data.sessionId?.trim();
|
|
2963
3293
|
const workflowId = parsed.data.workflowId?.trim();
|
|
@@ -2973,7 +3303,11 @@ function parseCheckpointRequestBody(value) {
|
|
|
2973
3303
|
const parsed = checkpointRequestBodySchema.safeParse(value);
|
|
2974
3304
|
if (parsed.success) return parsed.data;
|
|
2975
3305
|
const [firstIssue] = parsed.error.issues;
|
|
2976
|
-
throw new
|
|
3306
|
+
throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
|
|
3307
|
+
category: "request",
|
|
3308
|
+
retryable: false,
|
|
3309
|
+
safeMessage: "The request body is invalid."
|
|
3310
|
+
});
|
|
2977
3311
|
}
|
|
2978
3312
|
async function handleChatRequestBody(args) {
|
|
2979
3313
|
const { agent, body } = args;
|
|
@@ -2995,10 +3329,10 @@ async function handleChatRequestBody(args) {
|
|
|
2995
3329
|
return (0, import_stream.toSSE)(stream);
|
|
2996
3330
|
}
|
|
2997
3331
|
function createChatRouteHandler(args) {
|
|
2998
|
-
const { agent, errorStatus
|
|
3332
|
+
const { agent, errorStatus } = args;
|
|
2999
3333
|
return async function POST(request) {
|
|
3000
3334
|
try {
|
|
3001
|
-
const body = parseChatRequestBody(await request
|
|
3335
|
+
const body = parseChatRequestBody(await readRequestJson(request));
|
|
3002
3336
|
return await handleChatRequestBody({
|
|
3003
3337
|
agent,
|
|
3004
3338
|
body,
|
|
@@ -3006,17 +3340,7 @@ function createChatRouteHandler(args) {
|
|
|
3006
3340
|
onExecution: args.onExecution
|
|
3007
3341
|
});
|
|
3008
3342
|
} catch (error) {
|
|
3009
|
-
return
|
|
3010
|
-
JSON.stringify({
|
|
3011
|
-
error: toErrorMessage(error)
|
|
3012
|
-
}),
|
|
3013
|
-
{
|
|
3014
|
-
status: errorStatus,
|
|
3015
|
-
headers: {
|
|
3016
|
-
"content-type": "application/json"
|
|
3017
|
-
}
|
|
3018
|
-
}
|
|
3019
|
-
);
|
|
3343
|
+
return createFailureResponse(error, errorStatus);
|
|
3020
3344
|
}
|
|
3021
3345
|
};
|
|
3022
3346
|
}
|
|
@@ -3032,23 +3356,13 @@ async function handleCheckpointRequestBody(args) {
|
|
|
3032
3356
|
});
|
|
3033
3357
|
}
|
|
3034
3358
|
function createCheckpointRouteHandler(args) {
|
|
3035
|
-
const { agent, errorStatus
|
|
3359
|
+
const { agent, errorStatus } = args;
|
|
3036
3360
|
return async function POST(request) {
|
|
3037
3361
|
try {
|
|
3038
|
-
const body = parseCheckpointRequestBody(await request
|
|
3362
|
+
const body = parseCheckpointRequestBody(await readRequestJson(request));
|
|
3039
3363
|
return await handleCheckpointRequestBody({ agent, body });
|
|
3040
3364
|
} catch (error) {
|
|
3041
|
-
return
|
|
3042
|
-
JSON.stringify({
|
|
3043
|
-
error: toErrorMessage(error)
|
|
3044
|
-
}),
|
|
3045
|
-
{
|
|
3046
|
-
status: errorStatus,
|
|
3047
|
-
headers: {
|
|
3048
|
-
"content-type": "application/json"
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
);
|
|
3365
|
+
return createFailureResponse(error, errorStatus);
|
|
3052
3366
|
}
|
|
3053
3367
|
};
|
|
3054
3368
|
}
|
|
@@ -3077,6 +3391,7 @@ var import_zod5 = require("zod");
|
|
|
3077
3391
|
|
|
3078
3392
|
// src/execution/execute.ts
|
|
3079
3393
|
var import_core4 = require("@kortyx/core");
|
|
3394
|
+
var import_errors5 = require("@kortyx/core/errors");
|
|
3080
3395
|
var import_runtime3 = require("@kortyx/runtime");
|
|
3081
3396
|
var import_zod3 = require("zod");
|
|
3082
3397
|
|
|
@@ -3084,32 +3399,37 @@ var import_zod3 = require("zod");
|
|
|
3084
3399
|
var import_core3 = require("@kortyx/core");
|
|
3085
3400
|
var import_runtime2 = require("@kortyx/runtime");
|
|
3086
3401
|
|
|
3402
|
+
// src/execution/types.ts
|
|
3403
|
+
var import_errors2 = require("@kortyx/core/errors");
|
|
3404
|
+
var ExecutionRequestError = class extends import_errors2.KortyxError {
|
|
3405
|
+
name = "ExecutionRequestError";
|
|
3406
|
+
constructor(code, message, cause) {
|
|
3407
|
+
super(code, message, {
|
|
3408
|
+
category: "request",
|
|
3409
|
+
retryable: false,
|
|
3410
|
+
safeMessage: "The execution request is invalid. Check the workflow contract and request fields.",
|
|
3411
|
+
cause
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
|
|
3087
3416
|
// src/orchestrator.ts
|
|
3088
3417
|
var import_node_stream = require("stream");
|
|
3089
3418
|
var import_core2 = require("@kortyx/core");
|
|
3419
|
+
var import_errors4 = require("@kortyx/core/errors");
|
|
3090
3420
|
var import_runtime = require("@kortyx/runtime");
|
|
3091
3421
|
var import_langgraph = require("@langchain/langgraph");
|
|
3092
3422
|
|
|
3093
3423
|
// src/execution/contracts.ts
|
|
3424
|
+
var import_errors3 = require("@kortyx/core/errors");
|
|
3094
3425
|
var import_zod2 = require("zod");
|
|
3095
|
-
|
|
3096
|
-
// src/execution/types.ts
|
|
3097
|
-
var ExecutionRequestError = class extends Error {
|
|
3098
|
-
constructor(code, message) {
|
|
3099
|
-
super(message);
|
|
3100
|
-
this.code = code;
|
|
3101
|
-
}
|
|
3102
|
-
code;
|
|
3103
|
-
name = "ExecutionRequestError";
|
|
3104
|
-
};
|
|
3105
|
-
|
|
3106
|
-
// src/execution/contracts.ts
|
|
3107
3426
|
async function resolveExecutionWorkflow(select, reference) {
|
|
3108
3427
|
const id = typeof reference === "string" ? reference : reference.id;
|
|
3109
3428
|
let workflow;
|
|
3110
3429
|
try {
|
|
3111
3430
|
workflow = await select(id);
|
|
3112
|
-
} catch {
|
|
3431
|
+
} catch (error) {
|
|
3432
|
+
if ((0, import_errors3.errorProperty)(error, "code") !== "UNKNOWN_WORKFLOW") throw error;
|
|
3113
3433
|
throw new ExecutionRequestError(
|
|
3114
3434
|
"UNKNOWN_WORKFLOW",
|
|
3115
3435
|
`Workflow '${id}' is not registered.`
|
|
@@ -3139,10 +3459,8 @@ function parseExecutionInput(workflow, input) {
|
|
|
3139
3459
|
import_zod2.z.json().parse(parsed);
|
|
3140
3460
|
return parsed;
|
|
3141
3461
|
} catch (error) {
|
|
3142
|
-
throw
|
|
3143
|
-
|
|
3144
|
-
error instanceof Error ? error.message : String(error)
|
|
3145
|
-
);
|
|
3462
|
+
if (!(error instanceof import_zod2.z.ZodError)) throw error;
|
|
3463
|
+
throw new ExecutionRequestError("INVALID_INPUT", error.message, error);
|
|
3146
3464
|
}
|
|
3147
3465
|
}
|
|
3148
3466
|
async function validateExecutionOutput(state, select) {
|
|
@@ -3151,7 +3469,9 @@ async function validateExecutionOutput(state, select) {
|
|
|
3151
3469
|
const contract = state.config.executionContract;
|
|
3152
3470
|
const root = await select(contract.id);
|
|
3153
3471
|
if (root.id !== contract.id || root.version !== contract.version)
|
|
3154
|
-
throw new
|
|
3472
|
+
throw new import_errors3.WorkflowContractError(
|
|
3473
|
+
"The root workflow changed since execution started."
|
|
3474
|
+
);
|
|
3155
3475
|
let data = state.data ?? {};
|
|
3156
3476
|
try {
|
|
3157
3477
|
for (const schema of /* @__PURE__ */ new Set([terminal.outputSchema, root.outputSchema])) {
|
|
@@ -3159,10 +3479,12 @@ async function validateExecutionOutput(state, select) {
|
|
|
3159
3479
|
}
|
|
3160
3480
|
if (terminal.outputSchema || root.outputSchema) import_zod2.z.json().parse(data);
|
|
3161
3481
|
} catch (error) {
|
|
3162
|
-
|
|
3163
|
-
|
|
3482
|
+
if (!(error instanceof import_zod2.z.ZodError)) throw error;
|
|
3483
|
+
const failure = new import_errors3.ValidationError(
|
|
3484
|
+
"INVALID_OUTPUT",
|
|
3485
|
+
`Workflow output validation failed: ${error.message}`,
|
|
3486
|
+
error
|
|
3164
3487
|
);
|
|
3165
|
-
Object.assign(failure, { code: "INVALID_OUTPUT" });
|
|
3166
3488
|
throw failure;
|
|
3167
3489
|
}
|
|
3168
3490
|
return { ...state, data };
|
|
@@ -3674,7 +3996,8 @@ async function orchestrateGraphStream({
|
|
|
3674
3996
|
interruptId,
|
|
3675
3997
|
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3676
3998
|
resumeOutcome: "failed",
|
|
3677
|
-
resumeError:
|
|
3999
|
+
resumeError: (0, import_errors4.serializeFailure)(resumeError).message,
|
|
4000
|
+
failure: (0, import_errors4.serializeFailure)(resumeError)
|
|
3678
4001
|
},
|
|
3679
4002
|
flush: true
|
|
3680
4003
|
});
|
|
@@ -3745,8 +4068,10 @@ async function orchestrateGraphStream({
|
|
|
3745
4068
|
if (response.closed) outcomeState.config.responseCompleted = true;
|
|
3746
4069
|
if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
|
|
3747
4070
|
for (const [token, request] of activePendingRequests) {
|
|
3748
|
-
if (budget
|
|
3749
|
-
|
|
4071
|
+
if (budget && request.state)
|
|
4072
|
+
request.state.config.executionBudget = budget;
|
|
4073
|
+
if (response.closed && request.state)
|
|
4074
|
+
request.state.config.responseCompleted = true;
|
|
3750
4075
|
const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
|
|
3751
4076
|
frameworkAdapter.checkpointer,
|
|
3752
4077
|
runId,
|
|
@@ -3813,7 +4138,10 @@ async function orchestrateGraphStream({
|
|
|
3813
4138
|
if (pendingStore) {
|
|
3814
4139
|
const savePromise = pendingStore.save(record).catch((error) => {
|
|
3815
4140
|
outcomeError = error;
|
|
3816
|
-
console.error(
|
|
4141
|
+
console.error(
|
|
4142
|
+
"[orchestrator] failed to save pending request",
|
|
4143
|
+
(0, import_errors4.serializeFailure)(error)
|
|
4144
|
+
);
|
|
3817
4145
|
});
|
|
3818
4146
|
pendingRequestWrites.push(savePromise);
|
|
3819
4147
|
}
|
|
@@ -3885,8 +4213,13 @@ async function orchestrateGraphStream({
|
|
|
3885
4213
|
const msg = String(
|
|
3886
4214
|
payload?.message ?? "Unexpected error"
|
|
3887
4215
|
);
|
|
3888
|
-
|
|
3889
|
-
|
|
4216
|
+
const supplied = payload?.failure;
|
|
4217
|
+
outcomeError = (0, import_errors4.isFailureDescriptor)(supplied) ? (0, import_errors4.errorFromFailure)(supplied) : new Error(msg);
|
|
4218
|
+
write({
|
|
4219
|
+
type: "error",
|
|
4220
|
+
message: (0, import_errors4.serializeFailure)(outcomeError).message,
|
|
4221
|
+
failure: (0, import_errors4.serializeFailure)(outcomeError)
|
|
4222
|
+
});
|
|
3890
4223
|
write({ type: "done" });
|
|
3891
4224
|
finished = true;
|
|
3892
4225
|
out.end();
|
|
@@ -4058,7 +4391,10 @@ async function orchestrateGraphStream({
|
|
|
4058
4391
|
input: p?.input
|
|
4059
4392
|
};
|
|
4060
4393
|
void persistAndEmitInterrupt(local).catch((error) => {
|
|
4061
|
-
console.error(
|
|
4394
|
+
console.error(
|
|
4395
|
+
"[orchestrator] failed to emit interrupt",
|
|
4396
|
+
(0, import_errors4.serializeFailure)(error)
|
|
4397
|
+
);
|
|
4062
4398
|
});
|
|
4063
4399
|
return;
|
|
4064
4400
|
}
|
|
@@ -4245,7 +4581,8 @@ async function orchestrateGraphStream({
|
|
|
4245
4581
|
});
|
|
4246
4582
|
write({
|
|
4247
4583
|
type: "error",
|
|
4248
|
-
message:
|
|
4584
|
+
message: (0, import_errors4.serializeFailure)(err).message,
|
|
4585
|
+
failure: (0, import_errors4.serializeFailure)(err)
|
|
4249
4586
|
});
|
|
4250
4587
|
write({ type: "done" });
|
|
4251
4588
|
out.end();
|
|
@@ -4294,7 +4631,10 @@ async function orchestrateGraphStream({
|
|
|
4294
4631
|
}
|
|
4295
4632
|
}
|
|
4296
4633
|
} catch (e) {
|
|
4297
|
-
console.error(
|
|
4634
|
+
console.error(
|
|
4635
|
+
"[orchestrator] framework cleanup failed",
|
|
4636
|
+
(0, import_errors4.serializeFailure)(e)
|
|
4637
|
+
);
|
|
4298
4638
|
}
|
|
4299
4639
|
}
|
|
4300
4640
|
outcomeState = workflowFinalState;
|
|
@@ -4345,7 +4685,10 @@ async function orchestrateGraphStream({
|
|
|
4345
4685
|
});
|
|
4346
4686
|
} catch (error) {
|
|
4347
4687
|
outcomeError = error;
|
|
4348
|
-
console.error(
|
|
4688
|
+
console.error(
|
|
4689
|
+
"[orchestrator] session checkpoint failed",
|
|
4690
|
+
(0, import_errors4.serializeFailure)(error)
|
|
4691
|
+
);
|
|
4349
4692
|
}
|
|
4350
4693
|
}
|
|
4351
4694
|
write({ type: "done", data: workflowFinalState });
|
|
@@ -4414,7 +4757,10 @@ async function orchestrateGraphStream({
|
|
|
4414
4757
|
});
|
|
4415
4758
|
} catch (error) {
|
|
4416
4759
|
outcomeError = error;
|
|
4417
|
-
console.error(
|
|
4760
|
+
console.error(
|
|
4761
|
+
"[orchestrator] session checkpoint failed",
|
|
4762
|
+
(0, import_errors4.serializeFailure)(error)
|
|
4763
|
+
);
|
|
4418
4764
|
}
|
|
4419
4765
|
}
|
|
4420
4766
|
runTraceSpan.setAttributes?.({
|
|
@@ -4464,7 +4810,7 @@ async function orchestrateGraphStream({
|
|
|
4464
4810
|
try {
|
|
4465
4811
|
await frameworkAdapter?.cleanupRun?.(runId, [...namespacesUsed]);
|
|
4466
4812
|
} catch (cleanupError) {
|
|
4467
|
-
console.error("[cancel:cleanupRun]", cleanupError);
|
|
4813
|
+
console.error("[cancel:cleanupRun]", (0, import_errors4.serializeFailure)(cleanupError));
|
|
4468
4814
|
}
|
|
4469
4815
|
outcomeError = (0, import_core2.createExecutionCancelledError)();
|
|
4470
4816
|
fallbackRunSpan?.setAttributes?.({ "kortyx.run.cancelled": true });
|
|
@@ -4594,10 +4940,11 @@ async function orchestrateGraphStream({
|
|
|
4594
4940
|
outcomeError = err;
|
|
4595
4941
|
fallbackRunSpan?.fail?.(err);
|
|
4596
4942
|
emitResumeFailure(err);
|
|
4597
|
-
console.error("[error:orchestrateGraphStream]", err);
|
|
4943
|
+
console.error("[error:orchestrateGraphStream]", (0, import_errors4.serializeFailure)(err));
|
|
4598
4944
|
write({
|
|
4599
4945
|
type: "error",
|
|
4600
|
-
message:
|
|
4946
|
+
message: (0, import_errors4.serializeFailure)(err).message,
|
|
4947
|
+
failure: (0, import_errors4.serializeFailure)(err)
|
|
4601
4948
|
});
|
|
4602
4949
|
write({ type: "done" });
|
|
4603
4950
|
finished = true;
|
|
@@ -4665,12 +5012,16 @@ async function tryPrepareResumeStream({
|
|
|
4665
5012
|
if (!store) return null;
|
|
4666
5013
|
const pending = await store.get(meta.token);
|
|
4667
5014
|
if (!pending || pending.requestId !== meta.requestId) {
|
|
4668
|
-
throw new
|
|
5015
|
+
throw new ExecutionRequestError(
|
|
5016
|
+
"INVALID_RESUME",
|
|
4669
5017
|
"Interrupt is expired, already consumed, or does not match the request."
|
|
4670
5018
|
);
|
|
4671
5019
|
}
|
|
4672
5020
|
if (pending.sessionId && pending.sessionId !== sessionId)
|
|
4673
|
-
throw new
|
|
5021
|
+
throw new ExecutionRequestError(
|
|
5022
|
+
"RESUME_MISMATCH",
|
|
5023
|
+
"Interrupt belongs to another session."
|
|
5024
|
+
);
|
|
4674
5025
|
await validatePending?.(pending);
|
|
4675
5026
|
const isLimitPause = Boolean(pending.schema.meta?.__kortyxExecutionLimit);
|
|
4676
5027
|
const savedBudget = pending.state?.config?.executionBudget;
|
|
@@ -4690,7 +5041,10 @@ async function tryPrepareResumeStream({
|
|
|
4690
5041
|
})();
|
|
4691
5042
|
}
|
|
4692
5043
|
if (store.take && !await store.take(meta.token))
|
|
4693
|
-
throw new
|
|
5044
|
+
throw new ExecutionRequestError(
|
|
5045
|
+
"INVALID_RESUME",
|
|
5046
|
+
"Interrupt has already been resumed or cancelled."
|
|
5047
|
+
);
|
|
4694
5048
|
if (meta.cancel) {
|
|
4695
5049
|
await store.delete(pending.token);
|
|
4696
5050
|
emitTelemetryEvent({
|
|
@@ -4878,10 +5232,7 @@ function resultFromOutcome(outcome, runId, sessionId) {
|
|
|
4878
5232
|
return {
|
|
4879
5233
|
...info,
|
|
4880
5234
|
status: "failed",
|
|
4881
|
-
error:
|
|
4882
|
-
code: typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "EXECUTION_FAILED",
|
|
4883
|
-
message: error instanceof Error ? error.message : String(error)
|
|
4884
|
-
}
|
|
5235
|
+
error: (0, import_errors5.serializeFailure)(error)
|
|
4885
5236
|
};
|
|
4886
5237
|
}
|
|
4887
5238
|
if (outcome.cancelled)
|
|
@@ -5055,10 +5406,11 @@ async function resumeWorkflow(services, args) {
|
|
|
5055
5406
|
);
|
|
5056
5407
|
}).catch(
|
|
5057
5408
|
(error) => reject(
|
|
5058
|
-
error instanceof
|
|
5059
|
-
"
|
|
5060
|
-
|
|
5061
|
-
|
|
5409
|
+
error instanceof Error ? error : new import_errors5.KortyxError("RESUME_FAILED", "Resume failed.", {
|
|
5410
|
+
category: "persistence",
|
|
5411
|
+
cause: error,
|
|
5412
|
+
safeMessage: "The saved execution could not be resumed."
|
|
5413
|
+
})
|
|
5062
5414
|
)
|
|
5063
5415
|
);
|
|
5064
5416
|
});
|
|
@@ -5662,6 +6014,7 @@ function createAgent(args) {
|
|
|
5662
6014
|
createAgent,
|
|
5663
6015
|
createChatRouteHandler,
|
|
5664
6016
|
createCheckpointRouteHandler,
|
|
6017
|
+
createFailureResponse,
|
|
5665
6018
|
extractLatestUserMessage,
|
|
5666
6019
|
handleChatRequestBody,
|
|
5667
6020
|
handleCheckpointRequestBody,
|
|
@@ -5670,6 +6023,7 @@ function createAgent(args) {
|
|
|
5670
6023
|
parseCheckpointRequestBody,
|
|
5671
6024
|
parseResumeMeta,
|
|
5672
6025
|
projectWorkflowTopology,
|
|
6026
|
+
readRequestJson,
|
|
5673
6027
|
streamChat,
|
|
5674
6028
|
streamChatFromRoute,
|
|
5675
6029
|
transformGraphStreamForUI,
|