@kortyx/agent 0.15.0 → 0.18.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
@@ -1,7 +1,12 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
5
10
  var __export = (target, all) => {
6
11
  for (var name in all)
7
12
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -12,13 +17,2628 @@ var __copyProps = (to, from, except, desc) => {
12
17
  if (!__hasOwnProp.call(to, key) && key !== except)
13
18
  __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
19
  }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // ../hooks/dist/index.js
33
+ var require_dist = __commonJS({
34
+ "../hooks/dist/index.js"(exports2, module2) {
35
+ var __defProp2 = Object.defineProperty;
36
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
37
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
38
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
39
+ var __export2 = (target, all) => {
40
+ for (var name in all)
41
+ __defProp2(target, name, { get: all[name], enumerable: true });
42
+ };
43
+ var __copyProps2 = (to, from, except, desc) => {
44
+ if (from && typeof from === "object" || typeof from === "function") {
45
+ for (let key of __getOwnPropNames2(from))
46
+ if (!__hasOwnProp2.call(to, key) && key !== except)
47
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
48
+ }
49
+ return to;
50
+ };
51
+ var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
52
+ var index_exports2 = {};
53
+ __export2(index_exports2, {
54
+ WorkflowCallError: () => WorkflowCallError,
55
+ createWorkflowHooks: () => createWorkflowHooks,
56
+ emitWorkflowCall: () => emitWorkflowCall2,
57
+ runWithHookContext: () => runWithHookContext,
58
+ useAbortSignal: () => useAbortSignal,
59
+ useInterrupt: () => useInterrupt,
60
+ useNodeState: () => useNodeState,
61
+ useReason: () => useReason2,
62
+ useRuntimeContext: () => useRuntimeContext,
63
+ useStructuredData: () => useStructuredData,
64
+ useWorkflow: () => useWorkflow,
65
+ useWorkflowState: () => useWorkflowState,
66
+ workflowCallFingerprint: () => workflowCallFingerprint
67
+ });
68
+ module2.exports = __toCommonJS2(index_exports2);
69
+ var import_node_async_hooks = require("async_hooks");
70
+ var storage = new import_node_async_hooks.AsyncLocalStorage();
71
+ var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
72
+ var normalizeNodeState = (value) => {
73
+ if (Array.isArray(value)) {
74
+ return { byIndex: [...value], byKey: {} };
75
+ }
76
+ if (isRecord5(value)) {
77
+ const byIndex = Array.isArray(value.byIndex) ? [...value.byIndex] : [];
78
+ const byKey = isRecord5(value.byKey) ? { ...value.byKey } : {};
79
+ return { byIndex, byKey };
80
+ }
81
+ return { byIndex: [], byKey: {} };
82
+ };
83
+ var cloneWorkflowState = (value) => isRecord5(value) ? { ...value } : {};
84
+ var cloneTokenUsage = (value) => {
85
+ if (!isRecord5(value)) return void 0;
86
+ const input = typeof value.input === "number" ? value.input : void 0;
87
+ const output = typeof value.output === "number" ? value.output : void 0;
88
+ const total = typeof value.total === "number" ? value.total : void 0;
89
+ if (input === void 0 && output === void 0 && total === void 0) {
90
+ return void 0;
91
+ }
92
+ return {
93
+ input: input ?? 0,
94
+ output: output ?? 0,
95
+ total: total ?? 0
96
+ };
97
+ };
98
+ var getHookRuntimeState = (state) => {
99
+ const runtime = state.runtime ?? {};
100
+ const internal = isRecord5(runtime.__kortyx) ? runtime.__kortyx : void 0;
101
+ const result = {};
102
+ if (isRecord5(internal?.nodeState)) result.nodeState = internal?.nodeState;
103
+ if (isRecord5(internal?.workflowState)) {
104
+ result.workflowState = internal?.workflowState;
105
+ }
106
+ return result;
107
+ };
108
+ var createInternalContext = (ctx) => {
109
+ const hookRuntimeState = getHookRuntimeState(ctx.state);
110
+ const nodeId = ctx.node.graph.node;
111
+ const storedNodeState = isRecord5(hookRuntimeState.nodeState) ? hookRuntimeState.nodeState : void 0;
112
+ const storedNodeId = typeof storedNodeState?.nodeId === "string" ? String(storedNodeState.nodeId) : "";
113
+ const storedState = storedNodeState?.state;
114
+ const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
115
+ return {
116
+ ...ctx,
117
+ workflowCallActive: false,
118
+ workflowCallIds: /* @__PURE__ */ new Set(),
119
+ nodeStateIndex: 0,
120
+ reasonCallIndex: 0,
121
+ currentNodeState,
122
+ workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
123
+ tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
124
+ stateDirty: false,
125
+ runtimeDirty: false
126
+ };
127
+ };
128
+ var buildRuntimeStateUpdates = (ctx) => {
129
+ const updates = {};
130
+ if (ctx.stateDirty) {
131
+ updates.__kortyx = {
132
+ nodeState: {
133
+ nodeId: ctx.node.graph.node,
134
+ state: ctx.currentNodeState
135
+ },
136
+ workflowState: ctx.workflowState
137
+ };
138
+ }
139
+ if (ctx.runtimeDirty && ctx.tokenUsage) {
140
+ updates.tokenUsage = ctx.tokenUsage;
141
+ }
142
+ return Object.keys(updates).length > 0 ? updates : null;
143
+ };
144
+ var cleanupCompletedReasonCheckpoints = (ctx) => {
145
+ let changed = false;
146
+ for (const [key, value] of Object.entries(ctx.currentNodeState.byKey)) {
147
+ if (!key.startsWith("__useReason:")) continue;
148
+ if (!isRecord5(value) || value.status !== "completed") continue;
149
+ delete ctx.currentNodeState.byKey[key];
150
+ changed = true;
151
+ }
152
+ if (changed) ctx.stateDirty = true;
153
+ };
154
+ var toTokenUsageDelta = (usage) => {
155
+ if (!usage) return void 0;
156
+ const input = usage.input ?? 0;
157
+ const output = usage.output ?? 0;
158
+ const total = usage.total ?? input + output + (usage.reasoning ?? 0);
159
+ if (input === 0 && output === 0 && total === 0) {
160
+ return void 0;
161
+ }
162
+ return {
163
+ input,
164
+ output,
165
+ total
166
+ };
167
+ };
168
+ function accumulateTokenUsage(usage) {
169
+ const delta = toTokenUsageDelta(usage);
170
+ if (!delta) return;
171
+ const ctx = getHookContext();
172
+ const current = ctx.tokenUsage ?? {
173
+ input: 0,
174
+ output: 0,
175
+ total: 0
176
+ };
177
+ ctx.tokenUsage = {
178
+ input: current.input + delta.input,
179
+ output: current.output + delta.output,
180
+ total: current.total + delta.total
181
+ };
182
+ ctx.runtimeDirty = true;
183
+ }
184
+ function getReasonTraceAdapter() {
185
+ return getHookContext().reasonTrace;
186
+ }
187
+ async function runWithHookContext(ctx, fn) {
188
+ const internal = createInternalContext(ctx);
189
+ try {
190
+ const result = await storage.run(internal, fn);
191
+ cleanupCompletedReasonCheckpoints(internal);
192
+ for (const key of Object.keys(internal.currentNodeState.byKey)) {
193
+ if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
194
+ internal.currentNodeState.byKey[key] = null;
195
+ internal.stateDirty = true;
196
+ }
197
+ }
198
+ return { result, runtimeUpdates: buildRuntimeStateUpdates(internal) };
199
+ } catch (err) {
200
+ const runtimeUpdates = buildRuntimeStateUpdates(internal);
201
+ if (runtimeUpdates) {
202
+ const currentRuntime = isRecord5(internal.state.runtime) ? internal.state.runtime : {};
203
+ internal.state.runtime = {
204
+ ...currentRuntime,
205
+ ...runtimeUpdates
206
+ };
207
+ if (err && typeof err === "object") {
208
+ err.__kortyxHookStatePatch = runtimeUpdates;
209
+ }
210
+ }
211
+ throw err;
212
+ }
213
+ }
214
+ function getHookContext() {
215
+ const ctx = storage.getStore();
216
+ if (!ctx) {
217
+ throw new Error("Hooks can only be used while a node is executing.");
218
+ }
219
+ return ctx;
220
+ }
221
+ var import_core5 = require("@kortyx/core");
222
+ var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
223
+ var readReasonCheckpoint = (raw) => {
224
+ if (!raw || typeof raw !== "object") return void 0;
225
+ const value = raw;
226
+ if (value.status !== "awaiting_interrupt") return void 0;
227
+ if (typeof value.firstText !== "string") return void 0;
228
+ if (!value.request || typeof value.request !== "object") return void 0;
229
+ return {
230
+ status: "awaiting_interrupt",
231
+ request: value.request,
232
+ firstText: value.firstText,
233
+ ...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
234
+ ...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
235
+ ...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
236
+ ...Object.hasOwn(value, "firstProviderMetadata") ? {
237
+ firstProviderMetadata: value.firstProviderMetadata
238
+ } : {},
239
+ ...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
240
+ ...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
241
+ };
242
+ };
243
+ var readReasonCompletedCheckpoint = (raw) => {
244
+ if (!raw || typeof raw !== "object") return void 0;
245
+ const value = raw;
246
+ if (value.status !== "completed") return void 0;
247
+ if (!value.result || typeof value.result !== "object") return void 0;
248
+ return {
249
+ status: "completed",
250
+ result: value.result
251
+ };
252
+ };
253
+ var resolveHookStatePatch = (args) => ({
254
+ __kortyx: {
255
+ nodeState: {
256
+ nodeId: args.nodeId,
257
+ state: args.currentNodeState
258
+ },
259
+ workflowState: args.workflowState
260
+ }
261
+ });
262
+ var parseWithSchema = (schema, value, label) => {
263
+ if (!schema) return value;
264
+ const parsed = schema.safeParse(value);
265
+ if (parsed.success) return parsed.data;
266
+ const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
267
+ const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
268
+ throw new Error(`${label} validation failed: ${reason}`);
269
+ };
270
+ var awaitInterruptInternal = (args) => {
271
+ const ctx = getHookContext();
272
+ (0, import_core5.throwIfExecutionAborted)(ctx.node.abortSignal);
273
+ const request = parseWithSchema(
274
+ args.requestSchema,
275
+ args.request,
276
+ "useInterrupt request"
277
+ );
278
+ const userMeta = args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? args.meta : {};
279
+ const resumeStatePatch = resolveHookStatePatch({
280
+ nodeId: ctx.node.graph.node,
281
+ currentNodeState: ctx.currentNodeState,
282
+ workflowState: ctx.workflowState
283
+ });
284
+ const enrichedRequest = {
285
+ ...request,
286
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
287
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
288
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
289
+ meta: {
290
+ ...userMeta,
291
+ __kortyxResumeStatePatch: {
292
+ ...resumeStatePatch,
293
+ ...ctx.tokenUsage ? { tokenUsage: ctx.tokenUsage } : {}
294
+ }
295
+ }
296
+ };
297
+ const raw = ctx.node.awaitInterrupt(enrichedRequest);
298
+ const response = parseWithSchema(
299
+ args.responseSchema,
300
+ raw,
301
+ "useInterrupt response"
302
+ );
303
+ return Promise.resolve(response);
304
+ };
305
+ var import_core52 = require("@kortyx/core");
306
+ var import_node_crypto4 = require("crypto");
307
+ var parseStructuredPath = (path, source) => {
308
+ if (path.length === 0) {
309
+ throw new Error(`${source} path must be a non-empty dot-separated string.`);
310
+ }
311
+ const parts = path.split(".");
312
+ if (parts.some((part) => part.length === 0)) {
313
+ throw new Error(
314
+ `${source} path "${path}" must not contain empty segments.`
315
+ );
316
+ }
317
+ return parts;
318
+ };
319
+ var assertStructuredPath = (path, source) => {
320
+ parseStructuredPath(path, source);
321
+ };
322
+ var createStructuredStreamId = () => {
323
+ try {
324
+ return (0, import_node_crypto4.randomUUID)();
325
+ } catch {
326
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
327
+ }
328
+ };
329
+ var emitStructuredData = (args) => {
330
+ const ctx = getHookContext();
331
+ const streamId = typeof args.streamId === "string" && args.streamId.length > 0 ? args.streamId : createStructuredStreamId();
332
+ const dataType = typeof args.dataType === "string" && args.dataType.length > 0 ? args.dataType : "generic";
333
+ const base = {
334
+ node: ctx.node.graph.node,
335
+ streamId,
336
+ dataType,
337
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
338
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
339
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {}
340
+ };
341
+ if (args.kind === "set") {
342
+ assertStructuredPath(args.path, "useStructuredData");
343
+ const value = parseWithSchema(
344
+ args.valueSchema,
345
+ args.value,
346
+ "useStructuredData value"
347
+ );
348
+ ctx.node.emit("structured_data", {
349
+ ...base,
350
+ kind: "set",
351
+ path: args.path,
352
+ value
353
+ });
354
+ return;
355
+ }
356
+ if (args.kind === "append") {
357
+ assertStructuredPath(args.path, "useStructuredData");
358
+ const items = args.items.map(
359
+ (item) => parseWithSchema(args.itemSchema, item, "useStructuredData append item")
360
+ );
361
+ ctx.node.emit("structured_data", {
362
+ ...base,
363
+ kind: "append",
364
+ path: args.path,
365
+ items
366
+ });
367
+ return;
368
+ }
369
+ if (args.kind === "text-delta") {
370
+ assertStructuredPath(args.path, "useStructuredData");
371
+ ctx.node.emit("structured_data", {
372
+ ...base,
373
+ kind: "text-delta",
374
+ path: args.path,
375
+ delta: args.delta
376
+ });
377
+ return;
378
+ }
379
+ const validated = parseWithSchema(
380
+ args.dataSchema,
381
+ args.data,
382
+ "useStructuredData data"
383
+ );
384
+ ctx.node.emit("structured_data", {
385
+ ...base,
386
+ kind: "final",
387
+ data: validated
388
+ });
389
+ };
390
+ var shouldEmitStructured = (cfg) => Boolean(cfg);
391
+ var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
392
+ var import_node_crypto32 = require("crypto");
393
+ var import_core32 = require("@kortyx/core");
394
+ var import_node_crypto22 = require("crypto");
395
+ var import_core22 = require("@kortyx/core");
396
+ var toError = (value) => value instanceof Error ? value : new Error(String(value));
397
+ var createRuntimeId = () => {
398
+ try {
399
+ return (0, import_node_crypto22.randomUUID)();
400
+ } catch {
401
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
402
+ }
403
+ };
404
+ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
405
+ if (!emitEvent) return;
406
+ emitEvent(event, {
407
+ ...payload,
408
+ ...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
409
+ });
410
+ };
411
+ async function runReasonEngine(args) {
412
+ const stream = args.stream ?? args.model.options?.streaming ?? true;
413
+ const emit = args.emit ?? true;
414
+ const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
415
+ const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
416
+ const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
417
+ const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
418
+ const reasoning = args.reasoning ?? args.model.options?.reasoning;
419
+ const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
420
+ const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
421
+ const tools = args.tools ?? args.model.options?.tools;
422
+ (0, import_core22.throwIfExecutionAborted)(abortSignal);
423
+ const model = args.model.provider.getModel(args.model.modelId, {
424
+ ...temperature !== void 0 ? { temperature } : {},
425
+ streaming: stream,
426
+ ...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
427
+ ...stopSequences !== void 0 ? { stopSequences } : {},
428
+ ...abortSignal !== void 0 ? { abortSignal } : {},
429
+ ...reasoning !== void 0 ? { reasoning } : {},
430
+ ...responseFormat !== void 0 ? { responseFormat } : {},
431
+ ...tools !== void 0 ? { tools } : {},
432
+ ...providerOptions !== void 0 ? { providerOptions } : {}
433
+ });
434
+ const messages = args.messages !== void 0 ? args.messages : [
435
+ ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
436
+ { role: "user", content: String(args.input ?? "") }
437
+ ];
438
+ const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
439
+ const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
440
+ const commonMeta = {
441
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
442
+ opId,
443
+ segmentId
444
+ };
445
+ const traceSpan = args.reasonTrace?.startSpan({
446
+ name: "runReasonEngine",
447
+ attributes: {
448
+ ...commonMeta,
449
+ providerId: args.model.provider.id,
450
+ modelId: args.model.modelId,
451
+ stream,
452
+ emit,
453
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
454
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
455
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
456
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
457
+ ...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
458
+ },
459
+ telemetry: {
460
+ ...args.telemetry ?? {},
461
+ input: args.telemetry?.input ?? messages
462
+ }
463
+ });
464
+ const modelRequestStartedAt = Date.now();
465
+ let firstTextTokenAt;
466
+ let lastTextTokenAt;
467
+ const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
468
+ ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
469
+ streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
470
+ timeToLastTokenMs: Math.max(
471
+ 0,
472
+ lastTextTokenAt - modelRequestStartedAt
473
+ )
474
+ };
475
+ const endTrace = (span, result) => {
476
+ span?.end?.({
477
+ ...result.usage !== void 0 ? { usage: result.usage } : {},
478
+ ...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
479
+ ...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
480
+ ...result.warnings !== void 0 ? { warnings: result.warnings } : {},
481
+ attributes: {
482
+ textLength: result.text.length,
483
+ ...generationTimingAttributes()
484
+ },
485
+ telemetry: {
486
+ ...args.telemetry ?? {},
487
+ output: args.telemetry?.output ?? result.text
488
+ }
489
+ });
490
+ };
491
+ const failTrace = (span, error) => {
492
+ span?.fail?.(error, {
493
+ attributes: {
494
+ providerId: args.model.provider.id,
495
+ modelId: args.model.modelId
496
+ }
497
+ });
498
+ };
499
+ try {
500
+ if (stream) {
501
+ let final = "";
502
+ let raw;
503
+ let usage;
504
+ let finishReason;
505
+ let toolCalls;
506
+ let providerMetadata;
507
+ let warnings;
508
+ if (emit) {
509
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
510
+ ...commonMeta
511
+ });
512
+ }
513
+ const response2 = await model.stream(messages);
514
+ for await (const chunk of response2) {
515
+ (0, import_core22.throwIfExecutionAborted)(abortSignal);
516
+ switch (chunk.type) {
517
+ case "text-delta": {
518
+ if (chunk.delta.length === 0) break;
519
+ const tokenAt = Date.now();
520
+ firstTextTokenAt ??= tokenAt;
521
+ lastTextTokenAt = tokenAt;
522
+ final += chunk.delta;
523
+ args.onTextChunk?.(chunk.delta);
524
+ if (chunk.raw !== void 0) {
525
+ raw = chunk.raw;
526
+ }
527
+ if (chunk.providerMetadata !== void 0) {
528
+ providerMetadata = chunk.providerMetadata;
529
+ }
530
+ if (emit) {
531
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
532
+ ...commonMeta,
533
+ delta: chunk.delta
534
+ });
535
+ }
536
+ break;
537
+ }
538
+ case "finish": {
539
+ if (chunk.raw !== void 0) {
540
+ raw = chunk.raw;
541
+ }
542
+ if (chunk.usage !== void 0) {
543
+ usage = chunk.usage;
544
+ }
545
+ if (chunk.finishReason !== void 0) {
546
+ finishReason = chunk.finishReason;
547
+ }
548
+ if (chunk.toolCalls !== void 0) {
549
+ toolCalls = chunk.toolCalls;
550
+ }
551
+ if (chunk.providerMetadata !== void 0) {
552
+ providerMetadata = chunk.providerMetadata;
553
+ }
554
+ if (chunk.warnings !== void 0) {
555
+ warnings = chunk.warnings;
556
+ }
557
+ break;
558
+ }
559
+ case "raw": {
560
+ raw = chunk.raw;
561
+ if (chunk.providerMetadata !== void 0) {
562
+ providerMetadata = chunk.providerMetadata;
563
+ }
564
+ break;
565
+ }
566
+ case "error": {
567
+ if (chunk.raw !== void 0) {
568
+ raw = chunk.raw;
569
+ }
570
+ if (chunk.providerMetadata !== void 0) {
571
+ providerMetadata = chunk.providerMetadata;
572
+ }
573
+ if (chunk.warnings !== void 0) {
574
+ warnings = chunk.warnings;
575
+ }
576
+ throw toError(chunk.error);
577
+ }
578
+ }
579
+ }
580
+ if (emit) {
581
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
582
+ ...commonMeta
583
+ });
584
+ }
585
+ const result2 = {
586
+ text: final,
587
+ ...raw !== void 0 ? { raw } : {},
588
+ ...usage !== void 0 ? { usage } : {},
589
+ ...finishReason !== void 0 ? { finishReason } : {},
590
+ ...toolCalls !== void 0 ? { toolCalls } : {},
591
+ ...providerMetadata !== void 0 ? { providerMetadata } : {},
592
+ ...warnings !== void 0 ? { warnings } : {}
593
+ };
594
+ (0, import_core22.throwIfExecutionAborted)(abortSignal);
595
+ endTrace(traceSpan, result2);
596
+ return result2;
597
+ }
598
+ const response = await model.invoke(messages);
599
+ (0, import_core22.throwIfExecutionAborted)(abortSignal);
600
+ if (emit) {
601
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
602
+ ...commonMeta
603
+ });
604
+ if (response.content) {
605
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
606
+ ...commonMeta,
607
+ delta: response.content
608
+ });
609
+ }
610
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
611
+ ...commonMeta
612
+ });
613
+ }
614
+ const result = {
615
+ text: response.content,
616
+ ...response.raw !== void 0 ? { raw: response.raw } : {},
617
+ ...response.usage !== void 0 ? { usage: response.usage } : {},
618
+ ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
619
+ ...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
620
+ ...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
621
+ ...response.warnings !== void 0 ? { warnings: response.warnings } : {}
622
+ };
623
+ endTrace(traceSpan, result);
624
+ return result;
625
+ } catch (error) {
626
+ const failure = abortSignal?.aborted ? (0, import_core22.createExecutionCancelledError)() : error;
627
+ failTrace(traceSpan, failure);
628
+ throw failure;
629
+ }
630
+ }
631
+ var createRuntimeId2 = () => {
632
+ try {
633
+ return (0, import_node_crypto32.randomUUID)();
634
+ } catch {
635
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
636
+ }
637
+ };
638
+ async function reasonEngine(args, meta, inputOverride) {
639
+ const ctx = getHookContext();
640
+ const abortSignal = (0, import_core32.combineAbortSignals)(
641
+ ctx.node.abortSignal,
642
+ args.abortSignal,
643
+ args.model.options?.abortSignal
644
+ );
645
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
646
+ ctx.node.consumeExecution?.("maxModelPasses");
647
+ return runReasonEngine({
648
+ model: args.model,
649
+ input: inputOverride ?? args.input,
650
+ system: args.system,
651
+ temperature: args.temperature,
652
+ maxOutputTokens: args.maxOutputTokens,
653
+ stopSequences: args.stopSequences,
654
+ abortSignal,
655
+ reasoning: args.reasoning,
656
+ responseFormat: args.responseFormat,
657
+ providerOptions: args.providerOptions,
658
+ tools: args.tools,
659
+ messages: args.messages,
660
+ defaultTemperature: ctx.node.config?.model?.temperature,
661
+ stream: args.stream,
662
+ emit: args.emit,
663
+ onTextChunk: args.onTextChunk,
664
+ telemetry: args.telemetry,
665
+ nodeId: ctx.node.graph.node,
666
+ emitEvent: ctx.node.emit,
667
+ reasonTrace: ctx.reasonTrace,
668
+ id: meta.id,
669
+ opId: meta.opId
670
+ });
671
+ }
672
+ var tryJsonParse = (value) => {
673
+ try {
674
+ return { ok: true, value: JSON.parse(value) };
675
+ } catch (error) {
676
+ return {
677
+ ok: false,
678
+ error: error instanceof Error ? error.message : String(error)
679
+ };
680
+ }
681
+ };
682
+ var extractJsonCodeBlock = (value) => {
683
+ const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
684
+ if (!match || typeof match[1] !== "string") return null;
685
+ const inner = match[1].trim();
686
+ return inner.length > 0 ? inner : null;
687
+ };
688
+ var tryStringify = (value) => {
689
+ if (typeof value === "string") return value;
690
+ try {
691
+ return JSON.stringify(value, null, 2);
692
+ } catch {
693
+ return String(value);
694
+ }
695
+ };
696
+ var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
697
+ var resolveOutputCandidate = (text) => {
698
+ const trimmed = text.trim();
699
+ if (!trimmed) return text;
700
+ const direct = tryJsonParse(trimmed);
701
+ if (direct.ok) return direct.value;
702
+ const block = extractJsonCodeBlock(trimmed);
703
+ if (block) {
704
+ const parsedBlock = tryJsonParse(block);
705
+ if (parsedBlock.ok) return parsedBlock.value;
706
+ }
707
+ return text;
708
+ };
709
+ var looksLikeStructuredJsonAttempt = (value) => {
710
+ const trimmed = value.trim();
711
+ if (!trimmed) return false;
712
+ return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
713
+ };
714
+ var isLikelyTruncatedStructuredOutput = (text) => {
715
+ const trimmed = text.trim();
716
+ if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
717
+ const direct = tryJsonParse(trimmed);
718
+ if (!direct.ok && direct.error) return true;
719
+ const block = extractJsonCodeBlock(trimmed);
720
+ if (!block) return false;
721
+ const parsedBlock = tryJsonParse(block);
722
+ return !parsedBlock.ok && Boolean(parsedBlock.error);
723
+ };
724
+ var createTruncatedStructuredOutputError = (label) => new Error(
725
+ `${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
726
+ );
727
+ var createInvalidStructuredOutputError = (label) => new Error(
728
+ `${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.`
729
+ );
730
+ var parseReasonOutputWithSchema = (args) => {
731
+ const candidate = resolveOutputCandidate(args.text);
732
+ try {
733
+ return parseWithSchema(args.schema, candidate, args.label);
734
+ } catch (error) {
735
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
736
+ throw createTruncatedStructuredOutputError(args.label);
737
+ }
738
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
739
+ throw createInvalidStructuredOutputError(args.label);
740
+ }
741
+ throw error;
742
+ }
743
+ };
744
+ var parseInterruptFirstPassResult = (args) => {
745
+ const candidate = resolveOutputCandidate(args.text);
746
+ if (!isRecord22(candidate)) {
747
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
748
+ throw createTruncatedStructuredOutputError("useReason output");
749
+ }
750
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
751
+ throw createInvalidStructuredOutputError("useReason output");
752
+ }
753
+ throw new Error(
754
+ "useReason first pass with interrupt must return a JSON object."
755
+ );
756
+ }
757
+ const mode = args.mode ?? "required";
758
+ let interruptRequired = true;
759
+ if (mode === "optional") {
760
+ if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
761
+ throw new Error(
762
+ 'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
763
+ );
764
+ }
765
+ interruptRequired = candidate.decision === "interrupt";
766
+ if (!interruptRequired) {
767
+ const hasUnexpectedRequest = [
768
+ candidate.interruptRequest,
769
+ candidate.request,
770
+ candidate.interrupt
771
+ ].some((value) => value !== null && value !== void 0);
772
+ if (hasUnexpectedRequest) {
773
+ throw new Error(
774
+ 'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
775
+ );
776
+ }
777
+ }
778
+ }
779
+ let request;
780
+ if (interruptRequired) {
781
+ const requestCandidates = [
782
+ candidate.interruptRequest,
783
+ candidate.request,
784
+ candidate.interrupt,
785
+ mode === "required" ? candidate : void 0
786
+ ];
787
+ const requestPayload = requestCandidates.find(
788
+ (value) => value !== null && value !== void 0
789
+ );
790
+ request = parseWithSchema(
791
+ args.requestSchema,
792
+ requestPayload,
793
+ "useReason interrupt.request"
794
+ );
795
+ }
796
+ let output;
797
+ if (args.outputSchema) {
798
+ const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
799
+ try {
800
+ output = parseWithSchema(
801
+ args.outputSchema,
802
+ outputPayload,
803
+ "useReason output"
804
+ );
805
+ } catch (error) {
806
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
807
+ throw createTruncatedStructuredOutputError("useReason output");
808
+ }
809
+ if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
810
+ throw createInvalidStructuredOutputError("useReason output");
811
+ }
812
+ throw error;
813
+ }
814
+ }
815
+ const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
816
+ if (!args.outputSchema && !explicitDraft) {
817
+ throw new Error(
818
+ "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
819
+ );
820
+ }
821
+ const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
822
+ if (!interruptRequired) {
823
+ return {
824
+ draftText,
825
+ interruptRequired: false,
826
+ ...output !== void 0 ? { output } : {}
827
+ };
828
+ }
829
+ return {
830
+ draftText,
831
+ interruptRequired: true,
832
+ request,
833
+ ...output !== void 0 ? { output } : {}
834
+ };
835
+ };
836
+ var import_zod5 = require("zod");
837
+ var tryStringify2 = (value) => {
838
+ if (typeof value === "string") return value;
839
+ try {
840
+ return JSON.stringify(value, null, 2);
841
+ } catch {
842
+ return String(value);
843
+ }
844
+ };
845
+ var tryGetSchemaHint = (schema) => {
846
+ try {
847
+ const hint = (0, import_zod5.toJSONSchema)(schema);
848
+ return JSON.stringify(hint, null, 2);
849
+ } catch {
850
+ return void 0;
851
+ }
852
+ };
853
+ var withSchemaHint = (input, label, schema) => {
854
+ const hint = tryGetSchemaHint(schema);
855
+ if (!hint) return input;
856
+ return `${input}
857
+
858
+ ${label}:
859
+ ${hint}`;
860
+ };
861
+ var defaultInterruptFirstPassInput = (args) => {
862
+ const mode = args.mode ?? "required";
863
+ const base = mode === "optional" ? args.outputSchema ? [
864
+ args.input,
865
+ "Output rules:",
866
+ "- Return JSON only. No markdown fences.",
867
+ "- Return an object with keys: decision, output, interruptRequest, draftText.",
868
+ '- decision must be either "continue" or "interrupt".',
869
+ '- Use decision "continue" when you can produce the final output without asking the user.',
870
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
871
+ "- output must match the expected output JSON schema.",
872
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
873
+ '- When decision is "continue", interruptRequest must be null.',
874
+ "- draftText should be a concise plain-language summary of output."
875
+ ].join("\n\n") : [
876
+ args.input,
877
+ "Output rules:",
878
+ "- Return JSON only. No markdown fences.",
879
+ "- Return an object with keys: decision, draftText, interruptRequest.",
880
+ '- decision must be either "continue" or "interrupt".',
881
+ '- Use decision "continue" when you can produce the final answer without asking the user.',
882
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
883
+ "- draftText should be the current answer text.",
884
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
885
+ '- When decision is "continue", interruptRequest must be null.'
886
+ ].join("\n\n") : args.outputSchema ? [
887
+ args.input,
888
+ "Output rules:",
889
+ "- Return JSON only. No markdown fences.",
890
+ "- Return an object with keys: output, interruptRequest, draftText.",
891
+ "- output must match the expected output JSON schema.",
892
+ "- interruptRequest must match the expected interrupt request JSON schema.",
893
+ "- interruptRequest must be a non-null object.",
894
+ "- draftText should be a concise plain-language summary of output.",
895
+ "- If uncertain, still return a valid interruptRequest with safe default options."
896
+ ].join("\n\n") : [
897
+ args.input,
898
+ "Output rules:",
899
+ "- Return JSON only. No markdown fences.",
900
+ "- Return an object with keys: draftText, interruptRequest.",
901
+ "- draftText should be the current draft answer text.",
902
+ "- interruptRequest must match the expected interrupt request JSON schema.",
903
+ "- interruptRequest must be a non-null object.",
904
+ "- If uncertain, still return a valid interruptRequest with safe default options."
905
+ ].join("\n\n");
906
+ const withInterruptSchema = withSchemaHint(
907
+ base,
908
+ "Expected interrupt request JSON schema (for `interruptRequest`)",
909
+ args.requestSchema
910
+ );
911
+ if (!args.outputSchema) return withInterruptSchema;
912
+ const withOutputSchema = withSchemaHint(
913
+ withInterruptSchema,
914
+ "Expected output JSON schema (for `output`)",
915
+ args.outputSchema
916
+ );
917
+ return [
918
+ withOutputSchema,
919
+ "JSON template:",
920
+ mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
921
+ ].join("\n\n");
922
+ };
923
+ var defaultContinuationInput = (args) => [
924
+ "You are continuing a paused reasoning task after user input.",
925
+ "Update the previous result using the user response and return the final answer only.",
926
+ "If the previous output was JSON, return JSON in the same schema.",
927
+ "",
928
+ `Original request:
929
+ ${args.input}`,
930
+ `Draft answer text:
931
+ ${args.draftText}`,
932
+ `Draft output object:
933
+ ${tryStringify2(args.draftOutput ?? args.draftText)}`,
934
+ `Interrupt request shown to user:
935
+ ${tryStringify2(args.request)}`,
936
+ `User response:
937
+ ${tryStringify2(args.response)}`
938
+ ].join("\n\n");
939
+ var withOutputGuardrails = (input, schema) => {
940
+ const base = [
941
+ input,
942
+ "Output rules:",
943
+ "- Return only JSON, no markdown fences.",
944
+ "- Ensure the response conforms to the expected output schema.",
945
+ "- Do not include explanatory text outside the JSON object."
946
+ ].join("\n\n");
947
+ return withSchemaHint(base, "Expected output JSON schema", schema);
948
+ };
949
+ var withStructuredStreamHints = (input, args) => {
950
+ const rules = [];
951
+ for (const path of args.setFieldPaths ?? []) {
952
+ rules.push(
953
+ `- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
954
+ );
955
+ }
956
+ for (const path of args.appendFieldPaths ?? []) {
957
+ rules.push(
958
+ `- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
959
+ );
960
+ rules.push(
961
+ `- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
962
+ );
963
+ }
964
+ for (const path of args.textDeltaFieldPaths ?? []) {
965
+ rules.push(
966
+ `- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
967
+ );
968
+ rules.push(
969
+ `- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
970
+ );
971
+ }
972
+ if (rules.length === 0) return input;
973
+ return [
974
+ input,
975
+ "Streaming rules:",
976
+ ...rules,
977
+ "- Keep the overall response as valid JSON."
978
+ ].join("\n\n");
979
+ };
980
+ var mergeUsage = (left, right) => {
981
+ if (!left) return right;
982
+ if (!right) return left;
983
+ const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
984
+ const raw = left.raw || right.raw ? {
985
+ ...left.raw ?? {},
986
+ ...right.raw ?? {}
987
+ } : void 0;
988
+ const input = sum(left.input, right.input);
989
+ const output = sum(left.output, right.output);
990
+ const total = sum(left.total, right.total);
991
+ const reasoning = sum(left.reasoning, right.reasoning);
992
+ const cacheRead = sum(left.cacheRead, right.cacheRead);
993
+ const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
994
+ const merged = {};
995
+ if (input !== void 0) merged.input = input;
996
+ if (output !== void 0) merged.output = output;
997
+ if (total !== void 0) merged.total = total;
998
+ if (reasoning !== void 0) merged.reasoning = reasoning;
999
+ if (cacheRead !== void 0) merged.cacheRead = cacheRead;
1000
+ if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
1001
+ if (raw) merged.raw = raw;
1002
+ return merged;
1003
+ };
1004
+ var mergeWarnings = (left, right) => {
1005
+ if (!left?.length) return right;
1006
+ if (!right?.length) return left;
1007
+ const seen = /* @__PURE__ */ new Set();
1008
+ const merged = [];
1009
+ for (const warning of [...left, ...right]) {
1010
+ const key = JSON.stringify(warning);
1011
+ if (seen.has(key)) continue;
1012
+ seen.add(key);
1013
+ merged.push(warning);
1014
+ }
1015
+ return merged;
1016
+ };
1017
+ var mergeProviderMetadata = (left, right) => {
1018
+ if (!left) return right;
1019
+ if (!right) return left;
1020
+ return {
1021
+ ...left,
1022
+ ...right
1023
+ };
1024
+ };
1025
+ var emitReasonStructuredOutput = (args) => {
1026
+ if (!args.emit || !shouldEmitStructured(args.structured)) return;
1027
+ emitStructuredData({
1028
+ dataType: args.structured?.dataType ?? "reason-output",
1029
+ data: args.output,
1030
+ kind: "final",
1031
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1032
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1033
+ ...args.id ? { id: args.id } : {},
1034
+ streamId: args.opId
1035
+ });
1036
+ };
1037
+ var resolveAppendFieldPaths = (cfg) => {
1038
+ const fields = cfg?.fields;
1039
+ if (!fields) return [];
1040
+ const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
1041
+ for (const path of appendPaths) {
1042
+ assertStructuredPath(path, "useReason structured append");
1043
+ }
1044
+ return appendPaths;
1045
+ };
1046
+ var resolveSetFieldPaths = (cfg) => {
1047
+ const fields = cfg?.fields;
1048
+ if (!fields) return [];
1049
+ const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
1050
+ for (const path of setPaths) {
1051
+ assertStructuredPath(path, "useReason structured set");
1052
+ }
1053
+ return setPaths;
1054
+ };
1055
+ var resolveTextDeltaFieldPaths = (cfg) => {
1056
+ const fields = cfg?.fields;
1057
+ if (!fields) return [];
1058
+ const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
1059
+ for (const path of textPaths) {
1060
+ assertStructuredPath(path, "useReason structured text-delta");
1061
+ }
1062
+ return textPaths;
1063
+ };
1064
+ var extractCompletedStringValueAt = (args) => {
1065
+ let value = "";
1066
+ for (let i = args.start; i < args.text.length; i += 1) {
1067
+ const ch = args.text[i];
1068
+ if (ch === void 0) break;
1069
+ if (ch === '"') {
1070
+ return {
1071
+ complete: true,
1072
+ value
1073
+ };
1074
+ }
1075
+ if (ch !== "\\") {
1076
+ value += ch;
1077
+ continue;
1078
+ }
1079
+ const next = args.text[i + 1];
1080
+ if (!next) {
1081
+ return {
1082
+ complete: false
1083
+ };
1084
+ }
1085
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1086
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1087
+ i += 1;
1088
+ continue;
1089
+ }
1090
+ if (next === "u") {
1091
+ const hex = args.text.slice(i + 2, i + 6);
1092
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1093
+ return {
1094
+ complete: false
1095
+ };
1096
+ }
1097
+ value += String.fromCharCode(Number.parseInt(hex, 16));
1098
+ i += 5;
1099
+ continue;
1100
+ }
1101
+ return {
1102
+ complete: false
1103
+ };
1104
+ }
1105
+ return {
1106
+ complete: false
1107
+ };
1108
+ };
1109
+ var skipWhitespace = (text, start) => {
1110
+ let i = start;
1111
+ while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
1112
+ return i;
1113
+ };
1114
+ var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
1115
+ var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
1116
+ (part, index) => pathSegmentMatches(patternParts[index] ?? "", part)
1117
+ );
1118
+ var isPathPrefix = (prefix, full) => prefix.length <= full.length && prefix.every((part, index) => pathSegmentMatches(full[index] ?? "", part));
1119
+ var parseStringToken = (text, start) => {
1120
+ if (text[start] !== '"') return { complete: false };
1121
+ const parsed = extractCompletedStringValueAt({ text, start: start + 1 });
1122
+ if (!parsed.complete || parsed.value === void 0)
1123
+ return { complete: false };
1124
+ let inEscape = false;
1125
+ for (let i = start + 1; i < text.length; i += 1) {
1126
+ const ch = text[i];
1127
+ if (ch === void 0) break;
1128
+ if (inEscape) {
1129
+ inEscape = false;
1130
+ if (ch === "u") i += 4;
1131
+ continue;
1132
+ }
1133
+ if (ch === "\\") {
1134
+ inEscape = true;
1135
+ continue;
1136
+ }
1137
+ if (ch === '"') {
1138
+ return {
1139
+ complete: true,
1140
+ end: i + 1,
1141
+ value: parsed.value
1142
+ };
1143
+ }
1144
+ }
1145
+ return { complete: false };
1146
+ };
1147
+ var skipNestedValue = (text, start) => {
1148
+ const opener = text[start];
1149
+ const closer = opener === "{" ? "}" : "]";
1150
+ let inString = false;
1151
+ let isEscaped = false;
1152
+ let nesting = 0;
1153
+ for (let i = start; i < text.length; i += 1) {
1154
+ const ch = text[i];
1155
+ if (ch === void 0) break;
1156
+ if (inString) {
1157
+ if (isEscaped) {
1158
+ isEscaped = false;
1159
+ continue;
1160
+ }
1161
+ if (ch === "\\") {
1162
+ isEscaped = true;
1163
+ continue;
1164
+ }
1165
+ if (ch === '"') {
1166
+ inString = false;
1167
+ }
1168
+ continue;
1169
+ }
1170
+ if (ch === '"') {
1171
+ inString = true;
1172
+ continue;
1173
+ }
1174
+ if (ch === opener) {
1175
+ nesting += 1;
1176
+ continue;
1177
+ }
1178
+ if (ch === closer) {
1179
+ nesting -= 1;
1180
+ if (nesting === 0) {
1181
+ return { complete: true, end: i + 1 };
1182
+ }
1183
+ }
1184
+ }
1185
+ return { complete: false };
1186
+ };
1187
+ var skipCompleteValue = (text, start) => {
1188
+ const i = skipWhitespace(text, start);
1189
+ const first = text[i];
1190
+ if (!first) return { complete: false };
1191
+ if (first === '"') {
1192
+ const parsed = parseStringToken(text, i);
1193
+ return parsed.complete && parsed.end !== void 0 ? { complete: true, end: parsed.end } : { complete: false };
1194
+ }
1195
+ if (first === "{" || first === "[") {
1196
+ return skipNestedValue(text, i);
1197
+ }
1198
+ for (let j = i; j < text.length; j += 1) {
1199
+ const ch = text[j];
1200
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1201
+ const raw = text.slice(i, j).trim();
1202
+ if (!raw) return { complete: false };
1203
+ try {
1204
+ JSON.parse(raw);
1205
+ return { complete: true, end: j };
1206
+ } catch {
1207
+ return { complete: false };
1208
+ }
1209
+ }
1210
+ return { complete: false };
1211
+ };
1212
+ var locateValueStart = (args) => {
1213
+ const [first] = locateValueStarts(args);
1214
+ return first ? {
1215
+ found: true,
1216
+ start: first.start
1217
+ } : {
1218
+ found: false
1219
+ };
1220
+ };
1221
+ var locateValueStarts = (args) => {
1222
+ const targetParts = parseStructuredPath(
1223
+ args.path,
1224
+ "useReason structured field"
1225
+ );
1226
+ const matches = [];
1227
+ const visitValue = (start, currentParts) => {
1228
+ const valueStart = skipWhitespace(args.text, start);
1229
+ if (pathMatchesPattern(currentParts, targetParts)) {
1230
+ matches.push({
1231
+ path: currentParts.join("."),
1232
+ start: valueStart
1233
+ });
1234
+ return;
1235
+ }
1236
+ if (!isPathPrefix(currentParts, targetParts)) {
1237
+ return;
1238
+ }
1239
+ const first = args.text[valueStart];
1240
+ if (first === "{") visitObject(valueStart + 1, currentParts);
1241
+ if (first === "[") visitArray(valueStart + 1, currentParts);
1242
+ };
1243
+ const visitObject = (start, currentParts) => {
1244
+ let i = skipWhitespace(args.text, start);
1245
+ while (i < args.text.length) {
1246
+ const ch = args.text[i];
1247
+ if (ch === "}") return;
1248
+ if (ch === ",") {
1249
+ i = skipWhitespace(args.text, i + 1);
1250
+ continue;
1251
+ }
1252
+ if (ch !== '"') return;
1253
+ const key = parseStringToken(args.text, i);
1254
+ if (!key.complete || key.end === void 0 || key.value === void 0) {
1255
+ return;
1256
+ }
1257
+ i = skipWhitespace(args.text, key.end);
1258
+ if (args.text[i] !== ":") return;
1259
+ const valueStart = skipWhitespace(args.text, i + 1);
1260
+ const childParts = [...currentParts, key.value];
1261
+ if (pathMatchesPattern(childParts, targetParts)) {
1262
+ matches.push({
1263
+ path: childParts.join("."),
1264
+ start: valueStart
1265
+ });
1266
+ } else if (isPathPrefix(childParts, targetParts)) {
1267
+ visitValue(valueStart, childParts);
1268
+ }
1269
+ const skipped = skipCompleteValue(args.text, valueStart);
1270
+ if (!skipped.complete || skipped.end === void 0) {
1271
+ return;
1272
+ }
1273
+ i = skipWhitespace(args.text, skipped.end);
1274
+ if (args.text[i] === ",") {
1275
+ i = skipWhitespace(args.text, i + 1);
1276
+ continue;
1277
+ }
1278
+ if (args.text[i] === "}") return;
1279
+ return;
1280
+ }
1281
+ };
1282
+ const visitArray = (start, currentParts) => {
1283
+ let i = skipWhitespace(args.text, start);
1284
+ let index = 0;
1285
+ while (i < args.text.length) {
1286
+ if (args.text[i] === "]") return;
1287
+ const childParts = [...currentParts, String(index)];
1288
+ if (pathMatchesPattern(childParts, targetParts)) {
1289
+ matches.push({
1290
+ path: childParts.join("."),
1291
+ start: i
1292
+ });
1293
+ } else if (isPathPrefix(childParts, targetParts)) {
1294
+ visitValue(i, childParts);
1295
+ }
1296
+ const skipped = skipCompleteValue(args.text, i);
1297
+ if (!skipped.complete || skipped.end === void 0) {
1298
+ return;
1299
+ }
1300
+ i = skipWhitespace(args.text, skipped.end);
1301
+ if (args.text[i] === ",") {
1302
+ index += 1;
1303
+ i = skipWhitespace(args.text, i + 1);
1304
+ continue;
1305
+ }
1306
+ if (args.text[i] === "]") return;
1307
+ return;
1308
+ }
1309
+ };
1310
+ visitValue(0, []);
1311
+ return matches;
1312
+ };
1313
+ var extractCompletedValueAt = (args) => {
1314
+ const start = skipWhitespace(args.text, args.start);
1315
+ const first = args.text[start];
1316
+ if (!first) return { complete: false };
1317
+ if (first === '"') {
1318
+ const parsed = extractCompletedStringValueAt({
1319
+ text: args.text,
1320
+ start: start + 1
1321
+ });
1322
+ return parsed.complete ? {
1323
+ complete: true,
1324
+ value: parsed.value
1325
+ } : {
1326
+ complete: false
1327
+ };
1328
+ }
1329
+ if (first === "{") {
1330
+ return extractCompletedNestedValueAt({
1331
+ text: args.text,
1332
+ start,
1333
+ opener: "{",
1334
+ closer: "}"
1335
+ });
1336
+ }
1337
+ if (first === "[") {
1338
+ return extractCompletedNestedValueAt({
1339
+ text: args.text,
1340
+ start,
1341
+ opener: "[",
1342
+ closer: "]"
1343
+ });
1344
+ }
1345
+ for (let i = start; i < args.text.length; i += 1) {
1346
+ const ch = args.text[i];
1347
+ if (ch === void 0) break;
1348
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1349
+ const raw = args.text.slice(start, i).trim();
1350
+ if (!raw) return { complete: false };
1351
+ try {
1352
+ return {
1353
+ complete: true,
1354
+ value: JSON.parse(raw)
1355
+ };
1356
+ } catch {
1357
+ return { complete: false };
1358
+ }
1359
+ }
1360
+ return { complete: false };
1361
+ };
1362
+ var extractCompletedNestedValueAt = (args) => {
1363
+ let inString = false;
1364
+ let isEscaped = false;
1365
+ let nesting = 0;
1366
+ for (let i = args.start; i < args.text.length; i += 1) {
1367
+ const ch = args.text[i];
1368
+ if (ch === void 0) break;
1369
+ if (inString) {
1370
+ if (isEscaped) {
1371
+ isEscaped = false;
1372
+ continue;
1373
+ }
1374
+ if (ch === "\\") {
1375
+ isEscaped = true;
1376
+ continue;
1377
+ }
1378
+ if (ch === '"') {
1379
+ inString = false;
1380
+ }
1381
+ continue;
1382
+ }
1383
+ if (ch === '"') {
1384
+ inString = true;
1385
+ continue;
1386
+ }
1387
+ if (ch === args.opener) {
1388
+ nesting += 1;
1389
+ continue;
1390
+ }
1391
+ if (ch === args.closer) {
1392
+ nesting -= 1;
1393
+ if (nesting === 0) {
1394
+ const raw = args.text.slice(args.start, i + 1);
1395
+ try {
1396
+ return {
1397
+ complete: true,
1398
+ value: JSON.parse(raw)
1399
+ };
1400
+ } catch {
1401
+ return {
1402
+ complete: false
1403
+ };
1404
+ }
1405
+ }
1406
+ }
1407
+ }
1408
+ return {
1409
+ complete: false
1410
+ };
1411
+ };
1412
+ var extractCompletedFieldValues = (args) => {
1413
+ const located = locateValueStarts(args);
1414
+ const values = [];
1415
+ for (const match of located) {
1416
+ const parsed = extractCompletedValueAt({
1417
+ text: args.text,
1418
+ start: match.start
1419
+ });
1420
+ if (!parsed.complete) continue;
1421
+ values.push({
1422
+ path: match.path,
1423
+ value: parsed.value
1424
+ });
1425
+ }
1426
+ return values;
1427
+ };
1428
+ var extractCompletedArrayItems = (args) => {
1429
+ const located = locateValueStart(args);
1430
+ if (!located.found || located.start === void 0) return [];
1431
+ const arrayStart = skipWhitespace(args.text, located.start);
1432
+ if (args.text[arrayStart] !== "[") return [];
1433
+ const start = arrayStart + 1;
1434
+ const items = [];
1435
+ let inString = false;
1436
+ let isEscaped = false;
1437
+ let nesting = 0;
1438
+ let itemStart = -1;
1439
+ for (let i = start; i < args.text.length; i += 1) {
1440
+ const ch = args.text[i];
1441
+ if (ch === void 0) break;
1442
+ if (inString) {
1443
+ if (isEscaped) {
1444
+ isEscaped = false;
1445
+ continue;
1446
+ }
1447
+ if (ch === "\\") {
1448
+ isEscaped = true;
1449
+ continue;
1450
+ }
1451
+ if (ch === '"') {
1452
+ inString = false;
1453
+ }
1454
+ continue;
1455
+ }
1456
+ if (ch === '"') {
1457
+ inString = true;
1458
+ if (itemStart < 0) itemStart = i;
1459
+ continue;
1460
+ }
1461
+ if (itemStart < 0) {
1462
+ if (/\s/.test(ch) || ch === ",") continue;
1463
+ if (ch === "]") break;
1464
+ itemStart = i;
1465
+ if (ch === "{" || ch === "[") {
1466
+ nesting = 1;
1467
+ } else {
1468
+ nesting = 0;
1469
+ }
1470
+ continue;
1471
+ }
1472
+ if (ch === "{" || ch === "[") {
1473
+ nesting += 1;
1474
+ continue;
1475
+ }
1476
+ if (ch === "}" || ch === "]") {
1477
+ if (nesting > 0) {
1478
+ nesting -= 1;
1479
+ continue;
1480
+ }
1481
+ }
1482
+ if (nesting === 0 && (ch === "," || ch === "]")) {
1483
+ const raw = args.text.slice(itemStart, i).trim();
1484
+ if (raw) {
1485
+ try {
1486
+ items.push(JSON.parse(raw));
1487
+ } catch {
1488
+ }
1489
+ }
1490
+ itemStart = -1;
1491
+ if (ch === "]") break;
1492
+ }
1493
+ }
1494
+ return items;
1495
+ };
1496
+ var extractCompletedArrayItemGroups = (args) => {
1497
+ return locateValueStarts(args).flatMap((match) => {
1498
+ const items = extractCompletedArrayItems({
1499
+ text: args.text,
1500
+ path: match.path
1501
+ });
1502
+ return items.length > 0 ? [
1503
+ {
1504
+ path: match.path,
1505
+ items
1506
+ }
1507
+ ] : [];
1508
+ });
1509
+ };
1510
+ var extractStreamingStringValue = (args) => {
1511
+ const located = locateValueStart(args);
1512
+ if (!located.found || located.start === void 0) {
1513
+ return {
1514
+ found: located.found,
1515
+ complete: false,
1516
+ value: ""
1517
+ };
1518
+ }
1519
+ const stringStart = skipWhitespace(args.text, located.start);
1520
+ if (args.text[stringStart] !== '"') {
1521
+ return {
1522
+ found: true,
1523
+ complete: false,
1524
+ value: ""
1525
+ };
1526
+ }
1527
+ let value = "";
1528
+ for (let i = stringStart + 1; i < args.text.length; i += 1) {
1529
+ const ch = args.text[i];
1530
+ if (ch === void 0) break;
1531
+ if (ch === '"') {
1532
+ return {
1533
+ found: true,
1534
+ complete: true,
1535
+ value
1536
+ };
1537
+ }
1538
+ if (ch !== "\\") {
1539
+ value += ch;
1540
+ continue;
1541
+ }
1542
+ const next = args.text[i + 1];
1543
+ if (!next) {
1544
+ return {
1545
+ found: true,
1546
+ complete: false,
1547
+ value
1548
+ };
1549
+ }
1550
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1551
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1552
+ i += 1;
1553
+ continue;
1554
+ }
1555
+ if (next === "u") {
1556
+ const hex = args.text.slice(i + 2, i + 6);
1557
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1558
+ return {
1559
+ found: true,
1560
+ complete: false,
1561
+ value
1562
+ };
1563
+ }
1564
+ value += String.fromCharCode(Number.parseInt(hex, 16));
1565
+ i += 5;
1566
+ continue;
1567
+ }
1568
+ return {
1569
+ found: true,
1570
+ complete: false,
1571
+ value
1572
+ };
1573
+ }
1574
+ return {
1575
+ found: true,
1576
+ complete: false,
1577
+ value
1578
+ };
1579
+ };
1580
+ var extractStreamingStringValues = (args) => {
1581
+ return locateValueStarts(args).flatMap((match) => {
1582
+ const current = extractStreamingStringValue({
1583
+ text: args.text,
1584
+ path: match.path
1585
+ });
1586
+ return current.found ? [
1587
+ {
1588
+ path: match.path,
1589
+ complete: current.complete,
1590
+ value: current.value
1591
+ }
1592
+ ] : [];
1593
+ });
1594
+ };
1595
+ var import_core42 = require("@kortyx/core");
1596
+ var toErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
1597
+ var toToolDefinitions = (tools) => tools.map((tool) => ({
1598
+ name: tool.name,
1599
+ ...tool.title ? { title: tool.title } : {},
1600
+ ...tool.description ? { description: tool.description } : {},
1601
+ inputSchema: tool.inputSchema,
1602
+ ...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
1603
+ ...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
1604
+ ...tool.metadata !== void 0 ? { metadata: tool.metadata } : {}
1605
+ }));
1606
+ var resolveToolExecutionBoolean = (value, toolName, fallback) => {
1607
+ if (typeof value === "boolean") return value;
1608
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1609
+ return value[toolName] ?? fallback;
1610
+ }
1611
+ return fallback;
1612
+ };
1613
+ var normalizeToolResult = (toolCall, value) => {
1614
+ if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
1615
+ return value;
1616
+ }
1617
+ let content;
1618
+ try {
1619
+ content = typeof value === "string" ? value : JSON.stringify(value);
1620
+ } catch {
1621
+ content = String(value);
1622
+ }
1623
+ return {
1624
+ toolCallId: toolCall.id,
1625
+ name: toolCall.name,
1626
+ content,
1627
+ ...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
1628
+ };
1629
+ };
1630
+ var closeOwnedTools = async (tools) => {
1631
+ const closeFns = /* @__PURE__ */ new Set();
1632
+ for (const tool of tools) {
1633
+ if (tool.closeAfterUse === false || !tool.close) continue;
1634
+ closeFns.add(tool.close);
1635
+ }
1636
+ await Promise.all(
1637
+ [...closeFns].map(async (close) => {
1638
+ await close();
1639
+ })
1640
+ );
1641
+ };
1642
+ var createInitialMessages = (args) => [
1643
+ ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
1644
+ { role: "user", content: String(args.input ?? "") }
1645
+ ];
1646
+ var runReasonToolLoop = async (args) => {
1647
+ const { useReasonArgs, id, opId, traceSpan } = args;
1648
+ const ctx = getHookContext();
1649
+ const abortSignal = (0, import_core42.combineAbortSignals)(
1650
+ ctx.node.abortSignal,
1651
+ useReasonArgs.abortSignal,
1652
+ useReasonArgs.model.options?.abortSignal
1653
+ );
1654
+ const tools = useReasonArgs.tools ?? [];
1655
+ const toolByName = /* @__PURE__ */ new Map();
1656
+ let validationCompleted = false;
1657
+ try {
1658
+ for (const tool of tools) {
1659
+ if (toolByName.has(tool.name)) {
1660
+ throw new Error(
1661
+ `useReason received duplicate tool name "${tool.name}".`
1662
+ );
1663
+ }
1664
+ toolByName.set(tool.name, tool);
1665
+ }
1666
+ if (useReasonArgs.interrupt) {
1667
+ throw new Error(
1668
+ "useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
1669
+ );
1670
+ }
1671
+ validationCompleted = true;
1672
+ } finally {
1673
+ if (!validationCompleted) {
1674
+ await closeOwnedTools(tools);
1675
+ }
1676
+ }
1677
+ const toolDefinitions = toToolDefinitions(tools);
1678
+ const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
1679
+ const messages = createInitialMessages({
1680
+ system: useReasonArgs.system,
1681
+ input: useReasonArgs.input
1682
+ });
1683
+ const steps = [];
1684
+ const allToolCalls = [];
1685
+ const allToolResults = [];
1686
+ let finalText = "";
1687
+ let finalRaw;
1688
+ let finalOutput;
1689
+ let aggregatedUsage;
1690
+ let finalFinishReason;
1691
+ let aggregatedProviderMetadata;
1692
+ let aggregatedWarnings;
1693
+ const emitToolEvent = (event, payload) => {
1694
+ ctx.node.emit(event, {
1695
+ ...payload,
1696
+ node: ctx.node.graph.node,
1697
+ ...id ? { id } : {},
1698
+ opId
1699
+ });
1700
+ };
1701
+ try {
1702
+ for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
1703
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
1704
+ traceSpan?.addEvent?.("useReason.tool-step.start", {
1705
+ stepIndex,
1706
+ toolCount: toolDefinitions.length
1707
+ });
1708
+ const step = await reasonEngine(
1709
+ {
1710
+ ...useReasonArgs,
1711
+ tools: toolDefinitions,
1712
+ messages,
1713
+ emit: false,
1714
+ stream: false
1715
+ },
1716
+ { ...id ? { id } : {}, opId }
1717
+ );
1718
+ finalText = step.text;
1719
+ finalRaw = step.raw;
1720
+ aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
1721
+ accumulateTokenUsage(step.usage);
1722
+ finalFinishReason = step.finishReason;
1723
+ aggregatedProviderMetadata = mergeProviderMetadata(
1724
+ aggregatedProviderMetadata,
1725
+ step.providerMetadata
1726
+ );
1727
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
1728
+ const toolCalls = step.toolCalls ?? [];
1729
+ const toolResults = [];
1730
+ steps.push({
1731
+ stepIndex,
1732
+ text: step.text,
1733
+ toolCalls,
1734
+ toolResults,
1735
+ ...step.usage ? { usage: step.usage } : {},
1736
+ ...step.finishReason ? { finishReason: step.finishReason } : {},
1737
+ ...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
1738
+ ...step.warnings ? { warnings: step.warnings } : {}
1739
+ });
1740
+ if (toolCalls.length === 0) {
1741
+ if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
1742
+ emitToolEvent("text-start", {});
1743
+ emitToolEvent("text-delta", { delta: step.text });
1744
+ emitToolEvent("text-end", {});
1745
+ }
1746
+ break;
1747
+ }
1748
+ allToolCalls.push(...toolCalls);
1749
+ messages.push({
1750
+ role: "assistant",
1751
+ content: step.text,
1752
+ toolCalls
1753
+ });
1754
+ for (const toolCall of toolCalls) {
1755
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
1756
+ const tool = toolByName.get(toolCall.name);
1757
+ if (!tool) {
1758
+ throw new Error(
1759
+ `Model requested unknown tool "${toolCall.name}" from useReason.`
1760
+ );
1761
+ }
1762
+ const shouldEmitTool = resolveToolExecutionBoolean(
1763
+ useReasonArgs.toolExecution?.emit,
1764
+ tool.name,
1765
+ false
1766
+ );
1767
+ const shouldApproveTool = resolveToolExecutionBoolean(
1768
+ useReasonArgs.toolExecution?.approval,
1769
+ tool.name,
1770
+ false
1771
+ );
1772
+ traceSpan?.addEvent?.("useReason.tool-call.start", {
1773
+ stepIndex,
1774
+ tool: tool.name,
1775
+ toolCallId: toolCall.id
1776
+ });
1777
+ if (shouldEmitTool) {
1778
+ emitToolEvent("tool-call-start", {
1779
+ tool: tool.name,
1780
+ toolCallId: toolCall.id,
1781
+ input: toolCall.input
1782
+ });
1783
+ }
1784
+ if (shouldApproveTool) {
1785
+ const approval = await awaitInterruptInternal({
1786
+ id: `tool:${toolCall.id}`,
1787
+ request: {
1788
+ kind: "choice",
1789
+ question: `Approve ${tool.name}?`,
1790
+ options: [
1791
+ { id: "approve", label: "Approve" },
1792
+ { id: "deny", label: "Deny" }
1793
+ ],
1794
+ meta: {
1795
+ tool: tool.name,
1796
+ toolCallId: toolCall.id,
1797
+ input: toolCall.input
1798
+ }
1799
+ }
1800
+ });
1801
+ const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
1802
+ if (!approved) {
1803
+ const denied = {
1804
+ toolCallId: toolCall.id,
1805
+ name: tool.name,
1806
+ content: "Tool call denied by user.",
1807
+ isError: true
1808
+ };
1809
+ toolResults.push(denied);
1810
+ allToolResults.push(denied);
1811
+ if (shouldEmitTool) {
1812
+ emitToolEvent("tool-call-result", {
1813
+ tool: tool.name,
1814
+ toolCallId: toolCall.id,
1815
+ content: denied.content,
1816
+ isError: true
1817
+ });
1818
+ }
1819
+ continue;
1820
+ }
1821
+ }
1822
+ try {
1823
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
1824
+ ctx.node.consumeExecution?.("maxToolCalls");
1825
+ const rawResult = await tool.execute(toolCall.input, {
1826
+ toolCallId: toolCall.id,
1827
+ ...abortSignal ? { abortSignal } : {}
1828
+ });
1829
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
1830
+ const result2 = normalizeToolResult(toolCall, rawResult);
1831
+ toolResults.push(result2);
1832
+ allToolResults.push(result2);
1833
+ if (shouldEmitTool) {
1834
+ emitToolEvent("tool-call-result", {
1835
+ tool: result2.name,
1836
+ toolCallId: result2.toolCallId,
1837
+ content: result2.content,
1838
+ ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
1839
+ ...result2.isError !== void 0 ? { isError: result2.isError } : {}
1840
+ });
1841
+ }
1842
+ traceSpan?.addEvent?.("useReason.tool-call.complete", {
1843
+ stepIndex,
1844
+ tool: result2.name,
1845
+ toolCallId: result2.toolCallId,
1846
+ isError: Boolean(result2.isError)
1847
+ });
1848
+ } catch (error) {
1849
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
1850
+ if ((0, import_core42.isExecutionLimitReached)(error) || (0, import_core42.isExecutionCancelled)(error))
1851
+ throw error;
1852
+ const result2 = {
1853
+ toolCallId: toolCall.id,
1854
+ name: tool.name,
1855
+ content: toErrorMessage2(error),
1856
+ isError: true
1857
+ };
1858
+ toolResults.push(result2);
1859
+ allToolResults.push(result2);
1860
+ if (shouldEmitTool) {
1861
+ emitToolEvent("tool-call-error", {
1862
+ tool: tool.name,
1863
+ toolCallId: toolCall.id,
1864
+ message: result2.content
1865
+ });
1866
+ }
1867
+ traceSpan?.addEvent?.("useReason.tool-call.error", {
1868
+ stepIndex,
1869
+ tool: tool.name,
1870
+ toolCallId: toolCall.id,
1871
+ message: result2.content
1872
+ });
1873
+ }
1874
+ }
1875
+ const currentStep = steps[steps.length - 1];
1876
+ if (currentStep) {
1877
+ steps[steps.length - 1] = {
1878
+ ...currentStep,
1879
+ toolResults
1880
+ };
1881
+ }
1882
+ for (const result2 of toolResults) {
1883
+ messages.push({
1884
+ role: "tool",
1885
+ content: result2.content,
1886
+ toolCallId: result2.toolCallId,
1887
+ name: result2.name,
1888
+ ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
1889
+ ...result2.isError !== void 0 ? { isError: result2.isError } : {},
1890
+ ...result2.raw !== void 0 ? { raw: result2.raw } : {}
1891
+ });
1892
+ }
1893
+ }
1894
+ if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
1895
+ throw new Error(
1896
+ `useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
1897
+ );
1898
+ }
1899
+ if (useReasonArgs.outputSchema) {
1900
+ finalOutput = parseReasonOutputWithSchema({
1901
+ text: finalText,
1902
+ schema: useReasonArgs.outputSchema,
1903
+ ...finalFinishReason ? { finishReason: finalFinishReason } : {},
1904
+ label: "useReason output"
1905
+ });
1906
+ }
1907
+ if (finalOutput !== void 0) {
1908
+ emitReasonStructuredOutput({
1909
+ ...id ? { id } : {},
1910
+ opId,
1911
+ output: finalOutput,
1912
+ structured: useReasonArgs.structured,
1913
+ emit: useReasonArgs.emit ?? true
1914
+ });
1915
+ }
1916
+ const result = {
1917
+ ...id ? { id } : {},
1918
+ opId,
1919
+ text: finalText,
1920
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
1921
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
1922
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
1923
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
1924
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
1925
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
1926
+ toolCalls: allToolCalls,
1927
+ toolResults: allToolResults,
1928
+ steps
1929
+ };
1930
+ traceSpan?.end?.({
1931
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
1932
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
1933
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
1934
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
1935
+ attributes: {
1936
+ textLength: finalText.length,
1937
+ toolCallCount: allToolCalls.length,
1938
+ toolResultCount: allToolResults.length,
1939
+ toolStepCount: steps.length
1940
+ },
1941
+ telemetry: {
1942
+ ...useReasonArgs.telemetry ?? {},
1943
+ output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
1944
+ }
1945
+ });
1946
+ return result;
1947
+ } catch (error) {
1948
+ traceSpan?.fail?.(error, {
1949
+ attributes: {
1950
+ toolStepCount: steps.length,
1951
+ toolCallCount: allToolCalls.length
1952
+ }
1953
+ });
1954
+ throw error;
1955
+ } finally {
1956
+ await closeOwnedTools(tools);
1957
+ }
1958
+ };
1959
+ var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
1960
+ var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
1961
+ var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
1962
+ var assertReasoningThoughtsCompatibility = (args) => {
1963
+ if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
1964
+ const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
1965
+ if (!usesStructuredOutput) return;
1966
+ throw new Error(
1967
+ "useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
1968
+ );
1969
+ };
1970
+ async function useReason(args) {
1971
+ assertReasoningThoughtsCompatibility(args);
1972
+ const ctx = getHookContext();
1973
+ const abortSignal = (0, import_core52.combineAbortSignals)(
1974
+ ctx.node.abortSignal,
1975
+ args.abortSignal,
1976
+ args.model.options?.abortSignal
1977
+ );
1978
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
1979
+ args = { ...args, abortSignal };
1980
+ const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
1981
+ const opId = createRuntimeId2();
1982
+ const checkpointKey = resolveReasonCheckpointKey({
1983
+ ...id ? { id } : {},
1984
+ autoIndex: ctx.reasonCallIndex++
1985
+ });
1986
+ const existingCompleted = readReasonCompletedCheckpoint(
1987
+ ctx.currentNodeState.byKey[checkpointKey]
1988
+ );
1989
+ if (existingCompleted) {
1990
+ return existingCompleted.result;
1991
+ }
1992
+ const existingCheckpoint = readReasonCheckpoint(
1993
+ ctx.currentNodeState.byKey[checkpointKey]
1994
+ );
1995
+ const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
1996
+ const traceSpan = getReasonTraceAdapter()?.startSpan({
1997
+ name: "useReason",
1998
+ attributes: {
1999
+ ...id ? { id } : {},
2000
+ opId,
2001
+ nodeId: ctx.node.graph.node,
2002
+ providerId: args.model.provider.id,
2003
+ modelId: args.model.modelId,
2004
+ stream: args.stream ?? args.model.options?.streaming ?? true,
2005
+ emit: args.emit ?? true,
2006
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
2007
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
2008
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
2009
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
2010
+ hasOutputSchema: Boolean(args.outputSchema),
2011
+ hasInterrupt: Boolean(args.interrupt),
2012
+ hasStructured: Boolean(args.structured),
2013
+ hasTools: Boolean(args.tools?.length)
2014
+ },
2015
+ telemetry: {
2016
+ ...args.telemetry ?? {},
2017
+ input: args.telemetry?.input ?? args.input
2018
+ }
2019
+ });
2020
+ if (args.tools?.length) {
2021
+ return runReasonToolLoop({
2022
+ useReasonArgs: args,
2023
+ ...id ? { id } : {},
2024
+ opId,
2025
+ ...traceSpan ? { traceSpan } : {}
2026
+ });
2027
+ }
2028
+ const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
2029
+ const setFieldPaths = resolveSetFieldPaths(args.structured);
2030
+ const appendFieldPaths = resolveAppendFieldPaths(args.structured);
2031
+ const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
2032
+ const useStructuredIncrementalStreaming = Boolean(
2033
+ args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
2034
+ );
2035
+ let firstText = "";
2036
+ let firstRaw;
2037
+ let firstOutput;
2038
+ let firstInterruptRequest;
2039
+ let firstInterruptRequired = false;
2040
+ let finalText = "";
2041
+ let finalRaw;
2042
+ let finalOutput;
2043
+ let aggregatedUsage;
2044
+ let finalFinishReason;
2045
+ let aggregatedProviderMetadata;
2046
+ let aggregatedWarnings;
2047
+ const emittedSetValues = /* @__PURE__ */ new Map();
2048
+ const emittedAppendCounts = /* @__PURE__ */ new Map();
2049
+ const emittedTextValues = /* @__PURE__ */ new Map();
2050
+ if (existingCheckpoint) {
2051
+ firstText = existingCheckpoint.firstText;
2052
+ firstRaw = existingCheckpoint.firstRaw;
2053
+ firstOutput = existingCheckpoint.firstOutput;
2054
+ finalText = firstText;
2055
+ finalRaw = firstRaw;
2056
+ finalOutput = firstOutput;
2057
+ aggregatedUsage = existingCheckpoint.firstUsage;
2058
+ finalFinishReason = existingCheckpoint.firstFinishReason;
2059
+ aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
2060
+ aggregatedWarnings = existingCheckpoint.firstWarnings;
2061
+ traceSpan?.addEvent?.("useReason.resume", {
2062
+ checkpointKey
2063
+ });
2064
+ } else {
2065
+ const first = await reasonEngine(
2066
+ {
2067
+ ...args,
2068
+ emit: suppressTextStream ? false : args.emit,
2069
+ stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
2070
+ ...useStructuredIncrementalStreaming ? {
2071
+ onTextChunk: (delta) => {
2072
+ firstText += delta;
2073
+ finalText = firstText;
2074
+ for (const fieldPath of setFieldPaths) {
2075
+ const values = extractCompletedFieldValues({
2076
+ text: firstText,
2077
+ path: fieldPath
2078
+ });
2079
+ for (const { path, value } of values) {
2080
+ const nextSerialized = JSON.stringify(value);
2081
+ if (emittedSetValues.get(path) === nextSerialized) continue;
2082
+ emittedSetValues.set(path, nextSerialized);
2083
+ emitStructuredData({
2084
+ kind: "set",
2085
+ path,
2086
+ value,
2087
+ dataType: args.structured?.dataType ?? "reason-output",
2088
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2089
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2090
+ ...id ? { id } : {},
2091
+ streamId: opId
2092
+ });
2093
+ }
2094
+ }
2095
+ for (const fieldPath of appendFieldPaths) {
2096
+ const groups = extractCompletedArrayItemGroups({
2097
+ text: firstText,
2098
+ path: fieldPath
2099
+ });
2100
+ for (const { path, items } of groups) {
2101
+ const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
2102
+ if (items.length <= emittedAppendCount) continue;
2103
+ const nextItems = items.slice(emittedAppendCount);
2104
+ emittedAppendCounts.set(path, items.length);
2105
+ emitStructuredData({
2106
+ kind: "append",
2107
+ path,
2108
+ items: nextItems,
2109
+ dataType: args.structured?.dataType ?? "reason-output",
2110
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2111
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2112
+ ...id ? { id } : {},
2113
+ streamId: opId
2114
+ });
2115
+ }
2116
+ }
2117
+ for (const fieldPath of textDeltaFieldPaths) {
2118
+ const values = extractStreamingStringValues({
2119
+ text: firstText,
2120
+ path: fieldPath
2121
+ });
2122
+ for (const { path, value } of values) {
2123
+ const emittedTextValue = emittedTextValues.get(path) ?? "";
2124
+ if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
2125
+ continue;
2126
+ }
2127
+ const nextDelta = value.slice(emittedTextValue.length);
2128
+ emittedTextValues.set(path, value);
2129
+ if (nextDelta.length === 0) continue;
2130
+ emitStructuredData({
2131
+ kind: "text-delta",
2132
+ path,
2133
+ delta: nextDelta,
2134
+ dataType: args.structured?.dataType ?? "reason-output",
2135
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2136
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2137
+ ...id ? { id } : {},
2138
+ streamId: opId
2139
+ });
2140
+ }
2141
+ }
2142
+ }
2143
+ } : {}
2144
+ },
2145
+ reasonMeta,
2146
+ args.interrupt ? defaultInterruptFirstPassInput({
2147
+ input: args.input,
2148
+ requestSchema: args.interrupt.requestSchema,
2149
+ mode: args.interrupt.mode ?? "required",
2150
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
2151
+ }) : args.outputSchema ? withOutputGuardrails(
2152
+ withStructuredStreamHints(args.input, {
2153
+ ...setFieldPaths.length > 0 ? { setFieldPaths } : {},
2154
+ ...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
2155
+ ...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
2156
+ }),
2157
+ args.outputSchema
2158
+ ) : void 0
2159
+ );
2160
+ firstRaw = first.raw;
2161
+ finalRaw = first.raw;
2162
+ accumulateTokenUsage(first.usage);
2163
+ aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
2164
+ finalFinishReason = first.finishReason;
2165
+ aggregatedProviderMetadata = mergeProviderMetadata(
2166
+ aggregatedProviderMetadata,
2167
+ first.providerMetadata
2168
+ );
2169
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
2170
+ traceSpan?.addEvent?.("useReason.first-pass.complete", {
2171
+ textLength: first.text.length,
2172
+ hasInterrupt: Boolean(args.interrupt)
2173
+ });
2174
+ if (args.interrupt) {
2175
+ const firstPass = parseInterruptFirstPassResult({
2176
+ text: first.text,
2177
+ requestSchema: args.interrupt.requestSchema,
2178
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
2179
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {},
2180
+ mode: args.interrupt.mode ?? "required"
2181
+ });
2182
+ firstText = firstPass.draftText;
2183
+ finalText = firstPass.draftText;
2184
+ firstInterruptRequired = firstPass.interruptRequired;
2185
+ firstInterruptRequest = firstPass.request;
2186
+ firstOutput = firstPass.output;
2187
+ finalOutput = firstPass.output;
2188
+ } else {
2189
+ firstText = first.text;
2190
+ finalText = first.text;
2191
+ if (args.outputSchema) {
2192
+ firstOutput = parseReasonOutputWithSchema({
2193
+ text: first.text,
2194
+ schema: args.outputSchema,
2195
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
2196
+ label: "useReason output"
2197
+ });
2198
+ }
2199
+ finalOutput = firstOutput;
2200
+ }
2201
+ }
2202
+ let interruptResponse;
2203
+ if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
2204
+ const requestSchema = args.interrupt.requestSchema;
2205
+ const interruptRequest = existingCheckpoint ? parseWithSchema(
2206
+ requestSchema,
2207
+ existingCheckpoint.request,
2208
+ "useReason interrupt.request"
2209
+ ) : firstInterruptRequest;
2210
+ if (!interruptRequest) {
2211
+ throw new Error(
2212
+ "useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
2213
+ );
2214
+ }
2215
+ if (!existingCheckpoint) {
2216
+ ctx.currentNodeState.byKey[checkpointKey] = {
2217
+ status: "awaiting_interrupt",
2218
+ request: interruptRequest,
2219
+ firstText,
2220
+ ...firstRaw !== void 0 ? { firstRaw } : {},
2221
+ ...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
2222
+ ...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
2223
+ ...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
2224
+ ...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
2225
+ ...firstOutput !== void 0 ? { firstOutput } : {}
2226
+ };
2227
+ ctx.stateDirty = true;
2228
+ }
2229
+ const resumeStatePatch = resolveHookStatePatch({
2230
+ nodeId: ctx.node.graph.node,
2231
+ currentNodeState: ctx.currentNodeState,
2232
+ workflowState: ctx.workflowState
2233
+ });
2234
+ interruptResponse = await awaitInterruptInternal({
2235
+ request: interruptRequest,
2236
+ requestSchema,
2237
+ responseSchema: args.interrupt.responseSchema,
2238
+ ...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
2239
+ ...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
2240
+ ...id ? { id } : {},
2241
+ meta: {
2242
+ __kortyxResumeStatePatch: resumeStatePatch
2243
+ }
2244
+ });
2245
+ traceSpan?.addEvent?.("useReason.interrupt.resolved", {
2246
+ checkpointKey
2247
+ });
2248
+ if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
2249
+ delete ctx.currentNodeState.byKey[checkpointKey];
2250
+ ctx.stateDirty = true;
2251
+ }
2252
+ const continuationInput = defaultContinuationInput({
2253
+ input: args.input,
2254
+ draftText: firstText,
2255
+ ...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
2256
+ request: interruptRequest,
2257
+ response: interruptResponse
2258
+ });
2259
+ const second = await reasonEngine(
2260
+ {
2261
+ ...args,
2262
+ emit: suppressTextStream ? false : args.emit,
2263
+ stream: suppressTextStream ? false : args.stream
2264
+ },
2265
+ reasonMeta,
2266
+ args.outputSchema ? withOutputGuardrails(
2267
+ continuationInput,
2268
+ args.outputSchema
2269
+ ) : continuationInput
2270
+ );
2271
+ finalText = second.text;
2272
+ finalRaw = second.raw;
2273
+ accumulateTokenUsage(second.usage);
2274
+ aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
2275
+ finalFinishReason = second.finishReason;
2276
+ aggregatedProviderMetadata = mergeProviderMetadata(
2277
+ aggregatedProviderMetadata,
2278
+ second.providerMetadata
2279
+ );
2280
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
2281
+ traceSpan?.addEvent?.("useReason.continuation.complete", {
2282
+ textLength: second.text.length
2283
+ });
2284
+ if (args.outputSchema) {
2285
+ finalOutput = parseReasonOutputWithSchema({
2286
+ text: finalText,
2287
+ schema: args.outputSchema,
2288
+ ...second.finishReason ? { finishReason: second.finishReason } : {},
2289
+ label: "useReason output"
2290
+ });
2291
+ }
2292
+ }
2293
+ if (finalOutput !== void 0) {
2294
+ emitReasonStructuredOutput({
2295
+ ...id ? { id } : {},
2296
+ opId,
2297
+ output: finalOutput,
2298
+ structured: args.structured,
2299
+ emit: args.emit ?? true
2300
+ });
2301
+ }
2302
+ const result = {
2303
+ ...id ? { id } : {},
2304
+ opId,
2305
+ text: finalText,
2306
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
2307
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2308
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2309
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2310
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2311
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
2312
+ ...interruptResponse !== void 0 ? { interruptResponse } : {}
2313
+ };
2314
+ if (args.interrupt) {
2315
+ ctx.currentNodeState.byKey[checkpointKey] = {
2316
+ status: "completed",
2317
+ result
2318
+ };
2319
+ ctx.stateDirty = true;
2320
+ }
2321
+ traceSpan?.end?.({
2322
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2323
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2324
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2325
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2326
+ attributes: {
2327
+ textLength: finalText.length,
2328
+ resumedFromCheckpoint: Boolean(existingCheckpoint),
2329
+ interrupted: Boolean(interruptResponse !== void 0)
2330
+ },
2331
+ telemetry: {
2332
+ ...args.telemetry ?? {},
2333
+ output: args.telemetry?.output ?? finalOutput ?? finalText
2334
+ }
2335
+ });
2336
+ return result;
2337
+ }
2338
+ function useStructuredData(args) {
2339
+ emitStructuredData(args);
2340
+ }
2341
+ function useReason2(args) {
2342
+ return useReason(args);
2343
+ }
2344
+ function useInterrupt(args) {
2345
+ return awaitInterruptInternal(args);
2346
+ }
2347
+ function useAbortSignal() {
2348
+ return getHookContext().node.abortSignal;
2349
+ }
2350
+ function useRuntimeContext() {
2351
+ const ctx = getHookContext();
2352
+ const config = ctx.state.config;
2353
+ const context = config && typeof config === "object" && "context" in config ? config.context : void 0;
2354
+ return context && typeof context === "object" && !Array.isArray(context) ? context : {};
2355
+ }
2356
+ function useNodeState(initialValue) {
2357
+ const ctx = getHookContext();
2358
+ const nodeState = ctx.currentNodeState;
2359
+ const index = ctx.nodeStateIndex++;
2360
+ if (index >= nodeState.byIndex.length) {
2361
+ nodeState.byIndex[index] = initialValue;
2362
+ ctx.stateDirty = true;
2363
+ }
2364
+ const getValue = () => nodeState.byIndex[index];
2365
+ const setValue = (next) => {
2366
+ const prev = getValue();
2367
+ const resolved = typeof next === "function" ? next(prev) : next;
2368
+ nodeState.byIndex[index] = resolved;
2369
+ ctx.stateDirty = true;
2370
+ };
2371
+ return [getValue(), setValue];
2372
+ }
2373
+ function useWorkflowState(key, ...rest) {
2374
+ const ctx = getHookContext();
2375
+ const workflowState = ctx.workflowState;
2376
+ const hasInitial = rest.length > 0;
2377
+ const initialValue = rest[0];
2378
+ if (!Object.hasOwn(workflowState, key) && hasInitial) {
2379
+ workflowState[key] = initialValue;
2380
+ ctx.stateDirty = true;
2381
+ }
2382
+ const getValue = () => workflowState[key];
2383
+ const setValue = (next) => {
2384
+ const prev = getValue();
2385
+ const resolved = typeof next === "function" ? next(prev) : next;
2386
+ workflowState[key] = resolved;
2387
+ ctx.stateDirty = true;
2388
+ };
2389
+ return [getValue(), setValue];
2390
+ }
2391
+ var import_node_crypto5 = require("crypto");
2392
+ var import_core6 = require("@kortyx/core");
2393
+ var import_node_crypto42 = require("crypto");
2394
+ function emitWorkflowCall2(telemetry, type, payload) {
2395
+ const base = telemetry?.correlation;
2396
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
2397
+ return;
2398
+ try {
2399
+ const active = telemetry.trace?.getActiveContext?.();
2400
+ void Promise.resolve(
2401
+ telemetry.reporter.emit([
2402
+ {
2403
+ schemaVersion: 1,
2404
+ eventId: (0, import_node_crypto42.randomUUID)(),
2405
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
2406
+ environment: telemetry.environment,
2407
+ service: telemetry.service,
2408
+ correlation: {
2409
+ ...base,
2410
+ runId: base.runId,
2411
+ workflowId: base.workflowId,
2412
+ ...active
2413
+ },
2414
+ type,
2415
+ payload
2416
+ }
2417
+ ])
2418
+ ).catch(() => {
2419
+ });
2420
+ } catch {
2421
+ }
2422
+ }
2423
+ function workflowCallContent(telemetry, side, value) {
2424
+ const policy = telemetry?.captureContent;
2425
+ if (!(policy === true || policy && typeof policy === "object" && policy[side]))
2426
+ return {};
2427
+ const serialized = JSON.stringify(value);
2428
+ return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
2429
+ }
2430
+ var WorkflowCallError = class extends Error {
2431
+ constructor(workflow, message) {
2432
+ super(`Workflow '${workflow}': ${message}`);
2433
+ this.workflow = workflow;
2434
+ this.name = "WorkflowCallError";
2435
+ }
2436
+ workflow;
2437
+ };
2438
+ function workflowCallFingerprint(value) {
2439
+ const seen = /* @__PURE__ */ new Set();
2440
+ const canonical = (v) => {
2441
+ if (v === null || typeof v === "string" || typeof v === "boolean") return v;
2442
+ if (typeof v === "number" && Number.isFinite(v)) return v;
2443
+ if (typeof v !== "object" || !v || seen.has(v)) {
2444
+ throw new Error("Workflow input and output must be JSON serializable.");
2445
+ }
2446
+ if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
2447
+ throw new Error(
2448
+ "Workflow input and output must contain plain JSON objects."
2449
+ );
2450
+ }
2451
+ seen.add(v);
2452
+ const result = Array.isArray(v) ? v.map(canonical) : Object.fromEntries(
2453
+ Object.keys(v).sort().map((key) => [
2454
+ key,
2455
+ canonical(v[key])
2456
+ ])
2457
+ );
2458
+ seen.delete(v);
2459
+ return result;
2460
+ };
2461
+ return JSON.stringify(canonical(value));
2462
+ }
2463
+ function createWorkflowHooks(workflows) {
2464
+ return {
2465
+ useWorkflow: (args) => {
2466
+ const workflow = workflows[args.workflow];
2467
+ if (!workflow || workflow.id !== args.workflow)
2468
+ throw new Error("Workflow registry keys must match definition ids.");
2469
+ return useWorkflow({ ...args, workflow });
2470
+ }
2471
+ };
2472
+ }
2473
+ async function useWorkflow(args) {
2474
+ const ctx = getHookContext();
2475
+ (0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
2476
+ const service = ctx.node.callWorkflow;
2477
+ if (!service)
2478
+ throw new Error("useWorkflow requires an agent workflow registry.");
2479
+ if (!args.id.trim())
2480
+ throw new Error("useWorkflow requires a stable, nonempty id.");
2481
+ if (ctx.workflowCallActive)
2482
+ throw new Error(
2483
+ "Concurrent workflow calls are not supported; await each call."
2484
+ );
2485
+ if (ctx.workflowCallIds.has(args.id))
2486
+ throw new Error(
2487
+ `Duplicate workflow call id '${args.id}' in one node activation.`
2488
+ );
2489
+ if (ctx.workflowCallIds.size >= 64)
2490
+ throw new Error(
2491
+ "A node may call at most 64 child workflows per activation."
2492
+ );
2493
+ ctx.workflowCallIds.add(args.id);
2494
+ const definition = typeof args.workflow === "string" ? void 0 : args.workflow;
2495
+ const workflow = definition?.id ?? args.workflow;
2496
+ const input = args.input;
2497
+ const fingerprint = workflowCallFingerprint({
2498
+ workflow,
2499
+ input,
2500
+ version: definition?.version ?? null
2501
+ });
2502
+ const key = `__useWorkflow:${args.id}`;
2503
+ let record = ctx.currentNodeState.byKey[key];
2504
+ if (record && record.fingerprint !== fingerprint)
2505
+ throw new WorkflowCallError(
2506
+ workflow,
2507
+ "call input or target changed during replay"
2508
+ );
2509
+ if (!record) ctx.node.consumeExecution?.("maxChildInvocations");
2510
+ record ??= {
2511
+ fingerprint,
2512
+ invocationId: (0, import_node_crypto5.randomUUID)(),
2513
+ status: "running",
2514
+ interrupts: []
2515
+ };
2516
+ ctx.currentNodeState.byKey[key] = record;
2517
+ ctx.stateDirty = true;
2518
+ ctx.workflowCallActive = true;
2519
+ const telemetry = ctx.node.workflowCallTelemetry;
2520
+ const activationKey = "__useWorkflowActivation";
2521
+ ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
2522
+ const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
2523
+ const metadata2 = {
2524
+ invocationId: record.invocationId,
2525
+ parentInvocationId: telemetry?.correlation?.invocationId ?? null,
2526
+ branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
2527
+ callId: args.id,
2528
+ callerNodeId: ctx.node.graph.node,
2529
+ callerNodeExecutionId,
2530
+ sourceWorkflowId: ctx.node.graph.name,
2531
+ targetWorkflowId: workflow,
2532
+ targetVersion: definition?.version ?? null
2533
+ };
2534
+ record.telemetry = metadata2;
2535
+ const report = (status, content = {}) => {
2536
+ record.sequence = (record.sequence ?? 0) + 1;
2537
+ emitWorkflowCall2(telemetry, `workflow.call.${status}`, {
2538
+ ...metadata2,
2539
+ sequence: record.sequence,
2540
+ ...content
2541
+ });
2542
+ };
2543
+ const recordUsage = (usage) => {
2544
+ if (!usage) return;
2545
+ accumulateTokenUsage({
2546
+ input: usage.input - (record.usage?.input ?? 0),
2547
+ output: usage.output - (record.usage?.output ?? 0),
2548
+ total: usage.total - (record.usage?.total ?? 0)
2549
+ });
2550
+ record.usage = usage;
2551
+ };
2552
+ const cached = record.status === "completed";
2553
+ try {
2554
+ for (const interruption of record.interrupts) {
2555
+ interruption.response = await awaitInterruptInternal({
2556
+ request: interruption.request,
2557
+ ...interruption.request.meta ? { meta: interruption.request.meta } : {}
2558
+ });
2559
+ }
2560
+ if (record.status === "failed")
2561
+ throw new WorkflowCallError(workflow, record.error ?? "child failed");
2562
+ while (record.status !== "completed") {
2563
+ let outcome;
2564
+ try {
2565
+ report(
2566
+ record.snapshot ? "resumed" : "started",
2567
+ workflowCallContent(telemetry, "input", input)
2568
+ );
2569
+ outcome = await service({
2570
+ callerNodeExecutionId,
2571
+ id: args.id,
2572
+ workflow,
2573
+ input,
2574
+ definition,
2575
+ invocationId: record.invocationId,
2576
+ ...record.snapshot ? { snapshot: record.snapshot } : {},
2577
+ ...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
2578
+ });
2579
+ } catch (error) {
2580
+ (0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
2581
+ if ((0, import_core6.isExecutionCancelled)(error)) throw error;
2582
+ if ((0, import_core6.isExecutionLimitReached)(error)) {
2583
+ const child = error;
2584
+ if (child.__kortyxChildSnapshot)
2585
+ record.snapshot = child.__kortyxChildSnapshot;
2586
+ recordUsage(child.__kortyxChildUsage);
2587
+ report("suspended", {
2588
+ reason: "limit_reached",
2589
+ limit: error.limitReached
2590
+ });
2591
+ throw error;
2592
+ }
2593
+ record.status = "failed";
2594
+ record.error = error instanceof Error ? error.message : String(error);
2595
+ report("failed", { error: "Child workflow failed" });
2596
+ throw new WorkflowCallError(workflow, record.error);
2597
+ }
2598
+ (0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
2599
+ recordUsage(outcome.usage);
2600
+ if (outcome.status === "completed") {
2601
+ record.data = outcome.data;
2602
+ workflowCallFingerprint(record.data);
2603
+ record.status = "completed";
2604
+ delete record.snapshot;
2605
+ report(
2606
+ "completed",
2607
+ workflowCallContent(telemetry, "output", record.data)
2608
+ );
2609
+ } else {
2610
+ record.status = "interrupted";
2611
+ record.snapshot = outcome.snapshot;
2612
+ report("suspended", {
2613
+ leaf: outcome.request.meta?.workflowCall ?? null
2614
+ });
2615
+ const interruption = {
2616
+ request: outcome.request
2617
+ };
2618
+ record.interrupts.push(interruption);
2619
+ interruption.response = await awaitInterruptInternal({
2620
+ request: interruption.request,
2621
+ ...interruption.request.meta ? { meta: interruption.request.meta } : {}
2622
+ });
2623
+ }
2624
+ }
2625
+ if (cached)
2626
+ report("reused", workflowCallContent(telemetry, "output", record.data));
2627
+ const data = record.data ?? {};
2628
+ return {
2629
+ data: JSON.parse(JSON.stringify(data))
2630
+ };
2631
+ } finally {
2632
+ ctx.workflowCallActive = false;
2633
+ }
2634
+ }
2635
+ }
2636
+ });
18
2637
 
19
2638
  // src/index.ts
20
2639
  var index_exports = {};
21
2640
  __export(index_exports, {
2641
+ ExecutionRequestError: () => ExecutionRequestError,
22
2642
  createAgent: () => createAgent,
23
2643
  createChatRouteHandler: () => createChatRouteHandler,
24
2644
  createCheckpointRouteHandler: () => createCheckpointRouteHandler,
@@ -99,6 +2719,7 @@ function parseCheckpointRequestBody(value) {
99
2719
  async function handleChatRequestBody(args) {
100
2720
  const { agent, body } = args;
101
2721
  const stream = await agent.streamChat(body.messages, {
2722
+ ...args.abortSignal ? { abortSignal: args.abortSignal } : {},
102
2723
  sessionId: body.sessionId,
103
2724
  workflowId: body.workflowId,
104
2725
  context: body.context
@@ -118,7 +2739,11 @@ function createChatRouteHandler(args) {
118
2739
  return async function POST(request) {
119
2740
  try {
120
2741
  const body = parseChatRequestBody(await request.json());
121
- return await handleChatRequestBody({ agent, body });
2742
+ return await handleChatRequestBody({
2743
+ agent,
2744
+ body,
2745
+ abortSignal: request.signal
2746
+ });
122
2747
  } catch (error) {
123
2748
  return new Response(
124
2749
  JSON.stringify({
@@ -184,10 +2809,168 @@ async function* streamChatFromRoute(args) {
184
2809
 
185
2810
  // src/chat/create-agent.ts
186
2811
  var import_node_path = require("path");
2812
+ var import_core4 = require("@kortyx/core");
187
2813
  var import_providers = require("@kortyx/providers");
188
- var import_runtime4 = require("@kortyx/runtime");
2814
+ var import_runtime5 = require("@kortyx/runtime");
2815
+ var import_zod4 = require("zod");
2816
+
2817
+ // src/execution/execute.ts
2818
+ var import_core3 = require("@kortyx/core");
2819
+ var import_runtime3 = require("@kortyx/runtime");
2820
+ var import_zod3 = require("zod");
2821
+
2822
+ // src/interrupt/resume-handler.ts
2823
+ var import_core2 = require("@kortyx/core");
2824
+ var import_runtime2 = require("@kortyx/runtime");
2825
+
2826
+ // src/orchestrator.ts
2827
+ var import_node_stream = require("stream");
2828
+ var import_core = require("@kortyx/core");
2829
+ var import_runtime = require("@kortyx/runtime");
2830
+ var import_langgraph = require("@langchain/langgraph");
2831
+
2832
+ // src/execution/contracts.ts
189
2833
  var import_zod2 = require("zod");
190
2834
 
2835
+ // src/execution/types.ts
2836
+ var ExecutionRequestError = class extends Error {
2837
+ constructor(code, message) {
2838
+ super(message);
2839
+ this.code = code;
2840
+ }
2841
+ code;
2842
+ name = "ExecutionRequestError";
2843
+ };
2844
+
2845
+ // src/execution/contracts.ts
2846
+ async function resolveExecutionWorkflow(select, reference) {
2847
+ const id = typeof reference === "string" ? reference : reference.id;
2848
+ let workflow;
2849
+ try {
2850
+ workflow = await select(id);
2851
+ } catch {
2852
+ throw new ExecutionRequestError(
2853
+ "UNKNOWN_WORKFLOW",
2854
+ `Workflow '${id}' is not registered.`
2855
+ );
2856
+ }
2857
+ if (workflow.id !== id)
2858
+ throw new ExecutionRequestError(
2859
+ "UNKNOWN_WORKFLOW",
2860
+ `Workflow '${id}' is not registered.`
2861
+ );
2862
+ if (!workflow.inputSchema || !workflow.outputSchema)
2863
+ throw new ExecutionRequestError(
2864
+ "MISSING_CONTRACT",
2865
+ `Workflow '${id}' requires inputSchema and outputSchema.`
2866
+ );
2867
+ if (typeof reference !== "string" && (reference.version !== workflow.version || reference.inputSchema !== workflow.inputSchema || reference.outputSchema !== workflow.outputSchema))
2868
+ throw new ExecutionRequestError(
2869
+ "CONTRACT_MISMATCH",
2870
+ "The typed workflow contract does not match the registered definition."
2871
+ );
2872
+ return workflow;
2873
+ }
2874
+ function parseExecutionInput(workflow, input) {
2875
+ try {
2876
+ import_zod2.z.json().parse(input);
2877
+ const parsed = workflow.inputSchema ? workflow.inputSchema.parse(input) : input;
2878
+ import_zod2.z.json().parse(parsed);
2879
+ return parsed;
2880
+ } catch (error) {
2881
+ throw new ExecutionRequestError(
2882
+ "INVALID_INPUT",
2883
+ error instanceof Error ? error.message : String(error)
2884
+ );
2885
+ }
2886
+ }
2887
+ async function validateExecutionOutput(state, select) {
2888
+ if (!state.config?.executionContract) return state;
2889
+ const terminal = await select(String(state.currentWorkflow));
2890
+ const contract = state.config.executionContract;
2891
+ const root = await select(contract.id);
2892
+ if (root.id !== contract.id || root.version !== contract.version)
2893
+ throw new Error("The root workflow changed since execution started.");
2894
+ let data = state.data ?? {};
2895
+ try {
2896
+ for (const schema of /* @__PURE__ */ new Set([terminal.outputSchema, root.outputSchema])) {
2897
+ if (schema) data = schema.parse(data);
2898
+ }
2899
+ if (terminal.outputSchema || root.outputSchema) import_zod2.z.json().parse(data);
2900
+ } catch (error) {
2901
+ const failure = new Error(
2902
+ `Workflow output validation failed: ${error instanceof Error ? error.message : String(error)}`
2903
+ );
2904
+ Object.assign(failure, { code: "INVALID_OUTPUT" });
2905
+ throw failure;
2906
+ }
2907
+ return { ...state, data };
2908
+ }
2909
+
2910
+ // src/stream/transform-graph-stream-for-ui.ts
2911
+ var isGraphStateOutput = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && "currentWorkflow" in value;
2912
+ async function* transformGraphStreamForUI(stream, options = {}) {
2913
+ const { debug = false, emitStatus = debug } = options;
2914
+ const startedNodes = /* @__PURE__ */ new Set();
2915
+ const endedNodes = /* @__PURE__ */ new Set();
2916
+ let latestGraphState = null;
2917
+ for await (const event of stream) {
2918
+ const { event: type, name, data } = event ?? {};
2919
+ if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
2920
+ switch (type) {
2921
+ case "on_chain_start":
2922
+ if (name && !name.startsWith("ChannelWrite")) {
2923
+ if (name === "__start__" || name === "__end__") break;
2924
+ if (startedNodes.has(name)) break;
2925
+ startedNodes.add(name);
2926
+ if (debug) console.log(`[debug:start] node=${name}`);
2927
+ if (emitStatus) {
2928
+ yield { type: "status", message: `Processing node: ${name}` };
2929
+ }
2930
+ }
2931
+ break;
2932
+ case "on_chain_end": {
2933
+ const nodeName = name;
2934
+ const output = data?.output;
2935
+ if (debug)
2936
+ console.log(
2937
+ `[debug:on_chain_end:${nodeName}] output=`,
2938
+ JSON.stringify(output, null, 2)
2939
+ );
2940
+ if (!output || nodeName?.startsWith("ChannelWrite")) break;
2941
+ if (isGraphStateOutput(output)) latestGraphState = output;
2942
+ if (nodeName !== "__start__" && nodeName !== "__end__") {
2943
+ if (!endedNodes.has(nodeName)) {
2944
+ if (emitStatus) {
2945
+ yield {
2946
+ type: "status",
2947
+ message: `\u2705 Completed node: ${nodeName}`
2948
+ };
2949
+ }
2950
+ endedNodes.add(nodeName);
2951
+ }
2952
+ }
2953
+ break;
2954
+ }
2955
+ case "on_graph_end": {
2956
+ if (debug)
2957
+ console.log(`[debug:on_graph_end]`, JSON.stringify(data, null, 2));
2958
+ const output = data?.output;
2959
+ yield {
2960
+ type: "done",
2961
+ data: isGraphStateOutput(output) ? output : latestGraphState
2962
+ };
2963
+ break;
2964
+ }
2965
+ default:
2966
+ if (debug) {
2967
+ console.warn(`[debug:unknown_event]`, type);
2968
+ }
2969
+ break;
2970
+ }
2971
+ }
2972
+ }
2973
+
191
2974
  // src/telemetry/events.ts
192
2975
  var import_node_crypto = require("crypto");
193
2976
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -237,7 +3020,10 @@ var emitTelemetryEvent = (args) => {
237
3020
  }
238
3021
  } : {},
239
3022
  type: args.type,
240
- payload: args.payload
3023
+ payload: {
3024
+ ...args.payload,
3025
+ ...args.type === "run.cancelled" ? { branchId: args.config.executionBranchId ?? runId } : {}
3026
+ }
241
3027
  };
242
3028
  try {
243
3029
  void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
@@ -392,136 +3178,68 @@ var projectWorkflowTopology = (args) => {
392
3178
  environment: args.environment,
393
3179
  service: args.service,
394
3180
  workflow: {
395
- id: args.workflow.id,
396
- declaredVersion: args.workflow.version,
397
- ...args.workflow.description ? { description: args.workflow.description } : {},
398
- ...tags ? { tags } : {},
399
- topologyHash,
400
- nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
401
- edges,
402
- ...transitions.length > 0 ? { transitions } : {}
403
- }
404
- };
405
- };
406
- var prepareWorkflowTelemetry = (args) => {
407
- const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
408
- if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
409
- return args.config;
410
- }
411
- let snapshot;
412
- try {
413
- snapshot = projectWorkflowTopology({
414
- workflow: args.workflow,
415
- environment: telemetry.environment,
416
- service: telemetry.service,
417
- knownWorkflowIds: args.knownWorkflowIds
418
- });
419
- } catch {
420
- return args.config;
421
- }
422
- let workflowRevisionId;
423
- try {
424
- workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
425
- environment: snapshot.environment,
426
- workflowId: snapshot.workflow.id,
427
- topologyHash: snapshot.workflow.topologyHash
428
- });
429
- void Promise.resolve(
430
- telemetry.reporter.ensureWorkflowTopology(snapshot)
431
- ).catch(() => {
432
- });
433
- } catch {
434
- }
435
- return {
436
- ...args.config,
437
- telemetry: {
438
- ...telemetry,
439
- correlation: {
440
- ...telemetry.correlation ?? {},
441
- runId: args.runId,
442
- ...args.sessionId ? { sessionId: args.sessionId } : {},
443
- workflowId: snapshot.workflow.id,
444
- topologyHash: snapshot.workflow.topologyHash,
445
- ...workflowRevisionId ? { workflowRevisionId } : {}
446
- }
447
- }
448
- };
449
- };
450
-
451
- // src/chat/process-chat.ts
452
- var import_runtime3 = require("@kortyx/runtime");
453
-
454
- // src/interrupt/resume-handler.ts
455
- var import_runtime2 = require("@kortyx/runtime");
456
-
457
- // src/orchestrator.ts
458
- var import_node_stream = require("stream");
459
- var import_runtime = require("@kortyx/runtime");
460
- var import_langgraph = require("@langchain/langgraph");
461
-
462
- // src/stream/transform-graph-stream-for-ui.ts
463
- var isGraphStateOutput = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && "currentWorkflow" in value;
464
- async function* transformGraphStreamForUI(stream, options = {}) {
465
- const { debug = false, emitStatus = debug } = options;
466
- const startedNodes = /* @__PURE__ */ new Set();
467
- const endedNodes = /* @__PURE__ */ new Set();
468
- let latestGraphState = null;
469
- for await (const event of stream) {
470
- const { event: type, name, data } = event ?? {};
471
- if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
472
- switch (type) {
473
- case "on_chain_start":
474
- if (name && !name.startsWith("ChannelWrite")) {
475
- if (name === "__start__" || name === "__end__") break;
476
- if (startedNodes.has(name)) break;
477
- startedNodes.add(name);
478
- if (debug) console.log(`[debug:start] node=${name}`);
479
- if (emitStatus) {
480
- yield { type: "status", message: `Processing node: ${name}` };
481
- }
482
- }
483
- break;
484
- case "on_chain_end": {
485
- const nodeName = name;
486
- const output = data?.output;
487
- if (debug)
488
- console.log(
489
- `[debug:on_chain_end:${nodeName}] output=`,
490
- JSON.stringify(output, null, 2)
491
- );
492
- if (!output || nodeName?.startsWith("ChannelWrite")) break;
493
- if (isGraphStateOutput(output)) latestGraphState = output;
494
- if (nodeName !== "__start__" && nodeName !== "__end__") {
495
- if (!endedNodes.has(nodeName)) {
496
- if (emitStatus) {
497
- yield {
498
- type: "status",
499
- message: `\u2705 Completed node: ${nodeName}`
500
- };
501
- }
502
- endedNodes.add(nodeName);
503
- }
504
- }
505
- break;
506
- }
507
- case "on_graph_end": {
508
- if (debug)
509
- console.log(`[debug:on_graph_end]`, JSON.stringify(data, null, 2));
510
- const output = data?.output;
511
- yield {
512
- type: "done",
513
- data: isGraphStateOutput(output) ? output : latestGraphState
514
- };
515
- break;
516
- }
517
- default:
518
- if (debug) {
519
- console.warn(`[debug:unknown_event]`, type);
520
- }
521
- break;
3181
+ id: args.workflow.id,
3182
+ declaredVersion: args.workflow.version,
3183
+ ...args.workflow.description ? { description: args.workflow.description } : {},
3184
+ ...tags ? { tags } : {},
3185
+ topologyHash,
3186
+ nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
3187
+ edges,
3188
+ ...transitions.length > 0 ? { transitions } : {}
522
3189
  }
3190
+ };
3191
+ };
3192
+ var prepareWorkflowTelemetry = (args) => {
3193
+ const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
3194
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
3195
+ return args.config;
523
3196
  }
524
- }
3197
+ let snapshot;
3198
+ try {
3199
+ snapshot = projectWorkflowTopology({
3200
+ workflow: args.workflow,
3201
+ environment: telemetry.environment,
3202
+ service: telemetry.service,
3203
+ knownWorkflowIds: args.knownWorkflowIds
3204
+ });
3205
+ } catch {
3206
+ return args.config;
3207
+ }
3208
+ let workflowRevisionId;
3209
+ try {
3210
+ workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
3211
+ environment: snapshot.environment,
3212
+ workflowId: snapshot.workflow.id,
3213
+ topologyHash: snapshot.workflow.topologyHash
3214
+ });
3215
+ void Promise.resolve(
3216
+ telemetry.reporter.ensureWorkflowTopology(snapshot)
3217
+ ).catch(() => {
3218
+ });
3219
+ } catch {
3220
+ }
3221
+ return {
3222
+ ...args.config,
3223
+ prepareChildTelemetry: (workflow, config) => prepareWorkflowTelemetry({
3224
+ config,
3225
+ workflow,
3226
+ runId: args.runId,
3227
+ sessionId: args.sessionId,
3228
+ knownWorkflowIds: args.knownWorkflowIds
3229
+ }),
3230
+ telemetry: {
3231
+ ...telemetry,
3232
+ correlation: {
3233
+ ...telemetry.correlation ?? {},
3234
+ runId: args.runId,
3235
+ ...args.sessionId ? { sessionId: args.sessionId } : {},
3236
+ workflowId: snapshot.workflow.id,
3237
+ topologyHash: snapshot.workflow.topologyHash,
3238
+ ...workflowRevisionId ? { workflowRevisionId } : {}
3239
+ }
3240
+ }
3241
+ };
3242
+ };
525
3243
 
526
3244
  // src/orchestrator.ts
527
3245
  var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -544,15 +3262,30 @@ async function orchestrateGraphStream({
544
3262
  graph,
545
3263
  state,
546
3264
  config: initialConfig,
3265
+ abortSignal: requestSignal,
3266
+ emitOutput = true,
3267
+ onOutcome,
547
3268
  selectWorkflow,
548
3269
  knownWorkflowIds,
549
3270
  frameworkAdapter
550
3271
  }) {
551
3272
  const out = new import_node_stream.PassThrough({ objectMode: true });
3273
+ const write = (chunk) => {
3274
+ if (emitOutput && !out.destroyed) out.write(chunk);
3275
+ };
3276
+ let outcomeState = state;
3277
+ let outcomeError;
3278
+ let sessionCheckpointId;
552
3279
  let config = initialConfig;
3280
+ const budget = config.executionBudget;
553
3281
  let currentGraph = graph;
554
3282
  let currentState = state;
555
3283
  let finished = false;
3284
+ const controller = new AbortController();
3285
+ const abortSignal = (0, import_core.combineAbortSignals)(requestSignal, controller.signal);
3286
+ out.on("close", () => {
3287
+ if (!finished) controller.abort();
3288
+ });
556
3289
  let structuredSeq = 0;
557
3290
  const debugEnabled = Boolean(config?.features?.tracing);
558
3291
  const traceAdapter = getTraceAdapter(config);
@@ -593,7 +3326,7 @@ async function orchestrateGraphStream({
593
3326
  },
594
3327
  tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags : void 0,
595
3328
  captureContent: telemetryConfig.captureContent,
596
- ...typeof state.input === "string" ? { input: state.input } : {}
3329
+ input: typeof state.input === "string" ? state.input : JSON.stringify(state.input)
597
3330
  }
598
3331
  };
599
3332
  const namespacesUsed = /* @__PURE__ */ new Set();
@@ -604,7 +3337,7 @@ async function orchestrateGraphStream({
604
3337
  try {
605
3338
  const sid = config?.session?.id;
606
3339
  if (sid && typeof sid === "string") {
607
- out.write({ type: "session", sessionId: sid });
3340
+ write({ type: "session", sessionId: sid });
608
3341
  }
609
3342
  } catch {
610
3343
  }
@@ -628,12 +3361,33 @@ async function orchestrateGraphStream({
628
3361
  }
629
3362
  lastOutputSegmentId = textStreamId;
630
3363
  accumulatedOutput += delta;
3364
+ if (!emitOutput) accumulatedOutput = accumulatedOutput.slice(0, 65536);
631
3365
  };
632
3366
  const nextStructuredStreamId = () => `${runId}:structured:${structuredSeq++}`;
633
3367
  const runTraceEndArgs = () => accumulatedOutput.length > 0 ? { telemetry: { output: accumulatedOutput } } : void 0;
634
3368
  let pendingRecordToken = null;
635
3369
  let wroteHumanInput = false;
636
3370
  const pendingStore = frameworkAdapter?.pendingRequests;
3371
+ const sealPendingSnapshots = async () => {
3372
+ if (budget) outcomeState.config.executionBudget = budget;
3373
+ if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
3374
+ for (const [token, request] of activePendingRequests) {
3375
+ if (budget) request.state.config.executionBudget = budget;
3376
+ const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
3377
+ frameworkAdapter.checkpointer,
3378
+ runId,
3379
+ request.graphCheckpointId
3380
+ );
3381
+ if (graphSnapshot) {
3382
+ const sealed = { ...request, graphSnapshot };
3383
+ activePendingRequests.set(token, sealed);
3384
+ await pendingStore?.update(token, {
3385
+ graphSnapshot,
3386
+ state: request.state
3387
+ });
3388
+ }
3389
+ }
3390
+ };
637
3391
  const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1e3;
638
3392
  const persistAndEmitInterrupt = async (payload) => {
639
3393
  if (wroteHumanInput) return;
@@ -681,6 +3435,7 @@ async function orchestrateGraphStream({
681
3435
  activePendingRequests.set(record.token, record);
682
3436
  if (pendingStore) {
683
3437
  const savePromise = pendingStore.save(record).catch((error) => {
3438
+ outcomeError = error;
684
3439
  console.error("[orchestrator] failed to save pending request", error);
685
3440
  });
686
3441
  pendingRequestWrites.push(savePromise);
@@ -690,7 +3445,7 @@ async function orchestrateGraphStream({
690
3445
  ([key]) => !key.startsWith("__kortyx")
691
3446
  )
692
3447
  ) : void 0;
693
- out.write({
3448
+ write({
694
3449
  type: "interrupt",
695
3450
  requestId: record.requestId,
696
3451
  resumeToken: record.token,
@@ -719,6 +3474,9 @@ async function orchestrateGraphStream({
719
3474
  type: "interrupt.created",
720
3475
  correlation: { nodeId: record.node },
721
3476
  payload: {
3477
+ workflowCall: record.schema.meta?.workflowCall ?? null,
3478
+ workflowCallPath: record.schema.meta?.workflowCallPath ?? null,
3479
+ branchId: config.executionBranchId ?? runId,
722
3480
  interruptId: record.requestId,
723
3481
  requestId: record.requestId,
724
3482
  kind: record.schema.kind,
@@ -749,8 +3507,9 @@ async function orchestrateGraphStream({
749
3507
  const msg = String(
750
3508
  payload?.message ?? "Unexpected error"
751
3509
  );
752
- out.write({ type: "error", message: msg });
753
- out.write({ type: "done" });
3510
+ outcomeError = new Error(msg);
3511
+ write({ type: "error", message: msg });
3512
+ write({ type: "done" });
754
3513
  finished = true;
755
3514
  out.end();
756
3515
  return;
@@ -763,14 +3522,14 @@ async function orchestrateGraphStream({
763
3522
  if (msg && msg === lastStatusMsg && now - lastStatusAt < 250) return;
764
3523
  lastStatusMsg = msg;
765
3524
  lastStatusAt = now;
766
- out.write({ type: "status", message: msg });
3525
+ write({ type: "status", message: msg });
767
3526
  return;
768
3527
  }
769
3528
  if (event === "text-start") {
770
3529
  const node = payload?.node;
771
3530
  if (!node) return;
772
3531
  touchedNodes.add(node);
773
- out.write({
3532
+ write({
774
3533
  type: "text-start",
775
3534
  node,
776
3535
  ...typeof payload.id === "string" ? { id: payload.id } : {},
@@ -785,7 +3544,7 @@ async function orchestrateGraphStream({
785
3544
  if (!node || !delta) return;
786
3545
  touchedNodes.add(node);
787
3546
  appendAccumulatedOutput(payload, node, delta);
788
- out.write({
3547
+ write({
789
3548
  type: "text-delta",
790
3549
  delta,
791
3550
  node,
@@ -799,7 +3558,7 @@ async function orchestrateGraphStream({
799
3558
  const node = payload?.node;
800
3559
  if (!node) return;
801
3560
  touchedNodes.add(node);
802
- out.write({
3561
+ write({
803
3562
  type: "text-end",
804
3563
  node,
805
3564
  ...typeof payload.id === "string" ? { id: payload.id } : {},
@@ -813,7 +3572,7 @@ async function orchestrateGraphStream({
813
3572
  if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
814
3573
  return;
815
3574
  }
816
- out.write({
3575
+ write({
817
3576
  type: "tool-call-start",
818
3577
  tool: payloadObj.tool,
819
3578
  toolCallId: payloadObj.toolCallId,
@@ -830,7 +3589,7 @@ async function orchestrateGraphStream({
830
3589
  if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
831
3590
  return;
832
3591
  }
833
- out.write({
3592
+ write({
834
3593
  type: "tool-call-result",
835
3594
  tool: payloadObj.tool,
836
3595
  toolCallId: payloadObj.toolCallId,
@@ -849,7 +3608,7 @@ async function orchestrateGraphStream({
849
3608
  if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
850
3609
  return;
851
3610
  }
852
- out.write({
3611
+ write({
853
3612
  type: "tool-call-error",
854
3613
  tool: payloadObj.tool,
855
3614
  toolCallId: payloadObj.toolCallId,
@@ -865,7 +3624,7 @@ async function orchestrateGraphStream({
865
3624
  const node = payload?.node;
866
3625
  const text = String(payload?.content ?? "");
867
3626
  if (node) touchedNodes.add(node);
868
- out.write({ type: "message", node, content: text });
3627
+ write({ type: "message", node, content: text });
869
3628
  return;
870
3629
  }
871
3630
  if (event === "structured_data") {
@@ -874,7 +3633,7 @@ async function orchestrateGraphStream({
874
3633
  const streamId = typeof payloadObj.streamId === "string" && payloadObj.streamId ? payloadObj.streamId : nextStructuredStreamId();
875
3634
  structuredStreamIds.add(streamId);
876
3635
  if (payloadObj.node) touchedNodes.add(payloadObj.node);
877
- out.write({
3636
+ write({
878
3637
  type: "structured-data",
879
3638
  node: payloadObj.node,
880
3639
  streamId,
@@ -900,7 +3659,7 @@ async function orchestrateGraphStream({
900
3659
  return;
901
3660
  }
902
3661
  if (event === "transition") {
903
- out.write({
3662
+ write({
904
3663
  type: "transition",
905
3664
  transitionTo: payload?.transitionTo,
906
3665
  payload: payload?.payload ?? {}
@@ -928,12 +3687,17 @@ async function orchestrateGraphStream({
928
3687
  };
929
3688
  const runLoop = async (runTraceSpan) => {
930
3689
  while (true) {
3690
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
931
3691
  let workflowFinalState = null;
932
3692
  let workflowFinalGraphCheckpointId;
933
3693
  let sawWorkflowDone = false;
934
3694
  let workflowDoneData;
3695
+ currentGraph.execution ??= {};
3696
+ currentGraph.execution.abortSignal = abortSignal;
3697
+ currentGraph.execution.budget = budget;
935
3698
  currentGraph.config = currentGraph.config || {};
936
3699
  currentGraph.config.emit = forwardEmit;
3700
+ currentGraph.config.executionRunId = runId;
937
3701
  const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
938
3702
  const checkpointNs = "";
939
3703
  namespacesUsed.add(checkpointNs);
@@ -963,7 +3727,7 @@ async function orchestrateGraphStream({
963
3727
  };
964
3728
  };
965
3729
  if (debugEnabled) {
966
- out.write({
3730
+ write({
967
3731
  type: "status",
968
3732
  message: `\u{1F9F5} thread_id=${threadId} run_id=${runId} workflow=${currentState.currentWorkflow}`
969
3733
  });
@@ -982,16 +3746,32 @@ async function orchestrateGraphStream({
982
3746
  }
983
3747
  });
984
3748
  if (debugEnabled) {
985
- out.write({
3749
+ write({
986
3750
  type: "status",
987
3751
  message: `\u25B6\uFE0F streamEvents invoke: resume=${Boolean(currentGraph.config?.resume)} thread_id=${threadId} run_id=${runId} ns=${String(currentState.currentWorkflow || "default")}`
988
3752
  });
989
3753
  }
990
- const uiStream = transformGraphStreamForUI(runtimeStream, {
3754
+ const observedStream = (async function* () {
3755
+ for await (const event of runtimeStream) {
3756
+ const raw = event;
3757
+ if ((raw.event === "on_chain_end" || raw.event === "on_graph_end") && !raw.name?.startsWith("ChannelWrite") && isGraphState(raw.data?.output)) {
3758
+ outcomeState = raw.data.output;
3759
+ }
3760
+ yield event;
3761
+ }
3762
+ })();
3763
+ const uiStream = transformGraphStreamForUI(observedStream, {
991
3764
  debug: debugEnabled,
992
3765
  emitStatus: debugEnabled
993
3766
  });
994
- for await (const chunk of uiStream) {
3767
+ if (!emitOutput) {
3768
+ for await (const _event of observedStream) {
3769
+ if (finished) break;
3770
+ }
3771
+ sawWorkflowDone = true;
3772
+ workflowDoneData = outcomeState;
3773
+ }
3774
+ for await (const chunk of emitOutput ? uiStream : []) {
995
3775
  if (finished) break;
996
3776
  if ("node" in chunk && typeof chunk.node === "string") {
997
3777
  touchedNodes.add(chunk.node);
@@ -1001,14 +3781,18 @@ async function orchestrateGraphStream({
1001
3781
  workflowDoneData = chunk.data;
1002
3782
  continue;
1003
3783
  }
1004
- out.write(chunk);
3784
+ write(chunk);
3785
+ }
3786
+ if (finished) {
3787
+ runTraceSpan.fail?.(outcomeError);
3788
+ return;
1005
3789
  }
1006
- if (finished) return;
1007
3790
  if (sawWorkflowDone) {
1008
3791
  const graphSnapshot = await readLatestGraphSnapshot();
1009
3792
  workflowFinalGraphCheckpointId = graphSnapshot?.checkpointId;
1010
3793
  workflowFinalState = isGraphState(workflowDoneData) ? workflowDoneData : graphSnapshot?.state ?? currentState;
1011
3794
  }
3795
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
1012
3796
  const transitionTo = pending.to;
1013
3797
  const transitionPayload = pending.payload;
1014
3798
  const sourceNodeId = pending.sourceNodeId;
@@ -1045,6 +3829,10 @@ async function orchestrateGraphStream({
1045
3829
  } : {}
1046
3830
  }
1047
3831
  });
3832
+ delete config.resume;
3833
+ delete config.resumeValue;
3834
+ delete config.resumeUpdate;
3835
+ delete config.resumeCheckpointId;
1048
3836
  const nextGraph = await (0, import_runtime.createExecutionGraph)(nextWorkflow, {
1049
3837
  ...config,
1050
3838
  emit: forwardEmit
@@ -1055,12 +3843,13 @@ async function orchestrateGraphStream({
1055
3843
  ...transitionPayload
1056
3844
  };
1057
3845
  const rawInputFromPayload = transitionPayload?.rawInput;
1058
- const newInput = typeof rawInputFromPayload === "string" ? rawInputFromPayload : currentState.input;
3846
+ const rawNextInput = typeof rawInputFromPayload === "string" ? rawInputFromPayload : currentState.input;
1059
3847
  currentState = {
1060
3848
  ...currentState,
3849
+ runtime: workflowFinalState?.runtime ?? currentState.runtime,
1061
3850
  currentWorkflow: transitionTo,
1062
3851
  config,
1063
- input: newInput,
3852
+ input: nextWorkflow.inputSchema ? parseExecutionInput(nextWorkflow, rawNextInput) : rawNextInput,
1064
3853
  data: mergedData,
1065
3854
  ui: {}
1066
3855
  // reset UI layer on new graph
@@ -1068,14 +3857,18 @@ async function orchestrateGraphStream({
1068
3857
  currentGraph = nextGraph;
1069
3858
  continue;
1070
3859
  } catch (err) {
3860
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
3861
+ if ((0, import_core.isExecutionCancelled)(err)) throw err;
3862
+ outcomeError = err;
3863
+ runTraceSpan.fail?.(err);
1071
3864
  runTraceSpan.addEvent?.("kortyx.transition.error", {
1072
3865
  transitionTo
1073
3866
  });
1074
- out.write({
3867
+ write({
1075
3868
  type: "error",
1076
3869
  message: `Transition failed to '${transitionTo}': ${err instanceof Error ? err.message : String(err)}`
1077
3870
  });
1078
- out.write({ type: "done" });
3871
+ write({ type: "done" });
1079
3872
  out.end();
1080
3873
  return;
1081
3874
  }
@@ -1105,6 +3898,10 @@ async function orchestrateGraphStream({
1105
3898
  }
1106
3899
  const shouldKeepFrameworkState = Boolean(pendingRecordToken) || Boolean(workflowFinalState?.awaitingHumanInput);
1107
3900
  if (!shouldKeepFrameworkState) {
3901
+ workflowFinalState = await validateExecutionOutput(
3902
+ workflowFinalState,
3903
+ selectWorkflow
3904
+ );
1108
3905
  try {
1109
3906
  if (frameworkAdapter?.cleanupRun) {
1110
3907
  await frameworkAdapter.cleanupRun(
@@ -1121,6 +3918,12 @@ async function orchestrateGraphStream({
1121
3918
  console.error("[orchestrator] framework cleanup failed", e);
1122
3919
  }
1123
3920
  }
3921
+ outcomeState = workflowFinalState;
3922
+ if (!emitOutput && !shouldKeepFrameworkState)
3923
+ accumulatedOutput = JSON.stringify(workflowFinalState.data).slice(
3924
+ 0,
3925
+ 65536
3926
+ );
1124
3927
  finished = true;
1125
3928
  runTraceSpan.setAttributes?.({
1126
3929
  "kortyx.run.awaiting_human_input": shouldKeepFrameworkState,
@@ -1128,6 +3931,7 @@ async function orchestrateGraphStream({
1128
3931
  });
1129
3932
  runTraceSpan.end?.(runTraceEndArgs());
1130
3933
  const resolvedSessionId2 = config?.session?.id || sessionId || "";
3934
+ await sealPendingSnapshots();
1131
3935
  if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
1132
3936
  try {
1133
3937
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
@@ -1142,7 +3946,8 @@ async function orchestrateGraphStream({
1142
3946
  pendingRequests: Array.from(activePendingRequests.values())
1143
3947
  }
1144
3948
  );
1145
- out.write({
3949
+ sessionCheckpointId = checkpoint.id;
3950
+ write({
1146
3951
  type: "checkpoint",
1147
3952
  id: checkpoint.id,
1148
3953
  sessionId: checkpoint.sessionId,
@@ -1160,15 +3965,23 @@ async function orchestrateGraphStream({
1160
3965
  }
1161
3966
  });
1162
3967
  } catch (error) {
3968
+ outcomeError = error;
1163
3969
  console.error("[orchestrator] session checkpoint failed", error);
1164
3970
  }
1165
3971
  }
1166
- out.write({ type: "done", data: workflowFinalState });
3972
+ write({ type: "done", data: workflowFinalState });
1167
3973
  out.end();
1168
3974
  return;
1169
3975
  }
1170
3976
  const naturalGraphSnapshot = await readLatestGraphSnapshot();
1171
- const naturalFinalState = naturalGraphSnapshot?.state ?? currentState;
3977
+ let naturalFinalState = naturalGraphSnapshot?.state ?? currentState;
3978
+ if (!pendingRecordToken && !naturalFinalState.awaitingHumanInput) {
3979
+ naturalFinalState = await validateExecutionOutput(
3980
+ naturalFinalState,
3981
+ selectWorkflow
3982
+ );
3983
+ }
3984
+ outcomeState = naturalFinalState;
1172
3985
  if (pendingRequestWrites.length > 0) {
1173
3986
  await Promise.all(pendingRequestWrites);
1174
3987
  }
@@ -1189,6 +4002,7 @@ async function orchestrateGraphStream({
1189
4002
  }
1190
4003
  }
1191
4004
  const resolvedSessionId = config?.session?.id || sessionId || "";
4005
+ await sealPendingSnapshots();
1192
4006
  if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
1193
4007
  try {
1194
4008
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
@@ -1201,7 +4015,8 @@ async function orchestrateGraphStream({
1201
4015
  structuredStreamIds: Array.from(structuredStreamIds),
1202
4016
  pendingRequests: Array.from(activePendingRequests.values())
1203
4017
  });
1204
- out.write({
4018
+ sessionCheckpointId = checkpoint.id;
4019
+ write({
1205
4020
  type: "checkpoint",
1206
4021
  id: checkpoint.id,
1207
4022
  sessionId: checkpoint.sessionId,
@@ -1219,14 +4034,16 @@ async function orchestrateGraphStream({
1219
4034
  }
1220
4035
  });
1221
4036
  } catch (error) {
4037
+ outcomeError = error;
1222
4038
  console.error("[orchestrator] session checkpoint failed", error);
1223
4039
  }
1224
4040
  }
1225
4041
  runTraceSpan.setAttributes?.({
1226
4042
  "kortyx.run.final_workflow": String(naturalFinalState.currentWorkflow)
1227
4043
  });
4044
+ finished = true;
1228
4045
  runTraceSpan.end?.(runTraceEndArgs());
1229
- out.write({ type: "done", data: naturalFinalState });
4046
+ write({ type: "done", data: naturalFinalState });
1230
4047
  out.end();
1231
4048
  return;
1232
4049
  }
@@ -1234,7 +4051,7 @@ async function orchestrateGraphStream({
1234
4051
  const emitTraceChunk = () => {
1235
4052
  const traceContext = traceAdapter?.getActiveContext?.();
1236
4053
  if (!traceContext) return;
1237
- out.write({
4054
+ write({
1238
4055
  type: "trace",
1239
4056
  traceId: traceContext.traceId,
1240
4057
  spanId: traceContext.spanId,
@@ -1250,16 +4067,165 @@ async function orchestrateGraphStream({
1250
4067
  emitTraceChunk();
1251
4068
  return runLoop(runTraceSpan);
1252
4069
  }) : runLoop(fallbackRunSpan ?? {});
1253
- runPromise.catch((err) => {
4070
+ const finishCancelled = async () => {
4071
+ emitTelemetryEvent({
4072
+ config,
4073
+ type: "run.cancelled",
4074
+ correlation: { runId, workflowId: outcomeState.currentWorkflow },
4075
+ payload: { reason: "execution_aborted" },
4076
+ flush: true
4077
+ });
4078
+ await Promise.allSettled(pendingRequestWrites);
4079
+ await Promise.allSettled(
4080
+ [...activePendingRequests.keys()].map(
4081
+ (token) => pendingStore?.delete(token)
4082
+ )
4083
+ );
4084
+ activePendingRequests.clear();
4085
+ try {
4086
+ await frameworkAdapter?.cleanupRun?.(runId, [...namespacesUsed]);
4087
+ } catch (cleanupError) {
4088
+ console.error("[cancel:cleanupRun]", cleanupError);
4089
+ }
4090
+ outcomeError = (0, import_core.createExecutionCancelledError)();
4091
+ fallbackRunSpan?.setAttributes?.({ "kortyx.run.cancelled": true });
4092
+ fallbackRunSpan?.end?.();
4093
+ write({ type: "cancelled", runId, reason: "Execution cancelled." });
4094
+ write({ type: "done" });
4095
+ finished = true;
4096
+ out.end();
4097
+ };
4098
+ runPromise.catch(async (err) => {
4099
+ if (abortSignal?.aborted || (0, import_core.isExecutionCancelled)(err)) {
4100
+ await finishCancelled();
4101
+ return;
4102
+ }
4103
+ if ((0, import_core.isExecutionLimitReached)(err)) {
4104
+ try {
4105
+ const snapshot = frameworkAdapter && await (0, import_runtime.captureGraphSnapshot)(frameworkAdapter.checkpointer, runId);
4106
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
4107
+ if (!snapshot)
4108
+ throw new Error(
4109
+ "Execution limit reached without a resumable checkpoint."
4110
+ );
4111
+ const savedState = snapshot.checkpoint.channel_values;
4112
+ const pausedState = {
4113
+ ...savedState,
4114
+ config: {
4115
+ ...savedState.config,
4116
+ ...config,
4117
+ executionBudget: budget
4118
+ },
4119
+ awaitingHumanInput: true
4120
+ };
4121
+ const patch = err.__kortyxHookStatePatch;
4122
+ currentState = pausedState;
4123
+ outcomeState = pausedState;
4124
+ const limitReached = err.limitReached;
4125
+ write({ type: "limit-reached", runId, ...limitReached });
4126
+ emitTelemetryEvent({
4127
+ config,
4128
+ type: "run.limit_reached",
4129
+ payload: limitReached
4130
+ });
4131
+ await persistAndEmitInterrupt({
4132
+ node: String(
4133
+ err.nodeId ?? savedState.lastNode
4134
+ ),
4135
+ workflow: String(savedState.currentWorkflow),
4136
+ input: {
4137
+ kind: "choice",
4138
+ question: "Limit reached \u2014 Continue?",
4139
+ options: [{ id: "continue", label: "Continue" }],
4140
+ meta: {
4141
+ __kortyxExecutionLimit: true,
4142
+ executionLimit: limitReached,
4143
+ ...patch ? { __kortyxResumeStatePatch: patch } : {}
4144
+ }
4145
+ }
4146
+ });
4147
+ await Promise.all(pendingRequestWrites);
4148
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
4149
+ if (outcomeError) throw outcomeError;
4150
+ const request = activePendingRequests.get(pendingRecordToken);
4151
+ const sealed = {
4152
+ ...request,
4153
+ state: pausedState,
4154
+ graphCheckpointId: snapshot.checkpoint.id,
4155
+ graphSnapshot: snapshot
4156
+ };
4157
+ activePendingRequests.set(request.token, sealed);
4158
+ await pendingStore?.update(request.token, sealed);
4159
+ const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
4160
+ sessionId,
4161
+ runId,
4162
+ graphCheckpointId: snapshot.checkpoint.id,
4163
+ workflow: String(pausedState.currentWorkflow),
4164
+ state: pausedState,
4165
+ nodes: Array.from(touchedNodes),
4166
+ structuredStreamIds: Array.from(structuredStreamIds),
4167
+ pendingRequests: [sealed],
4168
+ label: "Execution limit reached"
4169
+ });
4170
+ (0, import_core.throwIfExecutionAborted)(abortSignal);
4171
+ sessionCheckpointId = checkpoint.id;
4172
+ write({
4173
+ type: "checkpoint",
4174
+ id: checkpoint.id,
4175
+ sessionId: checkpoint.sessionId,
4176
+ turnIndex: checkpoint.turnIndex,
4177
+ label: checkpoint.label
4178
+ });
4179
+ emitTelemetryEvent({
4180
+ config,
4181
+ type: "session.checkpointed",
4182
+ payload: {
4183
+ checkpointId: checkpoint.id,
4184
+ turnIndex: checkpoint.turnIndex,
4185
+ nodes: checkpoint.nodes
4186
+ }
4187
+ });
4188
+ fallbackRunSpan?.setAttributes?.({
4189
+ "kortyx.run.awaiting_human_input": true,
4190
+ "kortyx.run.limit_reached": limitReached.limit
4191
+ });
4192
+ fallbackRunSpan?.end?.();
4193
+ finished = true;
4194
+ write({ type: "done" });
4195
+ out.end();
4196
+ return;
4197
+ } catch (pauseError) {
4198
+ if (abortSignal?.aborted || (0, import_core.isExecutionCancelled)(pauseError)) {
4199
+ await finishCancelled();
4200
+ return;
4201
+ }
4202
+ await Promise.allSettled(
4203
+ [...activePendingRequests.keys()].map(
4204
+ (token) => pendingStore?.delete(token)
4205
+ )
4206
+ );
4207
+ activePendingRequests.clear();
4208
+ err = pauseError;
4209
+ }
4210
+ }
4211
+ outcomeError = err;
1254
4212
  fallbackRunSpan?.fail?.(err);
1255
4213
  emitResumeFailure(err);
1256
4214
  console.error("[error:orchestrateGraphStream]", err);
1257
- out.write({
4215
+ write({
1258
4216
  type: "error",
1259
4217
  message: err instanceof Error ? err.message : String(err)
1260
4218
  });
1261
- out.write({ type: "done" });
4219
+ write({ type: "done" });
4220
+ finished = true;
1262
4221
  out.end();
4222
+ }).finally(() => {
4223
+ onOutcome?.({
4224
+ state: outcomeState,
4225
+ pending: pendingRecordToken ? activePendingRequests.get(pendingRecordToken) : void 0,
4226
+ checkpointId: sessionCheckpointId,
4227
+ ...outcomeError ? { error: outcomeError } : {}
4228
+ });
1263
4229
  });
1264
4230
  return out;
1265
4231
  }
@@ -1290,7 +4256,13 @@ function parseResumeMeta(msg) {
1290
4256
  return { token, requestId, selected, cancel };
1291
4257
  }
1292
4258
  async function tryPrepareResumeStream({
4259
+ limits,
4260
+ abortSignal,
1293
4261
  lastMessage,
4262
+ meta: suppliedMeta,
4263
+ emitOutput,
4264
+ onOutcome,
4265
+ validatePending,
1294
4266
  sessionId,
1295
4267
  config,
1296
4268
  selectWorkflow,
@@ -1299,17 +4271,38 @@ async function tryPrepareResumeStream({
1299
4271
  applyResumeSelection,
1300
4272
  frameworkAdapter
1301
4273
  }) {
1302
- const meta = parseResumeMeta(lastMessage);
4274
+ const meta = suppliedMeta ?? parseResumeMeta(lastMessage);
1303
4275
  if (!meta) return null;
1304
4276
  const store = frameworkAdapter?.pendingRequests;
1305
4277
  if (!store) return null;
1306
4278
  const pending = await store.get(meta.token);
1307
4279
  if (!pending || pending.requestId !== meta.requestId) {
1308
- console.log(
1309
- `[resume] pending not found or mismatched. token=${meta.token} requestId=${meta.requestId}`
4280
+ throw new Error(
4281
+ "Interrupt is expired, already consumed, or does not match the request."
1310
4282
  );
1311
- return null;
1312
4283
  }
4284
+ if (pending.sessionId && pending.sessionId !== sessionId)
4285
+ throw new Error("Interrupt belongs to another session.");
4286
+ await validatePending?.(pending);
4287
+ const isLimitPause = Boolean(pending.schema.meta?.__kortyxExecutionLimit);
4288
+ const savedBudget = pending.state?.config?.executionBudget;
4289
+ const resumedBudget = savedBudget && isLimitPause && !meta.cancel ? (0, import_core2.restartExecutionBudget)(savedBudget, limits) : savedBudget;
4290
+ if (abortSignal?.aborted) {
4291
+ onOutcome?.({
4292
+ state: pending.state,
4293
+ error: (0, import_core2.createExecutionCancelledError)()
4294
+ });
4295
+ return (async function* () {
4296
+ yield {
4297
+ type: "cancelled",
4298
+ runId: pending.runId,
4299
+ reason: "Execution cancelled."
4300
+ };
4301
+ yield { type: "done" };
4302
+ })();
4303
+ }
4304
+ if (store.take && !await store.take(meta.token))
4305
+ throw new Error("Interrupt has already been resumed or cancelled.");
1313
4306
  if (meta.cancel) {
1314
4307
  await store.delete(pending.token);
1315
4308
  emitTelemetryEvent({
@@ -1327,109 +4320,412 @@ async function tryPrepareResumeStream({
1327
4320
  },
1328
4321
  flush: true
1329
4322
  });
1330
- return null;
4323
+ onOutcome?.({ state: pending.state, cancelled: true });
4324
+ return (async function* () {
4325
+ yield { type: "done" };
4326
+ })();
1331
4327
  }
1332
- console.log(
1333
- `[resume] token=${meta.token} requestId=${meta.requestId} selected=${JSON.stringify(
1334
- meta.selected
1335
- )} sessionId=${sessionId}`
1336
- );
1337
- const resumeData = applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
1338
- const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
1339
- const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
1340
- const resumeStatePatch = isRecord4(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
1341
- const pendingData = isRecord4(pending.state?.data) ? pending.state?.data : {};
1342
- const workflowId = typeof pending.workflow === "string" && pending.workflow.trim() ? pending.workflow : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() ? defaultWorkflowId : "job-search";
1343
- const resumedState = {
1344
- // For static breakpoints, resume with null input (set in orchestrator),
1345
- // and stash the user selection into data so the next node can read it.
1346
- input: "",
1347
- lastNode: "__start__",
1348
- currentWorkflow: workflowId,
1349
- config,
1350
- runtime: {},
1351
- conversationHistory: [],
1352
- awaitingHumanInput: false,
1353
- data: {
1354
- ...pendingData,
1355
- ...resumeDataPatch
1356
- }
1357
- };
1358
- const wf = await selectWorkflow(resumedState.currentWorkflow);
1359
- const telemetryConfig = prepareWorkflowTelemetry({
1360
- config,
1361
- workflow: wf,
1362
- runId: pending.runId,
1363
- sessionId,
1364
- knownWorkflowIds
1365
- });
1366
- resumedState.config = telemetryConfig;
1367
- const resumeUpdate = {};
1368
- if (Object.keys(resumeDataPatch).length > 0) {
1369
- resumeUpdate.data = {
1370
- ...pendingData,
1371
- ...resumeDataPatch
4328
+ try {
4329
+ const resumeData = isLimitPause ? {} : applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
4330
+ const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
4331
+ const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
4332
+ const resumeStatePatch = isRecord4(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
4333
+ const pendingData = isRecord4(pending.state?.data) ? pending.state?.data : {};
4334
+ const workflowId = typeof pending.workflow === "string" && pending.workflow.trim() ? pending.workflow : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() ? defaultWorkflowId : "job-search";
4335
+ const resumedState = {
4336
+ // For static breakpoints, resume with null input (set in orchestrator),
4337
+ // and stash the user selection into data so the next node can read it.
4338
+ input: pending.state ? pending.state.input : "",
4339
+ lastNode: "__start__",
4340
+ currentWorkflow: workflowId,
4341
+ config,
4342
+ runtime: {},
4343
+ conversationHistory: [],
4344
+ awaitingHumanInput: false,
4345
+ data: {
4346
+ ...pendingData,
4347
+ ...resumeDataPatch
4348
+ }
1372
4349
  };
1373
- }
1374
- if (resumeStatePatch) {
1375
- resumeUpdate.runtime = resumeStatePatch;
1376
- }
1377
- const hasResumeUpdate = Object.keys(resumeUpdate).length > 0;
1378
- const resumeValue = meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
1379
- const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
1380
- const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
1381
- ...telemetryConfig,
1382
- resume: true,
1383
- ...resumeValue !== void 0 ? { resumeValue } : {},
1384
- ...resumeCheckpointId ? { resumeCheckpointId } : {},
1385
- ...hasResumeUpdate ? { resumeUpdate } : {}
1386
- });
1387
- await store.delete(pending.token);
1388
- const response = responseFromSelection(meta.selected);
1389
- const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
1390
- const responseCaptured = Boolean(
1391
- response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
1392
- );
1393
- emitTelemetryEvent({
1394
- config: telemetryConfig,
1395
- type: "interrupt.resolved",
1396
- correlation: {
4350
+ const wf = await selectWorkflow(resumedState.currentWorkflow);
4351
+ const telemetryConfig = prepareWorkflowTelemetry({
4352
+ config: {
4353
+ ...config,
4354
+ ...resumedBudget ? { executionBudget: resumedBudget } : {},
4355
+ ...pending.state?.config?.context ? { context: pending.state.config.context } : {},
4356
+ ...pending.state?.config?.executionContract ? { executionContract: pending.state.config.executionContract } : {},
4357
+ ...pending.state?.config?.executionBranchId ? { executionBranchId: pending.state.config.executionBranchId } : {}
4358
+ },
4359
+ workflow: wf,
1397
4360
  runId: pending.runId,
1398
4361
  sessionId,
1399
- workflowId,
1400
- nodeId: pending.node
1401
- },
1402
- payload: {
1403
- interruptId: pending.requestId,
1404
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
1405
- resumeOutcome: "resumed",
1406
- responseCaptured,
1407
- ...responseCaptured && response ? { response } : {}
1408
- },
1409
- flush: true
1410
- });
1411
- const args = {
1412
- sessionId,
1413
- runId: pending.runId,
1414
- graph: resumedGraph,
1415
- state: resumedState,
1416
- config: {
4362
+ knownWorkflowIds
4363
+ });
4364
+ resumedState.config = telemetryConfig;
4365
+ const resumeUpdate = pending.graphSnapshot ? {
4366
+ config: {
4367
+ ...telemetryConfig,
4368
+ context: pending.state?.config?.context ?? telemetryConfig.context
4369
+ }
4370
+ } : {};
4371
+ if (isLimitPause && pending.graphSnapshot) {
4372
+ pending.graphSnapshot.checkpoint.channel_values.config = telemetryConfig;
4373
+ if (resumeStatePatch)
4374
+ pending.graphSnapshot.checkpoint.channel_values.runtime = {
4375
+ ...pending.graphSnapshot.checkpoint.channel_values.runtime,
4376
+ ...resumeStatePatch
4377
+ };
4378
+ }
4379
+ if (pending.graphSnapshot && frameworkAdapter)
4380
+ await (0, import_runtime2.restoreGraphSnapshot)(
4381
+ frameworkAdapter.checkpointer,
4382
+ pending.graphSnapshot,
4383
+ pending.runId
4384
+ );
4385
+ if (Object.keys(resumeDataPatch).length > 0) {
4386
+ resumeUpdate.data = {
4387
+ ...pendingData,
4388
+ ...resumeDataPatch
4389
+ };
4390
+ }
4391
+ if (resumeStatePatch) {
4392
+ resumeUpdate.runtime = resumeStatePatch;
4393
+ }
4394
+ const hasResumeUpdate = !isLimitPause && Object.keys(resumeUpdate).length > 0;
4395
+ const resumeValue = isLimitPause ? void 0 : meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
4396
+ const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
4397
+ const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
1417
4398
  ...telemetryConfig,
1418
4399
  resume: true,
1419
- telemetryInterruptId: pending.requestId,
1420
- telemetryInterruptNodeId: pending.node,
1421
4400
  ...resumeValue !== void 0 ? { resumeValue } : {},
1422
4401
  ...resumeCheckpointId ? { resumeCheckpointId } : {},
1423
4402
  ...hasResumeUpdate ? { resumeUpdate } : {}
4403
+ });
4404
+ await store.delete(pending.token);
4405
+ const response = responseFromSelection(meta.selected);
4406
+ const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
4407
+ const responseCaptured = Boolean(
4408
+ response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
4409
+ );
4410
+ emitTelemetryEvent({
4411
+ config: telemetryConfig,
4412
+ type: "interrupt.resolved",
4413
+ correlation: {
4414
+ runId: pending.runId,
4415
+ sessionId,
4416
+ workflowId,
4417
+ nodeId: pending.node
4418
+ },
4419
+ payload: {
4420
+ interruptId: pending.requestId,
4421
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
4422
+ resumeOutcome: "resumed",
4423
+ responseCaptured,
4424
+ ...responseCaptured && response ? { response } : {}
4425
+ },
4426
+ flush: true
4427
+ });
4428
+ const args = {
4429
+ abortSignal,
4430
+ emitOutput,
4431
+ onOutcome,
4432
+ sessionId,
4433
+ runId: pending.runId,
4434
+ graph: resumedGraph,
4435
+ state: resumedState,
4436
+ config: {
4437
+ ...telemetryConfig,
4438
+ resume: true,
4439
+ telemetryInterruptId: pending.requestId,
4440
+ telemetryInterruptNodeId: pending.node,
4441
+ ...resumeValue !== void 0 ? { resumeValue } : {},
4442
+ ...resumeCheckpointId ? { resumeCheckpointId } : {},
4443
+ ...hasResumeUpdate ? { resumeUpdate } : {}
4444
+ },
4445
+ selectWorkflow,
4446
+ knownWorkflowIds,
4447
+ frameworkAdapter
4448
+ };
4449
+ const stream = await orchestrateGraphStream(args);
4450
+ return stream;
4451
+ } catch (error) {
4452
+ await store.save(pending);
4453
+ throw error;
4454
+ }
4455
+ }
4456
+
4457
+ // src/execution/execute.ts
4458
+ var handleSchema = import_zod3.z.object({
4459
+ token: import_zod3.z.string().min(1),
4460
+ requestId: import_zod3.z.string().min(1),
4461
+ sessionId: import_zod3.z.string().min(1),
4462
+ runId: import_zod3.z.string().min(1)
4463
+ }).strict();
4464
+ var responseSchema = import_zod3.z.discriminatedUnion("type", [
4465
+ import_zod3.z.object({ type: import_zod3.z.literal("text"), text: import_zod3.z.string().min(1) }).strict(),
4466
+ import_zod3.z.object({
4467
+ type: import_zod3.z.literal("select"),
4468
+ ids: import_zod3.z.array(import_zod3.z.string().min(1)).min(1)
4469
+ }).strict(),
4470
+ import_zod3.z.object({ type: import_zod3.z.literal("cancel") }).strict()
4471
+ ]);
4472
+ function resultFromOutcome(outcome, runId, sessionId) {
4473
+ const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
4474
+ const info = {
4475
+ runId,
4476
+ sessionId,
4477
+ ...outcome.checkpointId ? { checkpointId: outcome.checkpointId } : {},
4478
+ ...usage ? { usage } : {}
4479
+ };
4480
+ if ((0, import_core3.isExecutionCancelled)(outcome.error))
4481
+ return { ...info, status: "cancelled", reason: "Execution cancelled." };
4482
+ if (outcome.error) {
4483
+ const error = outcome.error;
4484
+ return {
4485
+ ...info,
4486
+ status: "failed",
4487
+ error: {
4488
+ code: typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "EXECUTION_FAILED",
4489
+ message: error instanceof Error ? error.message : String(error)
4490
+ }
4491
+ };
4492
+ }
4493
+ if (outcome.cancelled)
4494
+ return { ...info, status: "cancelled", reason: "cancelled_by_client" };
4495
+ if (outcome.pending) {
4496
+ const pending = outcome.pending;
4497
+ const meta = Object.fromEntries(
4498
+ Object.entries(pending.schema.meta ?? {}).filter(
4499
+ ([key]) => !key.startsWith("__kortyx")
4500
+ )
4501
+ );
4502
+ return {
4503
+ ...info,
4504
+ status: "suspended",
4505
+ ...pending.schema.meta?.__kortyxExecutionLimit ? {
4506
+ reason: "limit_reached",
4507
+ limit: pending.schema.meta.executionLimit
4508
+ } : {},
4509
+ interrupt: {
4510
+ workflow: pending.workflow,
4511
+ node: pending.node,
4512
+ input: {
4513
+ ...pending.schema,
4514
+ meta,
4515
+ options: pending.options.map(({ id, label, description }) => ({
4516
+ id,
4517
+ label,
4518
+ description
4519
+ }))
4520
+ }
4521
+ },
4522
+ resume: {
4523
+ token: pending.token,
4524
+ requestId: pending.requestId,
4525
+ sessionId,
4526
+ runId
4527
+ }
4528
+ };
4529
+ }
4530
+ return { ...info, status: "completed", data: outcome.state.data ?? {} };
4531
+ }
4532
+ function runtimeConfig(services, sessionId, context) {
4533
+ return {
4534
+ session: { id: sessionId },
4535
+ ...context ? { context } : {},
4536
+ getProvider: services.getProvider,
4537
+ checkpointer: services.frameworkAdapter.checkpointer,
4538
+ selectWorkflow: (id) => services.registry.select(id),
4539
+ ...services.telemetry ? { telemetry: services.telemetry } : {}
4540
+ };
4541
+ }
4542
+ async function executeWorkflow(services, args) {
4543
+ const select = (id) => services.registry.select(id);
4544
+ const workflow = await resolveExecutionWorkflow(select, args.workflow);
4545
+ const input = parseExecutionInput(workflow, args.input);
4546
+ if (args.sessionId !== void 0 && !args.sessionId.trim())
4547
+ throw new ExecutionRequestError(
4548
+ "INVALID_SESSION",
4549
+ "sessionId must not be empty."
4550
+ );
4551
+ const sessionId = args.sessionId ?? (0, import_runtime3.makeRequestId)("session");
4552
+ const runId = (0, import_runtime3.makeRequestId)("run");
4553
+ const config = prepareWorkflowTelemetry({
4554
+ config: {
4555
+ ...runtimeConfig(services, sessionId, args.context),
4556
+ ...services.limits || args.limits ? {
4557
+ executionBudget: (0, import_core3.createExecutionBudget)(
4558
+ services.limits,
4559
+ args.limits
4560
+ )
4561
+ } : {},
4562
+ executionContract: { id: workflow.id, version: workflow.version }
1424
4563
  },
1425
- selectWorkflow,
1426
- knownWorkflowIds,
1427
- frameworkAdapter
4564
+ workflow,
4565
+ runId,
4566
+ sessionId,
4567
+ knownWorkflowIds: services.knownWorkflowIds
4568
+ });
4569
+ const state = await (0, import_runtime3.buildInitialGraphState)({
4570
+ input,
4571
+ runtime: {},
4572
+ config,
4573
+ defaultWorkflowId: workflow.id
4574
+ });
4575
+ return new Promise((resolve2) => {
4576
+ const onOutcome = (outcome) => resolve2(resultFromOutcome(outcome, runId, sessionId));
4577
+ void (async () => {
4578
+ (0, import_core3.throwIfExecutionAborted)(args.abortSignal);
4579
+ const graph = await (0, import_runtime3.createExecutionGraph)(workflow, config);
4580
+ await orchestrateGraphStream({
4581
+ graph,
4582
+ state,
4583
+ config,
4584
+ runId,
4585
+ sessionId,
4586
+ selectWorkflow: select,
4587
+ frameworkAdapter: services.frameworkAdapter,
4588
+ knownWorkflowIds: services.knownWorkflowIds,
4589
+ abortSignal: args.abortSignal,
4590
+ emitOutput: false,
4591
+ onOutcome
4592
+ });
4593
+ })().catch((error) => onOutcome({ state, error }));
4594
+ });
4595
+ }
4596
+ async function resumeWorkflow(services, args) {
4597
+ const parsed = import_zod3.z.object({ resume: handleSchema, response: responseSchema }).safeParse(args);
4598
+ if (!parsed.success)
4599
+ throw new ExecutionRequestError("INVALID_RESUME", parsed.error.message);
4600
+ const { resume: handle, response } = parsed.data;
4601
+ const select = (id) => services.registry.select(id);
4602
+ const workflow = await resolveExecutionWorkflow(select, args.workflow);
4603
+ const validatePending = async (pending) => {
4604
+ const contract = pending.state?.config?.executionContract ?? {
4605
+ id: pending.workflow
4606
+ };
4607
+ if (pending.runId !== handle.runId || contract.id !== workflow.id || contract.version && contract.version !== workflow.version)
4608
+ throw new ExecutionRequestError(
4609
+ "RESUME_MISMATCH",
4610
+ "Resume handle does not match the root execution and workflow version."
4611
+ );
4612
+ if (response.type === "cancel") return;
4613
+ if (response.type === "text") {
4614
+ if (pending.schema.kind !== "text")
4615
+ throw new ExecutionRequestError(
4616
+ "INVALID_RESPONSE",
4617
+ "This interrupt requires a selection."
4618
+ );
4619
+ } else {
4620
+ const multi = pending.schema.kind === "multi-choice" || pending.schema.multiple;
4621
+ if (pending.schema.kind === "text" || !multi && response.ids.length !== 1 || new Set(response.ids).size !== response.ids.length)
4622
+ throw new ExecutionRequestError(
4623
+ "INVALID_RESPONSE",
4624
+ "Invalid selection for this interrupt."
4625
+ );
4626
+ if (pending.options.length && response.ids.some(
4627
+ (id) => !pending.options.some((option) => option.id === id)
4628
+ ))
4629
+ throw new ExecutionRequestError(
4630
+ "INVALID_RESPONSE",
4631
+ "Selection contains an unknown option."
4632
+ );
4633
+ }
1428
4634
  };
1429
- const stream = await orchestrateGraphStream(args);
1430
- return stream;
4635
+ return new Promise((resolve2, reject) => {
4636
+ void tryPrepareResumeStream({
4637
+ limits: args.limits,
4638
+ abortSignal: args.abortSignal,
4639
+ meta: {
4640
+ token: handle.token,
4641
+ requestId: handle.requestId,
4642
+ selected: response.type === "text" ? [response.text] : response.type === "select" ? response.ids : [],
4643
+ cancel: response.type === "cancel"
4644
+ },
4645
+ sessionId: handle.sessionId,
4646
+ config: runtimeConfig(services, handle.sessionId),
4647
+ selectWorkflow: select,
4648
+ frameworkAdapter: services.frameworkAdapter,
4649
+ knownWorkflowIds: services.knownWorkflowIds,
4650
+ validatePending,
4651
+ applyResumeSelection: () => ({}),
4652
+ emitOutput: false,
4653
+ onOutcome: (outcome) => resolve2(resultFromOutcome(outcome, handle.runId, handle.sessionId))
4654
+ }).then((stream) => {
4655
+ if (!stream)
4656
+ reject(
4657
+ new ExecutionRequestError(
4658
+ "INVALID_RESUME",
4659
+ "No waiting execution found."
4660
+ )
4661
+ );
4662
+ }).catch(
4663
+ (error) => reject(
4664
+ error instanceof ExecutionRequestError ? error : new ExecutionRequestError(
4665
+ "INVALID_RESUME",
4666
+ error instanceof Error ? error.message : String(error)
4667
+ )
4668
+ )
4669
+ );
4670
+ });
4671
+ }
4672
+
4673
+ // src/telemetry/workflow-call-branch.ts
4674
+ var import_node_crypto3 = require("crypto");
4675
+ var import_hooks = __toESM(require_dist());
4676
+ function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
4677
+ const branchId = (0, import_node_crypto3.randomUUID)();
4678
+ const emitted = /* @__PURE__ */ new Set();
4679
+ for (const request of requests) {
4680
+ if (request.state)
4681
+ request.state.config = {
4682
+ ...request.state.config,
4683
+ executionBranchId: branchId
4684
+ };
4685
+ const seen = /* @__PURE__ */ new Set();
4686
+ const visit = (value) => {
4687
+ if (!value || typeof value !== "object" || seen.has(value)) return;
4688
+ seen.add(value);
4689
+ const record = value;
4690
+ if (typeof record.invocationId === "string" && typeof record.fingerprint === "string" && Array.isArray(record.interrupts)) {
4691
+ const previous = record.telemetry;
4692
+ if (previous) {
4693
+ record.sequence = 0;
4694
+ record.telemetry = { ...previous, branchId };
4695
+ if (!emitted.has(record.invocationId)) {
4696
+ emitted.add(record.invocationId);
4697
+ (0, import_hooks.emitWorkflowCall)(
4698
+ telemetry ? {
4699
+ ...telemetry,
4700
+ correlation: {
4701
+ runId: request.runId,
4702
+ workflowId: request.workflow,
4703
+ ...request.sessionId ? { sessionId: request.sessionId } : {}
4704
+ }
4705
+ } : void 0,
4706
+ "workflow.call.restored",
4707
+ {
4708
+ ...previous,
4709
+ branchId,
4710
+ sourceBranchId: previous.branchId,
4711
+ sourceRunId,
4712
+ sequence: 0,
4713
+ status: record.status
4714
+ }
4715
+ );
4716
+ }
4717
+ }
4718
+ }
4719
+ for (const child of Object.values(record)) visit(child);
4720
+ };
4721
+ visit(request);
4722
+ }
4723
+ return branchId;
1431
4724
  }
1432
4725
 
4726
+ // src/chat/process-chat.ts
4727
+ var import_runtime4 = require("@kortyx/runtime");
4728
+
1433
4729
  // src/utils/extract-latest-message.ts
1434
4730
  function extractLatestUserMessage(messages) {
1435
4731
  if (!messages || messages.length === 0) return "";
@@ -1445,6 +4741,7 @@ function extractLatestUserMessage(messages) {
1445
4741
  // src/chat/process-chat.ts
1446
4742
  async function streamChat({
1447
4743
  messages,
4744
+ abortSignal,
1448
4745
  options,
1449
4746
  sessionId,
1450
4747
  defaultWorkflowId,
@@ -1457,7 +4754,7 @@ async function streamChat({
1457
4754
  applyResumeSelection
1458
4755
  }) {
1459
4756
  const config = await loadRuntimeConfig(options);
1460
- let runtimeConfig = {
4757
+ let runtimeConfig2 = {
1461
4758
  ...config,
1462
4759
  getProvider,
1463
4760
  ...frameworkAdapter ? { checkpointer: frameworkAdapter.checkpointer } : {}
@@ -1468,6 +4765,7 @@ async function streamChat({
1468
4765
  "streamChat requires selectWorkflow or workflowRegistry to resolve workflows."
1469
4766
  );
1470
4767
  }
4768
+ runtimeConfig2.selectWorkflow = workflowSelector;
1471
4769
  const fallbackSessionId = options?.sessionId;
1472
4770
  const resolvedSessionId = sessionId || fallbackSessionId || "anonymous-session";
1473
4771
  const last = messages[messages.length - 1];
@@ -1489,16 +4787,11 @@ async function streamChat({
1489
4787
  if (requestedWorkflowId.trim() === "") delete runtime.requestedWorkflow;
1490
4788
  else runtime.requestedWorkflow = requestedWorkflowId;
1491
4789
  }
1492
- const baseState = await (0, import_runtime3.buildInitialGraphState)({
1493
- input,
1494
- config: runtimeConfig,
1495
- runtime,
1496
- ...defaultWorkflowId ? { defaultWorkflowId } : {}
1497
- });
1498
4790
  const resumeStream = await tryPrepareResumeStream({
4791
+ abortSignal,
1499
4792
  lastMessage: last,
1500
4793
  sessionId: resolvedSessionId,
1501
- config: runtimeConfig,
4794
+ config: runtimeConfig2,
1502
4795
  selectWorkflow: workflowSelector,
1503
4796
  knownWorkflowIds,
1504
4797
  ...frameworkAdapter ? { frameworkAdapter } : {},
@@ -1506,7 +4799,13 @@ async function streamChat({
1506
4799
  ...applyResumeSelection ? { applyResumeSelection } : {}
1507
4800
  });
1508
4801
  if (resumeStream) return resumeStream;
1509
- const runId = (0, import_runtime3.makeRequestId)("run");
4802
+ const baseState = await (0, import_runtime4.buildInitialGraphState)({
4803
+ input,
4804
+ config: runtimeConfig2,
4805
+ runtime,
4806
+ ...defaultWorkflowId ? { defaultWorkflowId } : {}
4807
+ });
4808
+ const runId = (0, import_runtime4.makeRequestId)("run");
1510
4809
  let headCheckpoint = !isResumeRequest && frameworkAdapter?.sessionCheckpoints ? await frameworkAdapter.sessionCheckpoints.getHead(resolvedSessionId) : null;
1511
4810
  if (!isResumeRequest && !headCheckpoint && frameworkAdapter?.sessionCheckpoints) {
1512
4811
  headCheckpoint = await frameworkAdapter.sessionCheckpoints.append({
@@ -1526,18 +4825,26 @@ async function streamChat({
1526
4825
  }
1527
4826
  const currentWorkflow = requestedWorkflowId ? baseState.currentWorkflow : headCheckpoint?.state.currentWorkflow ?? baseState.currentWorkflow;
1528
4827
  const selectedWorkflow = await workflowSelector(currentWorkflow);
1529
- runtimeConfig = prepareWorkflowTelemetry({
1530
- config: runtimeConfig,
4828
+ runtimeConfig2 = prepareWorkflowTelemetry({
4829
+ config: runtimeConfig2,
1531
4830
  workflow: selectedWorkflow,
1532
4831
  runId,
1533
4832
  sessionId: resolvedSessionId,
1534
4833
  knownWorkflowIds
1535
4834
  });
1536
- const graph = await (0, import_runtime3.createExecutionGraph)(selectedWorkflow, runtimeConfig);
4835
+ runtimeConfig2 = {
4836
+ ...runtimeConfig2,
4837
+ executionContract: {
4838
+ id: selectedWorkflow.id,
4839
+ version: selectedWorkflow.version
4840
+ }
4841
+ };
4842
+ const parsedInput = selectedWorkflow.inputSchema ? parseExecutionInput(selectedWorkflow, input) : input;
4843
+ const graph = await (0, import_runtime4.createExecutionGraph)(selectedWorkflow, runtimeConfig2);
1537
4844
  const initialState = headCheckpoint ? {
1538
4845
  ...headCheckpoint.state,
1539
- input,
1540
- config: runtimeConfig,
4846
+ input: parsedInput,
4847
+ config: runtimeConfig2,
1541
4848
  currentWorkflow,
1542
4849
  awaitingHumanInput: false,
1543
4850
  runtime: {
@@ -1545,13 +4852,19 @@ async function streamChat({
1545
4852
  ...runtime.requestedWorkflow ? { requestedWorkflow: runtime.requestedWorkflow } : {},
1546
4853
  ...runtime.priorMessages ? { priorMessages: runtime.priorMessages } : {}
1547
4854
  }
1548
- } : { ...baseState, config: runtimeConfig, currentWorkflow };
4855
+ } : {
4856
+ ...baseState,
4857
+ input: parsedInput,
4858
+ config: runtimeConfig2,
4859
+ currentWorkflow
4860
+ };
1549
4861
  const orchestratedStream = await orchestrateGraphStream({
4862
+ abortSignal,
1550
4863
  sessionId: resolvedSessionId,
1551
4864
  runId,
1552
4865
  graph,
1553
4866
  state: initialState,
1554
- config: runtimeConfig,
4867
+ config: runtimeConfig2,
1555
4868
  selectWorkflow: workflowSelector,
1556
4869
  knownWorkflowIds,
1557
4870
  ...frameworkAdapter ? { frameworkAdapter } : {}
@@ -1560,19 +4873,22 @@ async function streamChat({
1560
4873
  }
1561
4874
 
1562
4875
  // src/chat/create-agent.ts
1563
- var agentProcessOptionsSchema = import_zod2.z.object({
1564
- sessionId: import_zod2.z.string().optional(),
1565
- workflowId: import_zod2.z.string().optional(),
1566
- context: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional()
4876
+ var agentProcessOptionsSchema = import_zod4.z.object({
4877
+ limits: import_core4.ExecutionLimitsSchema.optional(),
4878
+ abortSignal: import_zod4.z.custom((value) => value instanceof AbortSignal).optional(),
4879
+ sessionId: import_zod4.z.string().optional(),
4880
+ workflowId: import_zod4.z.string().optional(),
4881
+ context: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
1567
4882
  }).strict();
1568
- var createAgentArgsBaseSchema = import_zod2.z.object({
1569
- getProvider: import_zod2.z.unknown().optional(),
1570
- workflows: import_zod2.z.array(import_zod2.z.unknown()).optional(),
1571
- workflowsDir: import_zod2.z.string().optional(),
1572
- workflowRegistry: import_zod2.z.unknown().optional(),
1573
- defaultWorkflowId: import_zod2.z.string().optional(),
1574
- frameworkAdapter: import_zod2.z.unknown().optional(),
1575
- telemetry: import_zod2.z.unknown().optional()
4883
+ var createAgentArgsBaseSchema = import_zod4.z.object({
4884
+ limits: import_core4.ExecutionLimitsSchema.optional(),
4885
+ getProvider: import_zod4.z.unknown().optional(),
4886
+ workflows: import_zod4.z.array(import_zod4.z.unknown()).optional(),
4887
+ workflowsDir: import_zod4.z.string().optional(),
4888
+ workflowRegistry: import_zod4.z.unknown().optional(),
4889
+ defaultWorkflowId: import_zod4.z.string().optional(),
4890
+ frameworkAdapter: import_zod4.z.unknown().optional(),
4891
+ telemetry: import_zod4.z.unknown().optional()
1576
4892
  }).strict();
1577
4893
  var createAgentArgsSchema = createAgentArgsBaseSchema.superRefine(
1578
4894
  (value, ctx) => {
@@ -1617,7 +4933,7 @@ var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
1617
4933
  return graphCheckpointId ? { ...request, graphCheckpointId } : request;
1618
4934
  };
1619
4935
  var clearPendingGraphWrites = async (frameworkAdapter, request) => {
1620
- if (!request.graphCheckpointId) return;
4936
+ if (request.graphSnapshot || !request.graphCheckpointId) return;
1621
4937
  const checkpointer = frameworkAdapter.checkpointer;
1622
4938
  if (!checkpointer?.deleteCheckpointWrites) return;
1623
4939
  await Promise.all([
@@ -1636,6 +4952,7 @@ var clearPendingGraphWrites = async (frameworkAdapter, request) => {
1636
4952
  function createAgent(args) {
1637
4953
  const parsedArgs = parseCreateAgentArgs(args);
1638
4954
  const {
4955
+ limits,
1639
4956
  getProvider,
1640
4957
  workflows,
1641
4958
  workflowsDir,
@@ -1645,24 +4962,24 @@ function createAgent(args) {
1645
4962
  telemetry
1646
4963
  } = parsedArgs;
1647
4964
  const resolvedDefaultWorkflowId = defaultWorkflowId;
1648
- const resolvedFrameworkAdapter = frameworkAdapter ?? (0, import_runtime4.createFrameworkAdapterFromEnv)();
4965
+ const resolvedFrameworkAdapter = frameworkAdapter ?? (0, import_runtime5.createFrameworkAdapterFromEnv)();
1649
4966
  const resolvedGetProvider = getProvider ?? import_providers.getProvider;
1650
4967
  const resolvedCwd = process.cwd();
1651
4968
  const registryPromise = (async () => {
1652
4969
  if (workflowRegistry) return workflowRegistry;
1653
4970
  if (workflows) {
1654
- return (0, import_runtime4.createInMemoryWorkflowRegistry)(workflows, {
4971
+ return (0, import_runtime5.createInMemoryWorkflowRegistry)(workflows, {
1655
4972
  fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
1656
4973
  });
1657
4974
  }
1658
4975
  if (workflowsDir) {
1659
- return (0, import_runtime4.createFileWorkflowRegistry)({
4976
+ return (0, import_runtime5.createFileWorkflowRegistry)({
1660
4977
  workflowsDir,
1661
4978
  fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
1662
4979
  });
1663
4980
  }
1664
4981
  const resolvedWorkflowsDir = (0, import_node_path.resolve)(resolvedCwd, "src", "workflows");
1665
- return (0, import_runtime4.createFileWorkflowRegistry)({
4982
+ return (0, import_runtime5.createFileWorkflowRegistry)({
1666
4983
  workflowsDir: resolvedWorkflowsDir,
1667
4984
  fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
1668
4985
  });
@@ -1700,12 +5017,19 @@ function createAgent(args) {
1700
5017
  return streamChat({
1701
5018
  ...resolvedDefaultWorkflowId ? { defaultWorkflowId: resolvedDefaultWorkflowId } : {},
1702
5019
  messages,
5020
+ abortSignal: parsedOptions?.abortSignal,
1703
5021
  options: parsedOptions,
1704
5022
  workflowRegistry: registry,
1705
5023
  ...knownWorkflowIds ? { knownWorkflowIds } : {},
1706
5024
  frameworkAdapter: resolvedFrameworkAdapter,
1707
5025
  getProvider: resolvedGetProvider,
1708
5026
  loadRuntimeConfig: (runtimeOptions) => ({
5027
+ ...limits || runtimeOptions?.limits ? {
5028
+ executionBudget: (0, import_core4.createExecutionBudget)(
5029
+ limits,
5030
+ runtimeOptions?.limits
5031
+ )
5032
+ } : {},
1709
5033
  ...runtimeOptions?.sessionId ? {
1710
5034
  session: {
1711
5035
  id: runtimeOptions.sessionId
@@ -1726,6 +5050,11 @@ function createAgent(args) {
1726
5050
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1727
5051
  )
1728
5052
  );
5053
+ restoreWorkflowCallBranch(
5054
+ activePendingRequests,
5055
+ telemetry,
5056
+ target?.runId ?? "unknown"
5057
+ );
1729
5058
  await Promise.all(
1730
5059
  activePendingRequests.map(
1731
5060
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
@@ -1757,6 +5086,7 @@ function createAgent(args) {
1757
5086
  return { ...result, activePendingRequests };
1758
5087
  };
1759
5088
  const fork = async (id, options) => {
5089
+ const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
1760
5090
  const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
1761
5091
  id,
1762
5092
  options
@@ -1766,6 +5096,11 @@ function createAgent(args) {
1766
5096
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1767
5097
  )
1768
5098
  );
5099
+ restoreWorkflowCallBranch(
5100
+ activePendingRequests,
5101
+ telemetry,
5102
+ source?.runId ?? "unknown"
5103
+ );
1769
5104
  await Promise.all(
1770
5105
  activePendingRequests.map(
1771
5106
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
@@ -1795,7 +5130,25 @@ function createAgent(args) {
1795
5130
  checkpoint: { ...result.checkpoint, activePendingRequests }
1796
5131
  };
1797
5132
  };
5133
+ const services = async () => {
5134
+ const registry = await registryPromise;
5135
+ if (!registry) throw new Error("Workflow registry is unavailable.");
5136
+ return {
5137
+ registry,
5138
+ limits,
5139
+ frameworkAdapter: resolvedFrameworkAdapter,
5140
+ getProvider: resolvedGetProvider,
5141
+ telemetry,
5142
+ knownWorkflowIds: (await registeredWorkflowsPromise)?.map(
5143
+ (workflow) => workflow.id
5144
+ )
5145
+ };
5146
+ };
5147
+ const execute = (async (options) => executeWorkflow(await services(), options));
5148
+ const resume = (async (options) => resumeWorkflow(await services(), options));
1798
5149
  return {
5150
+ execute,
5151
+ resume,
1799
5152
  projectTopology,
1800
5153
  streamChat: streamChat2,
1801
5154
  listCheckpoints,
@@ -1806,6 +5159,7 @@ function createAgent(args) {
1806
5159
  }
1807
5160
  // Annotate the CommonJS export names for ESM import in node:
1808
5161
  0 && (module.exports = {
5162
+ ExecutionRequestError,
1809
5163
  createAgent,
1810
5164
  createChatRouteHandler,
1811
5165
  createCheckpointRouteHandler,