@kortyx/agent 0.21.0 → 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/dist/index.js CHANGED
@@ -309,13 +309,18 @@ var require_dist = __commonJS({
309
309
  workflowState: args.workflowState
310
310
  }
311
311
  });
312
+ var import_errors6 = require("@kortyx/core/errors");
312
313
  var parseWithSchema = (schema, value, label) => {
313
314
  if (!schema) return value;
314
315
  const parsed = schema.safeParse(value);
315
316
  if (parsed.success) return parsed.data;
316
317
  const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
317
318
  const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
318
- throw new Error(`${label} validation failed: ${reason}`);
319
+ throw new import_errors6.ValidationError(
320
+ label === "useReason output" ? "MODEL_OUTPUT_SCHEMA" : "SCHEMA_VALIDATION",
321
+ `${label} validation failed: ${reason}`,
322
+ parsedError
323
+ );
319
324
  };
320
325
  var awaitInterruptInternal = (args) => {
321
326
  const ctx = getHookContext();
@@ -443,6 +448,21 @@ var require_dist = __commonJS({
443
448
  var import_core32 = require("@kortyx/core");
444
449
  var import_node_crypto22 = require("crypto");
445
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
+ }
446
466
  var toError = (value) => value instanceof Error ? value : new Error(String(value));
447
467
  var createRuntimeId = () => {
448
468
  try {
@@ -556,15 +576,18 @@ var require_dist = __commonJS({
556
576
  };
557
577
  const failTrace = (span, error) => {
558
578
  const detail = error;
559
- span?.fail?.(error, {
560
- ...detail?.usage ? { usage: detail.usage } : {},
561
- ...detail?.finishReason ? { finishReason: detail.finishReason } : {},
562
- ...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
563
- attributes: {
564
- providerId: args.model.provider.id,
565
- modelId: args.model.modelId
566
- }
567
- });
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
+ }
568
591
  };
569
592
  try {
570
593
  if (stream) {
@@ -649,6 +672,8 @@ var require_dist = __commonJS({
649
672
  }
650
673
  }
651
674
  }
675
+ (0, import_core22.throwIfExecutionAborted)(abortSignal);
676
+ assertModelFinish({ finishReason, usage });
652
677
  if (emit) {
653
678
  emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
654
679
  ...commonMeta
@@ -670,6 +695,7 @@ var require_dist = __commonJS({
670
695
  }
671
696
  const response = await model.invoke(messages);
672
697
  (0, import_core22.throwIfExecutionAborted)(abortSignal);
698
+ assertModelFinish(response);
673
699
  if (response.content) args.onTextChunk?.(response.content);
674
700
  if (emit) {
675
701
  emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
@@ -1459,6 +1485,7 @@ var require_dist = __commonJS({
1459
1485
  }
1460
1486
  };
1461
1487
  }
1488
+ var import_errors32 = require("@kortyx/core/errors");
1462
1489
  var tryJsonParse = (value) => {
1463
1490
  try {
1464
1491
  return { ok: true, value: JSON.parse(value) };
@@ -1496,132 +1523,135 @@ var require_dist = __commonJS({
1496
1523
  }
1497
1524
  return text;
1498
1525
  };
1499
- var looksLikeStructuredJsonAttempt = (value) => {
1500
- const trimmed = value.trim();
1501
- if (!trimmed) return false;
1502
- return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
1503
- };
1504
- var isLikelyTruncatedStructuredOutput = (text) => {
1505
- const trimmed = text.trim();
1506
- if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
1507
- const direct = tryJsonParse(trimmed);
1508
- if (!direct.ok && direct.error) return true;
1509
- const block = extractJsonCodeBlock(trimmed);
1510
- if (!block) return false;
1511
- const parsedBlock = tryJsonParse(block);
1512
- return !parsedBlock.ok && Boolean(parsedBlock.error);
1513
- };
1514
- var createTruncatedStructuredOutputError = (label) => new Error(
1515
- `${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
1516
1530
  );
1517
- var createInvalidStructuredOutputError = (label) => new Error(
1518
- `${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. This often happens when output is truncated or the provider does not follow JSON mode. Increase maxOutputTokens or simplify the requested output.`
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
1519
1535
  );
1520
1536
  var parseReasonOutputWithSchema = (args) => {
1521
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;
1522
1540
  try {
1523
1541
  return parseWithSchema(args.schema, candidate, args.label);
1524
1542
  } catch (error) {
1525
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1526
- throw createTruncatedStructuredOutputError(args.label);
1527
- }
1528
- if (typeof candidate === "string" && candidate.trim().length > 0) {
1529
- throw createInvalidStructuredOutputError(args.label);
1530
- }
1531
- throw error;
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;
1532
1551
  }
1533
1552
  };
1534
1553
  var parseInterruptFirstPassResult = (args) => {
1535
- const candidate = resolveOutputCandidate(args.text);
1536
- if (!isRecord22(candidate)) {
1537
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1538
- throw createTruncatedStructuredOutputError("useReason output");
1539
- }
1540
- if (typeof candidate === "string" && candidate.trim().length > 0) {
1541
- throw createInvalidStructuredOutputError("useReason output");
1542
- }
1543
- throw new Error(
1544
- "useReason first pass with interrupt must return a JSON object."
1545
- );
1546
- }
1547
- const mode = args.mode ?? "required";
1548
- let interruptRequired = true;
1549
- if (mode === "optional") {
1550
- if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
1551
- throw new Error(
1552
- '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."
1553
1566
  );
1554
1567
  }
1555
- interruptRequired = candidate.decision === "interrupt";
1556
- if (!interruptRequired) {
1557
- const hasUnexpectedRequest = [
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 = [
1558
1595
  candidate.interruptRequest,
1559
1596
  candidate.request,
1560
- candidate.interrupt
1561
- ].some((value) => value !== null && value !== void 0);
1562
- if (hasUnexpectedRequest) {
1563
- throw new Error(
1564
- 'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
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"
1565
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;
1566
1624
  }
1567
1625
  }
1568
- }
1569
- let request;
1570
- if (interruptRequired) {
1571
- const requestCandidates = [
1572
- candidate.interruptRequest,
1573
- candidate.request,
1574
- candidate.interrupt,
1575
- mode === "required" ? candidate : void 0
1576
- ];
1577
- const requestPayload = requestCandidates.find(
1578
- (value) => value !== null && value !== void 0
1579
- );
1580
- request = parseWithSchema(
1581
- args.requestSchema,
1582
- requestPayload,
1583
- "useReason interrupt.request"
1584
- );
1585
- }
1586
- let output;
1587
- if (args.outputSchema) {
1588
- const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
1589
- try {
1590
- output = parseWithSchema(
1591
- args.outputSchema,
1592
- outputPayload,
1593
- "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."
1594
1631
  );
1595
- } catch (error) {
1596
- if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
1597
- throw createTruncatedStructuredOutputError("useReason output");
1598
- }
1599
- if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
1600
- throw createInvalidStructuredOutputError("useReason output");
1601
- }
1602
- throw error;
1603
1632
  }
1604
- }
1605
- const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
1606
- if (!args.outputSchema && !explicitDraft) {
1607
- throw new Error(
1608
- "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
1609
- );
1610
- }
1611
- const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
1612
- 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
+ }
1613
1641
  return {
1614
1642
  draftText,
1615
- interruptRequired: false,
1643
+ interruptRequired: true,
1644
+ request,
1616
1645
  ...output !== void 0 ? { output } : {}
1617
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;
1618
1654
  }
1619
- return {
1620
- draftText,
1621
- interruptRequired: true,
1622
- request,
1623
- ...output !== void 0 ? { output } : {}
1624
- };
1625
1655
  };
1626
1656
  var import_zod32 = require("zod");
1627
1657
  var tryStringify2 = (value) => {
@@ -1829,7 +1859,7 @@ ${tryStringify2(args.response)}`
1829
1859
  });
1830
1860
  };
1831
1861
  var import_core42 = require("@kortyx/core");
1832
- var toErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
1862
+ var import_errors42 = require("@kortyx/core/errors");
1833
1863
  var toToolDefinitions = (tools) => tools.map((tool) => ({
1834
1864
  name: tool.name,
1835
1865
  ...tool.title ? { title: tool.title } : {},
@@ -1863,17 +1893,27 @@ ${tryStringify2(args.response)}`
1863
1893
  ...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
1864
1894
  };
1865
1895
  };
1866
- var closeOwnedTools = async (tools) => {
1896
+ var closeOwnedTools = async (tools, preserveFailure = false, traceSpan) => {
1867
1897
  const closeFns = /* @__PURE__ */ new Set();
1868
1898
  for (const tool of tools) {
1869
1899
  if (tool.closeAfterUse === false || !tool.close) continue;
1870
1900
  closeFns.add(tool.close);
1871
1901
  }
1872
- await Promise.all(
1873
- [...closeFns].map(async (close) => {
1874
- await close();
1875
- })
1876
- );
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
+ }
1877
1917
  };
1878
1918
  var createInitialMessages = (args) => [
1879
1919
  ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
@@ -1890,6 +1930,7 @@ ${tryStringify2(args.response)}`
1890
1930
  const saved = ctx.currentNodeState.byKey[checkpointKey];
1891
1931
  const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
1892
1932
  const opId = checkpoint?.opId ?? args.opId;
1933
+ let primaryFailure = false;
1893
1934
  const tools = useReasonArgs.tools ?? [];
1894
1935
  const toolByName = /* @__PURE__ */ new Map();
1895
1936
  let validationCompleted = false;
@@ -1922,7 +1963,7 @@ ${tryStringify2(args.response)}`
1922
1963
  validationCompleted = true;
1923
1964
  } finally {
1924
1965
  if (!validationCompleted) {
1925
- await closeOwnedTools(tools);
1966
+ await closeOwnedTools(tools, true, traceSpan);
1926
1967
  }
1927
1968
  }
1928
1969
  const toolDefinitions = toToolDefinitions(tools);
@@ -2041,6 +2082,7 @@ ${tryStringify2(args.response)}`
2041
2082
  text: step.text,
2042
2083
  schema: useReasonArgs.outputSchema,
2043
2084
  ...step.finishReason ? { finishReason: step.finishReason } : {},
2085
+ ...aggregatedUsage ? { usage: aggregatedUsage } : {},
2044
2086
  label: "useReason output"
2045
2087
  });
2046
2088
  aggregatedWarnings = mergeWarnings(aggregatedWarnings, [
@@ -2128,12 +2170,12 @@ ${tryStringify2(args.response)}`
2128
2170
  options: [
2129
2171
  { id: "approve", label: "Approve" },
2130
2172
  { id: "deny", label: "Deny" }
2131
- ],
2132
- meta: {
2133
- tool: tool.name,
2134
- toolCallId: toolCall.id,
2135
- input: toolCall.input
2136
- }
2173
+ ]
2174
+ },
2175
+ meta: {
2176
+ tool: tool.name,
2177
+ toolCallId: toolCall.id,
2178
+ input: toolCall.input
2137
2179
  }
2138
2180
  });
2139
2181
  const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
@@ -2195,12 +2237,12 @@ ${tryStringify2(args.response)}`
2195
2237
  });
2196
2238
  } catch (error) {
2197
2239
  (0, import_core42.throwIfExecutionAborted)(abortSignal);
2198
- if ((0, import_core42.isExecutionLimitReached)(error) || (0, import_core42.isExecutionCancelled)(error))
2199
- throw error;
2240
+ if ((0, import_errors42.isControlFlowError)(error)) throw error;
2200
2241
  const result2 = {
2201
2242
  toolCallId: toolCall.id,
2202
2243
  name: tool.name,
2203
- content: toErrorMessage2(error),
2244
+ content: (0, import_errors42.serializeFailure)(error).message,
2245
+ failure: (0, import_errors42.serializeFailure)(error),
2204
2246
  isError: true
2205
2247
  };
2206
2248
  toolResults.push(result2);
@@ -2209,14 +2251,16 @@ ${tryStringify2(args.response)}`
2209
2251
  emitToolEvent("tool-call-error", {
2210
2252
  tool: tool.name,
2211
2253
  toolCallId: toolCall.id,
2212
- message: result2.content
2254
+ message: result2.content,
2255
+ failure: result2.failure
2213
2256
  });
2214
2257
  }
2215
2258
  traceSpan?.addEvent?.("useReason.tool-call.error", {
2216
2259
  stepIndex,
2217
2260
  tool: tool.name,
2218
2261
  toolCallId: toolCall.id,
2219
- message: result2.content
2262
+ message: result2.content,
2263
+ failure: result2.failure
2220
2264
  });
2221
2265
  }
2222
2266
  const completed2 = toolResults.at(-1);
@@ -2243,6 +2287,7 @@ ${tryStringify2(args.response)}`
2243
2287
  text: finalText,
2244
2288
  schema: useReasonArgs.outputSchema,
2245
2289
  ...finalFinishReason ? { finishReason: finalFinishReason } : {},
2290
+ ...aggregatedUsage ? { usage: aggregatedUsage } : {},
2246
2291
  label: "useReason output"
2247
2292
  });
2248
2293
  }
@@ -2289,15 +2334,19 @@ ${tryStringify2(args.response)}`
2289
2334
  ctx.stateDirty = true;
2290
2335
  return result;
2291
2336
  } catch (error) {
2292
- traceSpan?.fail?.(error, {
2293
- attributes: {
2294
- toolStepCount: steps.length,
2295
- toolCallCount: allToolCalls.length
2296
- }
2297
- });
2337
+ primaryFailure = true;
2338
+ try {
2339
+ traceSpan?.fail?.(error, {
2340
+ attributes: {
2341
+ toolStepCount: steps.length,
2342
+ toolCallCount: allToolCalls.length
2343
+ }
2344
+ });
2345
+ } catch {
2346
+ }
2298
2347
  throw error;
2299
2348
  } finally {
2300
- await closeOwnedTools(tools);
2349
+ await closeOwnedTools(tools, primaryFailure, traceSpan);
2301
2350
  }
2302
2351
  };
2303
2352
  var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
@@ -2460,6 +2509,7 @@ ${tryStringify2(args.response)}`
2460
2509
  text: first.text,
2461
2510
  requestSchema: args.interrupt.requestSchema,
2462
2511
  ...first.finishReason ? { finishReason: first.finishReason } : {},
2512
+ ...first.usage ? { usage: first.usage } : {},
2463
2513
  ...args.outputSchema ? { outputSchema: args.outputSchema } : {},
2464
2514
  mode: args.interrupt.mode ?? "required"
2465
2515
  });
@@ -2477,6 +2527,7 @@ ${tryStringify2(args.response)}`
2477
2527
  text: first.text,
2478
2528
  schema: args.outputSchema,
2479
2529
  ...first.finishReason ? { finishReason: first.finishReason } : {},
2530
+ ...first.usage ? { usage: first.usage } : {},
2480
2531
  label: "useReason output"
2481
2532
  });
2482
2533
  }
@@ -2571,6 +2622,7 @@ ${tryStringify2(args.response)}`
2571
2622
  text: finalText,
2572
2623
  schema: args.outputSchema,
2573
2624
  ...second.finishReason ? { finishReason: second.finishReason } : {},
2625
+ ...aggregatedUsage ? { usage: aggregatedUsage } : {},
2574
2626
  label: "useReason output"
2575
2627
  });
2576
2628
  }
@@ -2674,7 +2726,9 @@ ${tryStringify2(args.response)}`
2674
2726
  return [getValue(), setValue];
2675
2727
  }
2676
2728
  var import_core62 = require("@kortyx/core");
2729
+ var import_errors52 = require("@kortyx/core/errors");
2677
2730
  var ParallelChildWaiting = class extends Error {
2731
+ name = "ParallelChildWaiting";
2678
2732
  };
2679
2733
  var ParallelError = class extends AggregateError {
2680
2734
  constructor(results) {
@@ -2686,8 +2740,23 @@ ${tryStringify2(args.response)}`
2686
2740
  );
2687
2741
  this.results = results;
2688
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
+ });
2689
2753
  }
2690
2754
  results;
2755
+ code = "PARALLEL_FAILED";
2756
+ failure;
2757
+ toJSON() {
2758
+ return (0, import_errors52.serializeFailure)(this);
2759
+ }
2691
2760
  };
2692
2761
  function registerWorkflowTask(id) {
2693
2762
  const ctx = getHookContext();
@@ -2825,6 +2894,7 @@ ${tryStringify2(args.response)}`
2825
2894
  }
2826
2895
  var import_node_crypto5 = require("crypto");
2827
2896
  var import_core7 = require("@kortyx/core");
2897
+ var import_errors62 = require("@kortyx/core/errors");
2828
2898
  var import_node_crypto42 = require("crypto");
2829
2899
  function emitWorkflowCall2(telemetry, type, payload) {
2830
2900
  const base = telemetry?.correlation;
@@ -2862,10 +2932,20 @@ ${tryStringify2(args.response)}`
2862
2932
  const serialized = JSON.stringify(value);
2863
2933
  return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
2864
2934
  }
2865
- var WorkflowCallError = class extends Error {
2866
- constructor(workflow, message) {
2867
- super(`Workflow '${workflow}': ${message}`);
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
+ });
2868
2945
  this.workflow = workflow;
2946
+ Object.assign(this.failure, detail, {
2947
+ context: { ...detail.context, workflow }
2948
+ });
2869
2949
  this.name = "WorkflowCallError";
2870
2950
  }
2871
2951
  workflow;
@@ -2876,10 +2956,12 @@ ${tryStringify2(args.response)}`
2876
2956
  if (v === null || typeof v === "string" || typeof v === "boolean") return v;
2877
2957
  if (typeof v === "number" && Number.isFinite(v)) return v;
2878
2958
  if (typeof v !== "object" || !v || seen.has(v)) {
2879
- throw new Error("Workflow input and output must be JSON serializable.");
2959
+ throw new import_errors62.WorkflowContractError(
2960
+ "Workflow input and output must be JSON serializable."
2961
+ );
2880
2962
  }
2881
2963
  if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
2882
- throw new Error(
2964
+ throw new import_errors62.WorkflowContractError(
2883
2965
  "Workflow input and output must contain plain JSON objects."
2884
2966
  );
2885
2967
  }
@@ -2900,7 +2982,9 @@ ${tryStringify2(args.response)}`
2900
2982
  useWorkflow: (args) => {
2901
2983
  const workflow = workflows[args.workflow];
2902
2984
  if (!workflow || workflow.id !== args.workflow)
2903
- throw new Error("Workflow registry keys must match definition ids.");
2985
+ throw new import_errors62.WorkflowContractError(
2986
+ "Workflow registry keys must match definition ids."
2987
+ );
2904
2988
  return useWorkflow({ ...args, workflow });
2905
2989
  }
2906
2990
  };
@@ -2924,19 +3008,23 @@ ${tryStringify2(args.response)}`
2924
3008
  (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
2925
3009
  const service = ctx.node.callWorkflow;
2926
3010
  if (!service)
2927
- throw new Error("useWorkflow requires an agent workflow registry.");
3011
+ throw new import_errors62.WorkflowContractError(
3012
+ "useWorkflow requires an agent workflow registry."
3013
+ );
2928
3014
  if (!args.id.trim())
2929
- throw new Error("useWorkflow requires a stable, nonempty id.");
3015
+ throw new import_errors62.WorkflowContractError(
3016
+ "useWorkflow requires a stable, nonempty id."
3017
+ );
2930
3018
  if (ctx.workflowCallActive && !task.group)
2931
- throw new Error(
3019
+ throw new import_errors62.WorkflowContractError(
2932
3020
  "Concurrent workflow calls require parallel([...]); otherwise await each call."
2933
3021
  );
2934
3022
  if (ctx.workflowCallIds.has(args.id))
2935
- throw new Error(
3023
+ throw new import_errors62.WorkflowContractError(
2936
3024
  `Duplicate workflow call id '${args.id}' in one node activation.`
2937
3025
  );
2938
3026
  if (ctx.workflowCallIds.size >= 64)
2939
- throw new Error(
3027
+ throw new import_errors62.WorkflowContractError(
2940
3028
  "A node may call at most 64 child workflows per activation."
2941
3029
  );
2942
3030
  ctx.workflowCallIds.add(args.id);
@@ -3013,7 +3101,12 @@ ${tryStringify2(args.response)}`
3013
3101
  });
3014
3102
  }
3015
3103
  if (record.status === "failed")
3016
- throw new WorkflowCallError(workflow, record.error ?? "child failed");
3104
+ throw new WorkflowCallError(
3105
+ workflow,
3106
+ record.error ?? "child failed",
3107
+ void 0,
3108
+ record.failure ?? (0, import_errors62.serializeFailure)(void 0)
3109
+ );
3017
3110
  if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
3018
3111
  throw new ParallelChildWaiting();
3019
3112
  while (record.status !== "completed") {
@@ -3048,9 +3141,15 @@ ${tryStringify2(args.response)}`
3048
3141
  throw error;
3049
3142
  }
3050
3143
  record.status = "failed";
3051
- record.error = error instanceof Error ? error.message : String(error);
3144
+ record.failure = (0, import_errors62.serializeFailure)(error);
3145
+ record.error = record.failure.message;
3052
3146
  report("failed", { error: "Child workflow failed" });
3053
- throw new WorkflowCallError(workflow, record.error);
3147
+ throw new WorkflowCallError(
3148
+ workflow,
3149
+ record.error,
3150
+ error,
3151
+ record.failure
3152
+ );
3054
3153
  }
3055
3154
  (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
3056
3155
  recordUsage(outcome.usage);
@@ -3100,6 +3199,7 @@ __export(index_exports, {
3100
3199
  createAgent: () => createAgent,
3101
3200
  createChatRouteHandler: () => createChatRouteHandler,
3102
3201
  createCheckpointRouteHandler: () => createCheckpointRouteHandler,
3202
+ createFailureResponse: () => createFailureResponse,
3103
3203
  extractLatestUserMessage: () => extractLatestUserMessage,
3104
3204
  handleChatRequestBody: () => handleChatRequestBody,
3105
3205
  handleCheckpointRequestBody: () => handleCheckpointRequestBody,
@@ -3108,6 +3208,7 @@ __export(index_exports, {
3108
3208
  parseCheckpointRequestBody: () => parseCheckpointRequestBody,
3109
3209
  parseResumeMeta: () => parseResumeMeta,
3110
3210
  projectWorkflowTopology: () => projectWorkflowTopology,
3211
+ readRequestJson: () => readRequestJson,
3111
3212
  streamChat: () => streamChat,
3112
3213
  streamChatFromRoute: () => streamChatFromRoute,
3113
3214
  transformGraphStreamForUI: () => transformGraphStreamForUI,
@@ -3116,6 +3217,7 @@ __export(index_exports, {
3116
3217
  module.exports = __toCommonJS(index_exports);
3117
3218
 
3118
3219
  // src/adapters/http.ts
3220
+ var import_errors = require("@kortyx/core/errors");
3119
3221
  var import_stream = require("@kortyx/stream");
3120
3222
  var import_zod = require("zod");
3121
3223
  var chatMessageSchema = import_zod.z.object({
@@ -3151,12 +3253,41 @@ var checkpointRequestBodySchema = import_zod.z.discriminatedUnion("action", [
3151
3253
  newSessionId: import_zod.z.string().min(1).optional()
3152
3254
  }).strict()
3153
3255
  ]);
3154
- var toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
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
+ }
3155
3282
  function parseChatRequestBody(value) {
3156
3283
  const parsed = chatRequestBodySchema.safeParse(value);
3157
3284
  if (!parsed.success) {
3158
3285
  const [firstIssue] = parsed.error.issues;
3159
- throw new Error(firstIssue.message);
3286
+ throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
3287
+ category: "request",
3288
+ retryable: false,
3289
+ safeMessage: "The request body is invalid."
3290
+ });
3160
3291
  }
3161
3292
  const sessionId = parsed.data.sessionId?.trim();
3162
3293
  const workflowId = parsed.data.workflowId?.trim();
@@ -3172,7 +3303,11 @@ function parseCheckpointRequestBody(value) {
3172
3303
  const parsed = checkpointRequestBodySchema.safeParse(value);
3173
3304
  if (parsed.success) return parsed.data;
3174
3305
  const [firstIssue] = parsed.error.issues;
3175
- throw new Error(firstIssue.message);
3306
+ throw new import_errors.KortyxError("INVALID_REQUEST", firstIssue.message, {
3307
+ category: "request",
3308
+ retryable: false,
3309
+ safeMessage: "The request body is invalid."
3310
+ });
3176
3311
  }
3177
3312
  async function handleChatRequestBody(args) {
3178
3313
  const { agent, body } = args;
@@ -3194,10 +3329,10 @@ async function handleChatRequestBody(args) {
3194
3329
  return (0, import_stream.toSSE)(stream);
3195
3330
  }
3196
3331
  function createChatRouteHandler(args) {
3197
- const { agent, errorStatus = 400 } = args;
3332
+ const { agent, errorStatus } = args;
3198
3333
  return async function POST(request) {
3199
3334
  try {
3200
- const body = parseChatRequestBody(await request.json());
3335
+ const body = parseChatRequestBody(await readRequestJson(request));
3201
3336
  return await handleChatRequestBody({
3202
3337
  agent,
3203
3338
  body,
@@ -3205,17 +3340,7 @@ function createChatRouteHandler(args) {
3205
3340
  onExecution: args.onExecution
3206
3341
  });
3207
3342
  } catch (error) {
3208
- return new Response(
3209
- JSON.stringify({
3210
- error: toErrorMessage(error)
3211
- }),
3212
- {
3213
- status: errorStatus,
3214
- headers: {
3215
- "content-type": "application/json"
3216
- }
3217
- }
3218
- );
3343
+ return createFailureResponse(error, errorStatus);
3219
3344
  }
3220
3345
  };
3221
3346
  }
@@ -3231,23 +3356,13 @@ async function handleCheckpointRequestBody(args) {
3231
3356
  });
3232
3357
  }
3233
3358
  function createCheckpointRouteHandler(args) {
3234
- const { agent, errorStatus = 400 } = args;
3359
+ const { agent, errorStatus } = args;
3235
3360
  return async function POST(request) {
3236
3361
  try {
3237
- const body = parseCheckpointRequestBody(await request.json());
3362
+ const body = parseCheckpointRequestBody(await readRequestJson(request));
3238
3363
  return await handleCheckpointRequestBody({ agent, body });
3239
3364
  } catch (error) {
3240
- return new Response(
3241
- JSON.stringify({
3242
- error: toErrorMessage(error)
3243
- }),
3244
- {
3245
- status: errorStatus,
3246
- headers: {
3247
- "content-type": "application/json"
3248
- }
3249
- }
3250
- );
3365
+ return createFailureResponse(error, errorStatus);
3251
3366
  }
3252
3367
  };
3253
3368
  }
@@ -3276,6 +3391,7 @@ var import_zod5 = require("zod");
3276
3391
 
3277
3392
  // src/execution/execute.ts
3278
3393
  var import_core4 = require("@kortyx/core");
3394
+ var import_errors5 = require("@kortyx/core/errors");
3279
3395
  var import_runtime3 = require("@kortyx/runtime");
3280
3396
  var import_zod3 = require("zod");
3281
3397
 
@@ -3283,32 +3399,37 @@ var import_zod3 = require("zod");
3283
3399
  var import_core3 = require("@kortyx/core");
3284
3400
  var import_runtime2 = require("@kortyx/runtime");
3285
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
+
3286
3416
  // src/orchestrator.ts
3287
3417
  var import_node_stream = require("stream");
3288
3418
  var import_core2 = require("@kortyx/core");
3419
+ var import_errors4 = require("@kortyx/core/errors");
3289
3420
  var import_runtime = require("@kortyx/runtime");
3290
3421
  var import_langgraph = require("@langchain/langgraph");
3291
3422
 
3292
3423
  // src/execution/contracts.ts
3424
+ var import_errors3 = require("@kortyx/core/errors");
3293
3425
  var import_zod2 = require("zod");
3294
-
3295
- // src/execution/types.ts
3296
- var ExecutionRequestError = class extends Error {
3297
- constructor(code, message) {
3298
- super(message);
3299
- this.code = code;
3300
- }
3301
- code;
3302
- name = "ExecutionRequestError";
3303
- };
3304
-
3305
- // src/execution/contracts.ts
3306
3426
  async function resolveExecutionWorkflow(select, reference) {
3307
3427
  const id = typeof reference === "string" ? reference : reference.id;
3308
3428
  let workflow;
3309
3429
  try {
3310
3430
  workflow = await select(id);
3311
- } catch {
3431
+ } catch (error) {
3432
+ if ((0, import_errors3.errorProperty)(error, "code") !== "UNKNOWN_WORKFLOW") throw error;
3312
3433
  throw new ExecutionRequestError(
3313
3434
  "UNKNOWN_WORKFLOW",
3314
3435
  `Workflow '${id}' is not registered.`
@@ -3338,10 +3459,8 @@ function parseExecutionInput(workflow, input) {
3338
3459
  import_zod2.z.json().parse(parsed);
3339
3460
  return parsed;
3340
3461
  } catch (error) {
3341
- throw new ExecutionRequestError(
3342
- "INVALID_INPUT",
3343
- error instanceof Error ? error.message : String(error)
3344
- );
3462
+ if (!(error instanceof import_zod2.z.ZodError)) throw error;
3463
+ throw new ExecutionRequestError("INVALID_INPUT", error.message, error);
3345
3464
  }
3346
3465
  }
3347
3466
  async function validateExecutionOutput(state, select) {
@@ -3350,7 +3469,9 @@ async function validateExecutionOutput(state, select) {
3350
3469
  const contract = state.config.executionContract;
3351
3470
  const root = await select(contract.id);
3352
3471
  if (root.id !== contract.id || root.version !== contract.version)
3353
- throw new Error("The root workflow changed since execution started.");
3472
+ throw new import_errors3.WorkflowContractError(
3473
+ "The root workflow changed since execution started."
3474
+ );
3354
3475
  let data = state.data ?? {};
3355
3476
  try {
3356
3477
  for (const schema of /* @__PURE__ */ new Set([terminal.outputSchema, root.outputSchema])) {
@@ -3358,10 +3479,12 @@ async function validateExecutionOutput(state, select) {
3358
3479
  }
3359
3480
  if (terminal.outputSchema || root.outputSchema) import_zod2.z.json().parse(data);
3360
3481
  } catch (error) {
3361
- const failure = new Error(
3362
- `Workflow output validation failed: ${error instanceof Error ? error.message : String(error)}`
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
3363
3487
  );
3364
- Object.assign(failure, { code: "INVALID_OUTPUT" });
3365
3488
  throw failure;
3366
3489
  }
3367
3490
  return { ...state, data };
@@ -3873,7 +3996,8 @@ async function orchestrateGraphStream({
3873
3996
  interruptId,
3874
3997
  resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
3875
3998
  resumeOutcome: "failed",
3876
- resumeError: resumeError instanceof Error ? resumeError.message : String(resumeError)
3999
+ resumeError: (0, import_errors4.serializeFailure)(resumeError).message,
4000
+ failure: (0, import_errors4.serializeFailure)(resumeError)
3877
4001
  },
3878
4002
  flush: true
3879
4003
  });
@@ -3944,8 +4068,10 @@ async function orchestrateGraphStream({
3944
4068
  if (response.closed) outcomeState.config.responseCompleted = true;
3945
4069
  if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
3946
4070
  for (const [token, request] of activePendingRequests) {
3947
- if (budget) request.state.config.executionBudget = budget;
3948
- if (response.closed) request.state.config.responseCompleted = true;
4071
+ if (budget && request.state)
4072
+ request.state.config.executionBudget = budget;
4073
+ if (response.closed && request.state)
4074
+ request.state.config.responseCompleted = true;
3949
4075
  const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
3950
4076
  frameworkAdapter.checkpointer,
3951
4077
  runId,
@@ -4012,7 +4138,10 @@ async function orchestrateGraphStream({
4012
4138
  if (pendingStore) {
4013
4139
  const savePromise = pendingStore.save(record).catch((error) => {
4014
4140
  outcomeError = error;
4015
- console.error("[orchestrator] failed to save pending request", error);
4141
+ console.error(
4142
+ "[orchestrator] failed to save pending request",
4143
+ (0, import_errors4.serializeFailure)(error)
4144
+ );
4016
4145
  });
4017
4146
  pendingRequestWrites.push(savePromise);
4018
4147
  }
@@ -4084,8 +4213,13 @@ async function orchestrateGraphStream({
4084
4213
  const msg = String(
4085
4214
  payload?.message ?? "Unexpected error"
4086
4215
  );
4087
- outcomeError = new Error(msg);
4088
- write({ type: "error", message: msg });
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
+ });
4089
4223
  write({ type: "done" });
4090
4224
  finished = true;
4091
4225
  out.end();
@@ -4257,7 +4391,10 @@ async function orchestrateGraphStream({
4257
4391
  input: p?.input
4258
4392
  };
4259
4393
  void persistAndEmitInterrupt(local).catch((error) => {
4260
- console.error("[orchestrator] failed to emit interrupt", error);
4394
+ console.error(
4395
+ "[orchestrator] failed to emit interrupt",
4396
+ (0, import_errors4.serializeFailure)(error)
4397
+ );
4261
4398
  });
4262
4399
  return;
4263
4400
  }
@@ -4444,7 +4581,8 @@ async function orchestrateGraphStream({
4444
4581
  });
4445
4582
  write({
4446
4583
  type: "error",
4447
- message: `Transition failed to '${transitionTo}': ${err instanceof Error ? err.message : String(err)}`
4584
+ message: (0, import_errors4.serializeFailure)(err).message,
4585
+ failure: (0, import_errors4.serializeFailure)(err)
4448
4586
  });
4449
4587
  write({ type: "done" });
4450
4588
  out.end();
@@ -4493,7 +4631,10 @@ async function orchestrateGraphStream({
4493
4631
  }
4494
4632
  }
4495
4633
  } catch (e) {
4496
- console.error("[orchestrator] framework cleanup failed", e);
4634
+ console.error(
4635
+ "[orchestrator] framework cleanup failed",
4636
+ (0, import_errors4.serializeFailure)(e)
4637
+ );
4497
4638
  }
4498
4639
  }
4499
4640
  outcomeState = workflowFinalState;
@@ -4544,7 +4685,10 @@ async function orchestrateGraphStream({
4544
4685
  });
4545
4686
  } catch (error) {
4546
4687
  outcomeError = error;
4547
- console.error("[orchestrator] session checkpoint failed", error);
4688
+ console.error(
4689
+ "[orchestrator] session checkpoint failed",
4690
+ (0, import_errors4.serializeFailure)(error)
4691
+ );
4548
4692
  }
4549
4693
  }
4550
4694
  write({ type: "done", data: workflowFinalState });
@@ -4613,7 +4757,10 @@ async function orchestrateGraphStream({
4613
4757
  });
4614
4758
  } catch (error) {
4615
4759
  outcomeError = error;
4616
- console.error("[orchestrator] session checkpoint failed", error);
4760
+ console.error(
4761
+ "[orchestrator] session checkpoint failed",
4762
+ (0, import_errors4.serializeFailure)(error)
4763
+ );
4617
4764
  }
4618
4765
  }
4619
4766
  runTraceSpan.setAttributes?.({
@@ -4663,7 +4810,7 @@ async function orchestrateGraphStream({
4663
4810
  try {
4664
4811
  await frameworkAdapter?.cleanupRun?.(runId, [...namespacesUsed]);
4665
4812
  } catch (cleanupError) {
4666
- console.error("[cancel:cleanupRun]", cleanupError);
4813
+ console.error("[cancel:cleanupRun]", (0, import_errors4.serializeFailure)(cleanupError));
4667
4814
  }
4668
4815
  outcomeError = (0, import_core2.createExecutionCancelledError)();
4669
4816
  fallbackRunSpan?.setAttributes?.({ "kortyx.run.cancelled": true });
@@ -4793,10 +4940,11 @@ async function orchestrateGraphStream({
4793
4940
  outcomeError = err;
4794
4941
  fallbackRunSpan?.fail?.(err);
4795
4942
  emitResumeFailure(err);
4796
- console.error("[error:orchestrateGraphStream]", err);
4943
+ console.error("[error:orchestrateGraphStream]", (0, import_errors4.serializeFailure)(err));
4797
4944
  write({
4798
4945
  type: "error",
4799
- message: err instanceof Error ? err.message : String(err)
4946
+ message: (0, import_errors4.serializeFailure)(err).message,
4947
+ failure: (0, import_errors4.serializeFailure)(err)
4800
4948
  });
4801
4949
  write({ type: "done" });
4802
4950
  finished = true;
@@ -4864,12 +5012,16 @@ async function tryPrepareResumeStream({
4864
5012
  if (!store) return null;
4865
5013
  const pending = await store.get(meta.token);
4866
5014
  if (!pending || pending.requestId !== meta.requestId) {
4867
- throw new Error(
5015
+ throw new ExecutionRequestError(
5016
+ "INVALID_RESUME",
4868
5017
  "Interrupt is expired, already consumed, or does not match the request."
4869
5018
  );
4870
5019
  }
4871
5020
  if (pending.sessionId && pending.sessionId !== sessionId)
4872
- throw new Error("Interrupt belongs to another session.");
5021
+ throw new ExecutionRequestError(
5022
+ "RESUME_MISMATCH",
5023
+ "Interrupt belongs to another session."
5024
+ );
4873
5025
  await validatePending?.(pending);
4874
5026
  const isLimitPause = Boolean(pending.schema.meta?.__kortyxExecutionLimit);
4875
5027
  const savedBudget = pending.state?.config?.executionBudget;
@@ -4889,7 +5041,10 @@ async function tryPrepareResumeStream({
4889
5041
  })();
4890
5042
  }
4891
5043
  if (store.take && !await store.take(meta.token))
4892
- throw new Error("Interrupt has already been resumed or cancelled.");
5044
+ throw new ExecutionRequestError(
5045
+ "INVALID_RESUME",
5046
+ "Interrupt has already been resumed or cancelled."
5047
+ );
4893
5048
  if (meta.cancel) {
4894
5049
  await store.delete(pending.token);
4895
5050
  emitTelemetryEvent({
@@ -5077,10 +5232,7 @@ function resultFromOutcome(outcome, runId, sessionId) {
5077
5232
  return {
5078
5233
  ...info,
5079
5234
  status: "failed",
5080
- error: {
5081
- code: typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "EXECUTION_FAILED",
5082
- message: error instanceof Error ? error.message : String(error)
5083
- }
5235
+ error: (0, import_errors5.serializeFailure)(error)
5084
5236
  };
5085
5237
  }
5086
5238
  if (outcome.cancelled)
@@ -5254,10 +5406,11 @@ async function resumeWorkflow(services, args) {
5254
5406
  );
5255
5407
  }).catch(
5256
5408
  (error) => reject(
5257
- error instanceof ExecutionRequestError ? error : new ExecutionRequestError(
5258
- "INVALID_RESUME",
5259
- error instanceof Error ? error.message : String(error)
5260
- )
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
+ })
5261
5414
  )
5262
5415
  );
5263
5416
  });
@@ -5861,6 +6014,7 @@ function createAgent(args) {
5861
6014
  createAgent,
5862
6015
  createChatRouteHandler,
5863
6016
  createCheckpointRouteHandler,
6017
+ createFailureResponse,
5864
6018
  extractLatestUserMessage,
5865
6019
  handleChatRequestBody,
5866
6020
  handleCheckpointRequestBody,
@@ -5869,6 +6023,7 @@ function createAgent(args) {
5869
6023
  parseCheckpointRequestBody,
5870
6024
  parseResumeMeta,
5871
6025
  projectWorkflowTopology,
6026
+ readRequestJson,
5872
6027
  streamChat,
5873
6028
  streamChatFromRoute,
5874
6029
  transformGraphStreamForUI,