@kortyx/agent 0.14.2 → 0.16.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,9 +17,2554 @@ 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
+ useInterrupt: () => useInterrupt,
59
+ useNodeState: () => useNodeState,
60
+ useReason: () => useReason2,
61
+ useRuntimeContext: () => useRuntimeContext,
62
+ useStructuredData: () => useStructuredData,
63
+ useWorkflow: () => useWorkflow,
64
+ useWorkflowState: () => useWorkflowState,
65
+ workflowCallFingerprint: () => workflowCallFingerprint
66
+ });
67
+ module2.exports = __toCommonJS2(index_exports2);
68
+ var import_node_async_hooks = require("async_hooks");
69
+ var storage = new import_node_async_hooks.AsyncLocalStorage();
70
+ var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
71
+ var normalizeNodeState = (value) => {
72
+ if (Array.isArray(value)) {
73
+ return { byIndex: [...value], byKey: {} };
74
+ }
75
+ if (isRecord5(value)) {
76
+ const byIndex = Array.isArray(value.byIndex) ? [...value.byIndex] : [];
77
+ const byKey = isRecord5(value.byKey) ? { ...value.byKey } : {};
78
+ return { byIndex, byKey };
79
+ }
80
+ return { byIndex: [], byKey: {} };
81
+ };
82
+ var cloneWorkflowState = (value) => isRecord5(value) ? { ...value } : {};
83
+ var cloneTokenUsage = (value) => {
84
+ if (!isRecord5(value)) return void 0;
85
+ const input = typeof value.input === "number" ? value.input : void 0;
86
+ const output = typeof value.output === "number" ? value.output : void 0;
87
+ const total = typeof value.total === "number" ? value.total : void 0;
88
+ if (input === void 0 && output === void 0 && total === void 0) {
89
+ return void 0;
90
+ }
91
+ return {
92
+ input: input ?? 0,
93
+ output: output ?? 0,
94
+ total: total ?? 0
95
+ };
96
+ };
97
+ var getHookRuntimeState = (state) => {
98
+ const runtime = state.runtime ?? {};
99
+ const internal = isRecord5(runtime.__kortyx) ? runtime.__kortyx : void 0;
100
+ const result = {};
101
+ if (isRecord5(internal?.nodeState)) result.nodeState = internal?.nodeState;
102
+ if (isRecord5(internal?.workflowState)) {
103
+ result.workflowState = internal?.workflowState;
104
+ }
105
+ return result;
106
+ };
107
+ var createInternalContext = (ctx) => {
108
+ const hookRuntimeState = getHookRuntimeState(ctx.state);
109
+ const nodeId = ctx.node.graph.node;
110
+ const storedNodeState = isRecord5(hookRuntimeState.nodeState) ? hookRuntimeState.nodeState : void 0;
111
+ const storedNodeId = typeof storedNodeState?.nodeId === "string" ? String(storedNodeState.nodeId) : "";
112
+ const storedState = storedNodeState?.state;
113
+ const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
114
+ return {
115
+ ...ctx,
116
+ workflowCallActive: false,
117
+ workflowCallIds: /* @__PURE__ */ new Set(),
118
+ nodeStateIndex: 0,
119
+ reasonCallIndex: 0,
120
+ currentNodeState,
121
+ workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
122
+ tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
123
+ stateDirty: false,
124
+ runtimeDirty: false
125
+ };
126
+ };
127
+ var buildRuntimeStateUpdates = (ctx) => {
128
+ const updates = {};
129
+ if (ctx.stateDirty) {
130
+ updates.__kortyx = {
131
+ nodeState: {
132
+ nodeId: ctx.node.graph.node,
133
+ state: ctx.currentNodeState
134
+ },
135
+ workflowState: ctx.workflowState
136
+ };
137
+ }
138
+ if (ctx.runtimeDirty && ctx.tokenUsage) {
139
+ updates.tokenUsage = ctx.tokenUsage;
140
+ }
141
+ return Object.keys(updates).length > 0 ? updates : null;
142
+ };
143
+ var cleanupCompletedReasonCheckpoints = (ctx) => {
144
+ let changed = false;
145
+ for (const [key, value] of Object.entries(ctx.currentNodeState.byKey)) {
146
+ if (!key.startsWith("__useReason:")) continue;
147
+ if (!isRecord5(value) || value.status !== "completed") continue;
148
+ delete ctx.currentNodeState.byKey[key];
149
+ changed = true;
150
+ }
151
+ if (changed) ctx.stateDirty = true;
152
+ };
153
+ var toTokenUsageDelta = (usage) => {
154
+ if (!usage) return void 0;
155
+ const input = usage.input ?? 0;
156
+ const output = usage.output ?? 0;
157
+ const total = usage.total ?? input + output + (usage.reasoning ?? 0);
158
+ if (input === 0 && output === 0 && total === 0) {
159
+ return void 0;
160
+ }
161
+ return {
162
+ input,
163
+ output,
164
+ total
165
+ };
166
+ };
167
+ function accumulateTokenUsage(usage) {
168
+ const delta = toTokenUsageDelta(usage);
169
+ if (!delta) return;
170
+ const ctx = getHookContext();
171
+ const current = ctx.tokenUsage ?? {
172
+ input: 0,
173
+ output: 0,
174
+ total: 0
175
+ };
176
+ ctx.tokenUsage = {
177
+ input: current.input + delta.input,
178
+ output: current.output + delta.output,
179
+ total: current.total + delta.total
180
+ };
181
+ ctx.runtimeDirty = true;
182
+ }
183
+ function getReasonTraceAdapter() {
184
+ return getHookContext().reasonTrace;
185
+ }
186
+ async function runWithHookContext(ctx, fn) {
187
+ const internal = createInternalContext(ctx);
188
+ try {
189
+ const result = await storage.run(internal, fn);
190
+ cleanupCompletedReasonCheckpoints(internal);
191
+ for (const key of Object.keys(internal.currentNodeState.byKey)) {
192
+ if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
193
+ internal.currentNodeState.byKey[key] = null;
194
+ internal.stateDirty = true;
195
+ }
196
+ }
197
+ return { result, runtimeUpdates: buildRuntimeStateUpdates(internal) };
198
+ } catch (err) {
199
+ const runtimeUpdates = buildRuntimeStateUpdates(internal);
200
+ if (runtimeUpdates) {
201
+ const currentRuntime = isRecord5(internal.state.runtime) ? internal.state.runtime : {};
202
+ internal.state.runtime = {
203
+ ...currentRuntime,
204
+ ...runtimeUpdates
205
+ };
206
+ if (err && typeof err === "object") {
207
+ err.__kortyxHookStatePatch = runtimeUpdates;
208
+ }
209
+ }
210
+ throw err;
211
+ }
212
+ }
213
+ function getHookContext() {
214
+ const ctx = storage.getStore();
215
+ if (!ctx) {
216
+ throw new Error("Hooks can only be used while a node is executing.");
217
+ }
218
+ return ctx;
219
+ }
220
+ var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
221
+ var readReasonCheckpoint = (raw) => {
222
+ if (!raw || typeof raw !== "object") return void 0;
223
+ const value = raw;
224
+ if (value.status !== "awaiting_interrupt") return void 0;
225
+ if (typeof value.firstText !== "string") return void 0;
226
+ if (!value.request || typeof value.request !== "object") return void 0;
227
+ return {
228
+ status: "awaiting_interrupt",
229
+ request: value.request,
230
+ firstText: value.firstText,
231
+ ...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
232
+ ...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
233
+ ...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
234
+ ...Object.hasOwn(value, "firstProviderMetadata") ? {
235
+ firstProviderMetadata: value.firstProviderMetadata
236
+ } : {},
237
+ ...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
238
+ ...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
239
+ };
240
+ };
241
+ var readReasonCompletedCheckpoint = (raw) => {
242
+ if (!raw || typeof raw !== "object") return void 0;
243
+ const value = raw;
244
+ if (value.status !== "completed") return void 0;
245
+ if (!value.result || typeof value.result !== "object") return void 0;
246
+ return {
247
+ status: "completed",
248
+ result: value.result
249
+ };
250
+ };
251
+ var resolveHookStatePatch = (args) => ({
252
+ __kortyx: {
253
+ nodeState: {
254
+ nodeId: args.nodeId,
255
+ state: args.currentNodeState
256
+ },
257
+ workflowState: args.workflowState
258
+ }
259
+ });
260
+ var parseWithSchema = (schema, value, label) => {
261
+ if (!schema) return value;
262
+ const parsed = schema.safeParse(value);
263
+ if (parsed.success) return parsed.data;
264
+ const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
265
+ const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
266
+ throw new Error(`${label} validation failed: ${reason}`);
267
+ };
268
+ var awaitInterruptInternal = (args) => {
269
+ const ctx = getHookContext();
270
+ const request = parseWithSchema(
271
+ args.requestSchema,
272
+ args.request,
273
+ "useInterrupt request"
274
+ );
275
+ const userMeta = args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? args.meta : {};
276
+ const resumeStatePatch = resolveHookStatePatch({
277
+ nodeId: ctx.node.graph.node,
278
+ currentNodeState: ctx.currentNodeState,
279
+ workflowState: ctx.workflowState
280
+ });
281
+ const enrichedRequest = {
282
+ ...request,
283
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
284
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
285
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
286
+ meta: {
287
+ ...userMeta,
288
+ __kortyxResumeStatePatch: {
289
+ ...resumeStatePatch,
290
+ ...ctx.tokenUsage ? { tokenUsage: ctx.tokenUsage } : {}
291
+ }
292
+ }
293
+ };
294
+ const raw = ctx.node.awaitInterrupt(enrichedRequest);
295
+ const response = parseWithSchema(
296
+ args.responseSchema,
297
+ raw,
298
+ "useInterrupt response"
299
+ );
300
+ return Promise.resolve(response);
301
+ };
302
+ var import_node_crypto4 = require("crypto");
303
+ var parseStructuredPath = (path, source) => {
304
+ if (path.length === 0) {
305
+ throw new Error(`${source} path must be a non-empty dot-separated string.`);
306
+ }
307
+ const parts = path.split(".");
308
+ if (parts.some((part) => part.length === 0)) {
309
+ throw new Error(
310
+ `${source} path "${path}" must not contain empty segments.`
311
+ );
312
+ }
313
+ return parts;
314
+ };
315
+ var assertStructuredPath = (path, source) => {
316
+ parseStructuredPath(path, source);
317
+ };
318
+ var createStructuredStreamId = () => {
319
+ try {
320
+ return (0, import_node_crypto4.randomUUID)();
321
+ } catch {
322
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
323
+ }
324
+ };
325
+ var emitStructuredData = (args) => {
326
+ const ctx = getHookContext();
327
+ const streamId = typeof args.streamId === "string" && args.streamId.length > 0 ? args.streamId : createStructuredStreamId();
328
+ const dataType = typeof args.dataType === "string" && args.dataType.length > 0 ? args.dataType : "generic";
329
+ const base = {
330
+ node: ctx.node.graph.node,
331
+ streamId,
332
+ dataType,
333
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
334
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
335
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {}
336
+ };
337
+ if (args.kind === "set") {
338
+ assertStructuredPath(args.path, "useStructuredData");
339
+ const value = parseWithSchema(
340
+ args.valueSchema,
341
+ args.value,
342
+ "useStructuredData value"
343
+ );
344
+ ctx.node.emit("structured_data", {
345
+ ...base,
346
+ kind: "set",
347
+ path: args.path,
348
+ value
349
+ });
350
+ return;
351
+ }
352
+ if (args.kind === "append") {
353
+ assertStructuredPath(args.path, "useStructuredData");
354
+ const items = args.items.map(
355
+ (item) => parseWithSchema(args.itemSchema, item, "useStructuredData append item")
356
+ );
357
+ ctx.node.emit("structured_data", {
358
+ ...base,
359
+ kind: "append",
360
+ path: args.path,
361
+ items
362
+ });
363
+ return;
364
+ }
365
+ if (args.kind === "text-delta") {
366
+ assertStructuredPath(args.path, "useStructuredData");
367
+ ctx.node.emit("structured_data", {
368
+ ...base,
369
+ kind: "text-delta",
370
+ path: args.path,
371
+ delta: args.delta
372
+ });
373
+ return;
374
+ }
375
+ const validated = parseWithSchema(
376
+ args.dataSchema,
377
+ args.data,
378
+ "useStructuredData data"
379
+ );
380
+ ctx.node.emit("structured_data", {
381
+ ...base,
382
+ kind: "final",
383
+ data: validated
384
+ });
385
+ };
386
+ var shouldEmitStructured = (cfg) => Boolean(cfg);
387
+ var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
388
+ var import_node_crypto32 = require("crypto");
389
+ var import_node_crypto22 = require("crypto");
390
+ var toError = (value) => value instanceof Error ? value : new Error(String(value));
391
+ var createRuntimeId = () => {
392
+ try {
393
+ return (0, import_node_crypto22.randomUUID)();
394
+ } catch {
395
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
396
+ }
397
+ };
398
+ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
399
+ if (!emitEvent) return;
400
+ emitEvent(event, {
401
+ ...payload,
402
+ ...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
403
+ });
404
+ };
405
+ async function runReasonEngine(args) {
406
+ const stream = args.stream ?? args.model.options?.streaming ?? true;
407
+ const emit = args.emit ?? true;
408
+ const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
409
+ const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
410
+ const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
411
+ const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
412
+ const reasoning = args.reasoning ?? args.model.options?.reasoning;
413
+ const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
414
+ const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
415
+ const tools = args.tools ?? args.model.options?.tools;
416
+ const model = args.model.provider.getModel(args.model.modelId, {
417
+ ...temperature !== void 0 ? { temperature } : {},
418
+ streaming: stream,
419
+ ...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
420
+ ...stopSequences !== void 0 ? { stopSequences } : {},
421
+ ...abortSignal !== void 0 ? { abortSignal } : {},
422
+ ...reasoning !== void 0 ? { reasoning } : {},
423
+ ...responseFormat !== void 0 ? { responseFormat } : {},
424
+ ...tools !== void 0 ? { tools } : {},
425
+ ...providerOptions !== void 0 ? { providerOptions } : {}
426
+ });
427
+ const messages = args.messages !== void 0 ? args.messages : [
428
+ ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
429
+ { role: "user", content: String(args.input ?? "") }
430
+ ];
431
+ const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
432
+ const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
433
+ const commonMeta = {
434
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
435
+ opId,
436
+ segmentId
437
+ };
438
+ const traceSpan = args.reasonTrace?.startSpan({
439
+ name: "runReasonEngine",
440
+ attributes: {
441
+ ...commonMeta,
442
+ providerId: args.model.provider.id,
443
+ modelId: args.model.modelId,
444
+ stream,
445
+ emit,
446
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
447
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
448
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
449
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
450
+ ...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
451
+ },
452
+ telemetry: {
453
+ ...args.telemetry ?? {},
454
+ input: args.telemetry?.input ?? messages
455
+ }
456
+ });
457
+ const modelRequestStartedAt = Date.now();
458
+ let firstTextTokenAt;
459
+ let lastTextTokenAt;
460
+ const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
461
+ ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
462
+ streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
463
+ timeToLastTokenMs: Math.max(
464
+ 0,
465
+ lastTextTokenAt - modelRequestStartedAt
466
+ )
467
+ };
468
+ const endTrace = (span, result) => {
469
+ span?.end?.({
470
+ ...result.usage !== void 0 ? { usage: result.usage } : {},
471
+ ...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
472
+ ...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
473
+ ...result.warnings !== void 0 ? { warnings: result.warnings } : {},
474
+ attributes: {
475
+ textLength: result.text.length,
476
+ ...generationTimingAttributes()
477
+ },
478
+ telemetry: {
479
+ ...args.telemetry ?? {},
480
+ output: args.telemetry?.output ?? result.text
481
+ }
482
+ });
483
+ };
484
+ const failTrace = (span, error) => {
485
+ span?.fail?.(error, {
486
+ attributes: {
487
+ providerId: args.model.provider.id,
488
+ modelId: args.model.modelId
489
+ }
490
+ });
491
+ };
492
+ try {
493
+ if (stream) {
494
+ let final = "";
495
+ let raw;
496
+ let usage;
497
+ let finishReason;
498
+ let toolCalls;
499
+ let providerMetadata;
500
+ let warnings;
501
+ if (emit) {
502
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
503
+ ...commonMeta
504
+ });
505
+ }
506
+ const response2 = await model.stream(messages);
507
+ for await (const chunk of response2) {
508
+ switch (chunk.type) {
509
+ case "text-delta": {
510
+ if (chunk.delta.length === 0) break;
511
+ const tokenAt = Date.now();
512
+ firstTextTokenAt ??= tokenAt;
513
+ lastTextTokenAt = tokenAt;
514
+ final += chunk.delta;
515
+ args.onTextChunk?.(chunk.delta);
516
+ if (chunk.raw !== void 0) {
517
+ raw = chunk.raw;
518
+ }
519
+ if (chunk.providerMetadata !== void 0) {
520
+ providerMetadata = chunk.providerMetadata;
521
+ }
522
+ if (emit) {
523
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
524
+ ...commonMeta,
525
+ delta: chunk.delta
526
+ });
527
+ }
528
+ break;
529
+ }
530
+ case "finish": {
531
+ if (chunk.raw !== void 0) {
532
+ raw = chunk.raw;
533
+ }
534
+ if (chunk.usage !== void 0) {
535
+ usage = chunk.usage;
536
+ }
537
+ if (chunk.finishReason !== void 0) {
538
+ finishReason = chunk.finishReason;
539
+ }
540
+ if (chunk.toolCalls !== void 0) {
541
+ toolCalls = chunk.toolCalls;
542
+ }
543
+ if (chunk.providerMetadata !== void 0) {
544
+ providerMetadata = chunk.providerMetadata;
545
+ }
546
+ if (chunk.warnings !== void 0) {
547
+ warnings = chunk.warnings;
548
+ }
549
+ break;
550
+ }
551
+ case "raw": {
552
+ raw = chunk.raw;
553
+ if (chunk.providerMetadata !== void 0) {
554
+ providerMetadata = chunk.providerMetadata;
555
+ }
556
+ break;
557
+ }
558
+ case "error": {
559
+ if (chunk.raw !== void 0) {
560
+ raw = chunk.raw;
561
+ }
562
+ if (chunk.providerMetadata !== void 0) {
563
+ providerMetadata = chunk.providerMetadata;
564
+ }
565
+ if (chunk.warnings !== void 0) {
566
+ warnings = chunk.warnings;
567
+ }
568
+ throw toError(chunk.error);
569
+ }
570
+ }
571
+ }
572
+ if (emit) {
573
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
574
+ ...commonMeta
575
+ });
576
+ }
577
+ const result2 = {
578
+ text: final,
579
+ ...raw !== void 0 ? { raw } : {},
580
+ ...usage !== void 0 ? { usage } : {},
581
+ ...finishReason !== void 0 ? { finishReason } : {},
582
+ ...toolCalls !== void 0 ? { toolCalls } : {},
583
+ ...providerMetadata !== void 0 ? { providerMetadata } : {},
584
+ ...warnings !== void 0 ? { warnings } : {}
585
+ };
586
+ endTrace(traceSpan, result2);
587
+ return result2;
588
+ }
589
+ const response = await model.invoke(messages);
590
+ if (emit) {
591
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
592
+ ...commonMeta
593
+ });
594
+ if (response.content) {
595
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
596
+ ...commonMeta,
597
+ delta: response.content
598
+ });
599
+ }
600
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
601
+ ...commonMeta
602
+ });
603
+ }
604
+ const result = {
605
+ text: response.content,
606
+ ...response.raw !== void 0 ? { raw: response.raw } : {},
607
+ ...response.usage !== void 0 ? { usage: response.usage } : {},
608
+ ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
609
+ ...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
610
+ ...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
611
+ ...response.warnings !== void 0 ? { warnings: response.warnings } : {}
612
+ };
613
+ endTrace(traceSpan, result);
614
+ return result;
615
+ } catch (error) {
616
+ failTrace(traceSpan, error);
617
+ throw error;
618
+ }
619
+ }
620
+ var createRuntimeId2 = () => {
621
+ try {
622
+ return (0, import_node_crypto32.randomUUID)();
623
+ } catch {
624
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
625
+ }
626
+ };
627
+ async function reasonEngine(args, meta, inputOverride) {
628
+ const ctx = getHookContext();
629
+ return runReasonEngine({
630
+ model: args.model,
631
+ input: inputOverride ?? args.input,
632
+ system: args.system,
633
+ temperature: args.temperature,
634
+ maxOutputTokens: args.maxOutputTokens,
635
+ stopSequences: args.stopSequences,
636
+ abortSignal: args.abortSignal,
637
+ reasoning: args.reasoning,
638
+ responseFormat: args.responseFormat,
639
+ providerOptions: args.providerOptions,
640
+ tools: args.tools,
641
+ messages: args.messages,
642
+ defaultTemperature: ctx.node.config?.model?.temperature,
643
+ stream: args.stream,
644
+ emit: args.emit,
645
+ onTextChunk: args.onTextChunk,
646
+ telemetry: args.telemetry,
647
+ nodeId: ctx.node.graph.node,
648
+ emitEvent: ctx.node.emit,
649
+ reasonTrace: ctx.reasonTrace,
650
+ id: meta.id,
651
+ opId: meta.opId
652
+ });
653
+ }
654
+ var tryJsonParse = (value) => {
655
+ try {
656
+ return { ok: true, value: JSON.parse(value) };
657
+ } catch (error) {
658
+ return {
659
+ ok: false,
660
+ error: error instanceof Error ? error.message : String(error)
661
+ };
662
+ }
663
+ };
664
+ var extractJsonCodeBlock = (value) => {
665
+ const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
666
+ if (!match || typeof match[1] !== "string") return null;
667
+ const inner = match[1].trim();
668
+ return inner.length > 0 ? inner : null;
669
+ };
670
+ var tryStringify = (value) => {
671
+ if (typeof value === "string") return value;
672
+ try {
673
+ return JSON.stringify(value, null, 2);
674
+ } catch {
675
+ return String(value);
676
+ }
677
+ };
678
+ var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
679
+ var resolveOutputCandidate = (text) => {
680
+ const trimmed = text.trim();
681
+ if (!trimmed) return text;
682
+ const direct = tryJsonParse(trimmed);
683
+ if (direct.ok) return direct.value;
684
+ const block = extractJsonCodeBlock(trimmed);
685
+ if (block) {
686
+ const parsedBlock = tryJsonParse(block);
687
+ if (parsedBlock.ok) return parsedBlock.value;
688
+ }
689
+ return text;
690
+ };
691
+ var looksLikeStructuredJsonAttempt = (value) => {
692
+ const trimmed = value.trim();
693
+ if (!trimmed) return false;
694
+ return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
695
+ };
696
+ var isLikelyTruncatedStructuredOutput = (text) => {
697
+ const trimmed = text.trim();
698
+ if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
699
+ const direct = tryJsonParse(trimmed);
700
+ if (!direct.ok && direct.error) return true;
701
+ const block = extractJsonCodeBlock(trimmed);
702
+ if (!block) return false;
703
+ const parsedBlock = tryJsonParse(block);
704
+ return !parsedBlock.ok && Boolean(parsedBlock.error);
705
+ };
706
+ var createTruncatedStructuredOutputError = (label) => new Error(
707
+ `${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
708
+ );
709
+ var createInvalidStructuredOutputError = (label) => new Error(
710
+ `${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.`
711
+ );
712
+ var parseReasonOutputWithSchema = (args) => {
713
+ const candidate = resolveOutputCandidate(args.text);
714
+ try {
715
+ return parseWithSchema(args.schema, candidate, args.label);
716
+ } catch (error) {
717
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
718
+ throw createTruncatedStructuredOutputError(args.label);
719
+ }
720
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
721
+ throw createInvalidStructuredOutputError(args.label);
722
+ }
723
+ throw error;
724
+ }
725
+ };
726
+ var parseInterruptFirstPassResult = (args) => {
727
+ const candidate = resolveOutputCandidate(args.text);
728
+ if (!isRecord22(candidate)) {
729
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
730
+ throw createTruncatedStructuredOutputError("useReason output");
731
+ }
732
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
733
+ throw createInvalidStructuredOutputError("useReason output");
734
+ }
735
+ throw new Error(
736
+ "useReason first pass with interrupt must return a JSON object."
737
+ );
738
+ }
739
+ const mode = args.mode ?? "required";
740
+ let interruptRequired = true;
741
+ if (mode === "optional") {
742
+ if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
743
+ throw new Error(
744
+ 'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
745
+ );
746
+ }
747
+ interruptRequired = candidate.decision === "interrupt";
748
+ if (!interruptRequired) {
749
+ const hasUnexpectedRequest = [
750
+ candidate.interruptRequest,
751
+ candidate.request,
752
+ candidate.interrupt
753
+ ].some((value) => value !== null && value !== void 0);
754
+ if (hasUnexpectedRequest) {
755
+ throw new Error(
756
+ 'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
757
+ );
758
+ }
759
+ }
760
+ }
761
+ let request;
762
+ if (interruptRequired) {
763
+ const requestCandidates = [
764
+ candidate.interruptRequest,
765
+ candidate.request,
766
+ candidate.interrupt,
767
+ mode === "required" ? candidate : void 0
768
+ ];
769
+ const requestPayload = requestCandidates.find(
770
+ (value) => value !== null && value !== void 0
771
+ );
772
+ request = parseWithSchema(
773
+ args.requestSchema,
774
+ requestPayload,
775
+ "useReason interrupt.request"
776
+ );
777
+ }
778
+ let output;
779
+ if (args.outputSchema) {
780
+ const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
781
+ try {
782
+ output = parseWithSchema(
783
+ args.outputSchema,
784
+ outputPayload,
785
+ "useReason output"
786
+ );
787
+ } catch (error) {
788
+ if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
789
+ throw createTruncatedStructuredOutputError("useReason output");
790
+ }
791
+ if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
792
+ throw createInvalidStructuredOutputError("useReason output");
793
+ }
794
+ throw error;
795
+ }
796
+ }
797
+ const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
798
+ if (!args.outputSchema && !explicitDraft) {
799
+ throw new Error(
800
+ "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
801
+ );
802
+ }
803
+ const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
804
+ if (!interruptRequired) {
805
+ return {
806
+ draftText,
807
+ interruptRequired: false,
808
+ ...output !== void 0 ? { output } : {}
809
+ };
810
+ }
811
+ return {
812
+ draftText,
813
+ interruptRequired: true,
814
+ request,
815
+ ...output !== void 0 ? { output } : {}
816
+ };
817
+ };
818
+ var import_zod3 = require("zod");
819
+ var tryStringify2 = (value) => {
820
+ if (typeof value === "string") return value;
821
+ try {
822
+ return JSON.stringify(value, null, 2);
823
+ } catch {
824
+ return String(value);
825
+ }
826
+ };
827
+ var tryGetSchemaHint = (schema) => {
828
+ try {
829
+ const hint = (0, import_zod3.toJSONSchema)(schema);
830
+ return JSON.stringify(hint, null, 2);
831
+ } catch {
832
+ return void 0;
833
+ }
834
+ };
835
+ var withSchemaHint = (input, label, schema) => {
836
+ const hint = tryGetSchemaHint(schema);
837
+ if (!hint) return input;
838
+ return `${input}
839
+
840
+ ${label}:
841
+ ${hint}`;
842
+ };
843
+ var defaultInterruptFirstPassInput = (args) => {
844
+ const mode = args.mode ?? "required";
845
+ const base = mode === "optional" ? args.outputSchema ? [
846
+ args.input,
847
+ "Output rules:",
848
+ "- Return JSON only. No markdown fences.",
849
+ "- Return an object with keys: decision, output, interruptRequest, draftText.",
850
+ '- decision must be either "continue" or "interrupt".',
851
+ '- Use decision "continue" when you can produce the final output without asking the user.',
852
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
853
+ "- output must match the expected output JSON schema.",
854
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
855
+ '- When decision is "continue", interruptRequest must be null.',
856
+ "- draftText should be a concise plain-language summary of output."
857
+ ].join("\n\n") : [
858
+ args.input,
859
+ "Output rules:",
860
+ "- Return JSON only. No markdown fences.",
861
+ "- Return an object with keys: decision, draftText, interruptRequest.",
862
+ '- decision must be either "continue" or "interrupt".',
863
+ '- Use decision "continue" when you can produce the final answer without asking the user.',
864
+ '- Use decision "interrupt" only when user input is needed before finalizing.',
865
+ "- draftText should be the current answer text.",
866
+ '- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
867
+ '- When decision is "continue", interruptRequest must be null.'
868
+ ].join("\n\n") : args.outputSchema ? [
869
+ args.input,
870
+ "Output rules:",
871
+ "- Return JSON only. No markdown fences.",
872
+ "- Return an object with keys: output, interruptRequest, draftText.",
873
+ "- output must match the expected output JSON schema.",
874
+ "- interruptRequest must match the expected interrupt request JSON schema.",
875
+ "- interruptRequest must be a non-null object.",
876
+ "- draftText should be a concise plain-language summary of output.",
877
+ "- If uncertain, still return a valid interruptRequest with safe default options."
878
+ ].join("\n\n") : [
879
+ args.input,
880
+ "Output rules:",
881
+ "- Return JSON only. No markdown fences.",
882
+ "- Return an object with keys: draftText, interruptRequest.",
883
+ "- draftText should be the current draft answer text.",
884
+ "- interruptRequest must match the expected interrupt request JSON schema.",
885
+ "- interruptRequest must be a non-null object.",
886
+ "- If uncertain, still return a valid interruptRequest with safe default options."
887
+ ].join("\n\n");
888
+ const withInterruptSchema = withSchemaHint(
889
+ base,
890
+ "Expected interrupt request JSON schema (for `interruptRequest`)",
891
+ args.requestSchema
892
+ );
893
+ if (!args.outputSchema) return withInterruptSchema;
894
+ const withOutputSchema = withSchemaHint(
895
+ withInterruptSchema,
896
+ "Expected output JSON schema (for `output`)",
897
+ args.outputSchema
898
+ );
899
+ return [
900
+ withOutputSchema,
901
+ "JSON template:",
902
+ mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
903
+ ].join("\n\n");
904
+ };
905
+ var defaultContinuationInput = (args) => [
906
+ "You are continuing a paused reasoning task after user input.",
907
+ "Update the previous result using the user response and return the final answer only.",
908
+ "If the previous output was JSON, return JSON in the same schema.",
909
+ "",
910
+ `Original request:
911
+ ${args.input}`,
912
+ `Draft answer text:
913
+ ${args.draftText}`,
914
+ `Draft output object:
915
+ ${tryStringify2(args.draftOutput ?? args.draftText)}`,
916
+ `Interrupt request shown to user:
917
+ ${tryStringify2(args.request)}`,
918
+ `User response:
919
+ ${tryStringify2(args.response)}`
920
+ ].join("\n\n");
921
+ var withOutputGuardrails = (input, schema) => {
922
+ const base = [
923
+ input,
924
+ "Output rules:",
925
+ "- Return only JSON, no markdown fences.",
926
+ "- Ensure the response conforms to the expected output schema.",
927
+ "- Do not include explanatory text outside the JSON object."
928
+ ].join("\n\n");
929
+ return withSchemaHint(base, "Expected output JSON schema", schema);
930
+ };
931
+ var withStructuredStreamHints = (input, args) => {
932
+ const rules = [];
933
+ for (const path of args.setFieldPaths ?? []) {
934
+ rules.push(
935
+ `- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
936
+ );
937
+ }
938
+ for (const path of args.appendFieldPaths ?? []) {
939
+ rules.push(
940
+ `- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
941
+ );
942
+ rules.push(
943
+ `- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
944
+ );
945
+ }
946
+ for (const path of args.textDeltaFieldPaths ?? []) {
947
+ rules.push(
948
+ `- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
949
+ );
950
+ rules.push(
951
+ `- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
952
+ );
953
+ }
954
+ if (rules.length === 0) return input;
955
+ return [
956
+ input,
957
+ "Streaming rules:",
958
+ ...rules,
959
+ "- Keep the overall response as valid JSON."
960
+ ].join("\n\n");
961
+ };
962
+ var mergeUsage = (left, right) => {
963
+ if (!left) return right;
964
+ if (!right) return left;
965
+ const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
966
+ const raw = left.raw || right.raw ? {
967
+ ...left.raw ?? {},
968
+ ...right.raw ?? {}
969
+ } : void 0;
970
+ const input = sum(left.input, right.input);
971
+ const output = sum(left.output, right.output);
972
+ const total = sum(left.total, right.total);
973
+ const reasoning = sum(left.reasoning, right.reasoning);
974
+ const cacheRead = sum(left.cacheRead, right.cacheRead);
975
+ const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
976
+ const merged = {};
977
+ if (input !== void 0) merged.input = input;
978
+ if (output !== void 0) merged.output = output;
979
+ if (total !== void 0) merged.total = total;
980
+ if (reasoning !== void 0) merged.reasoning = reasoning;
981
+ if (cacheRead !== void 0) merged.cacheRead = cacheRead;
982
+ if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
983
+ if (raw) merged.raw = raw;
984
+ return merged;
985
+ };
986
+ var mergeWarnings = (left, right) => {
987
+ if (!left?.length) return right;
988
+ if (!right?.length) return left;
989
+ const seen = /* @__PURE__ */ new Set();
990
+ const merged = [];
991
+ for (const warning of [...left, ...right]) {
992
+ const key = JSON.stringify(warning);
993
+ if (seen.has(key)) continue;
994
+ seen.add(key);
995
+ merged.push(warning);
996
+ }
997
+ return merged;
998
+ };
999
+ var mergeProviderMetadata = (left, right) => {
1000
+ if (!left) return right;
1001
+ if (!right) return left;
1002
+ return {
1003
+ ...left,
1004
+ ...right
1005
+ };
1006
+ };
1007
+ var emitReasonStructuredOutput = (args) => {
1008
+ if (!args.emit || !shouldEmitStructured(args.structured)) return;
1009
+ emitStructuredData({
1010
+ dataType: args.structured?.dataType ?? "reason-output",
1011
+ data: args.output,
1012
+ kind: "final",
1013
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
1014
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
1015
+ ...args.id ? { id: args.id } : {},
1016
+ streamId: args.opId
1017
+ });
1018
+ };
1019
+ var resolveAppendFieldPaths = (cfg) => {
1020
+ const fields = cfg?.fields;
1021
+ if (!fields) return [];
1022
+ const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
1023
+ for (const path of appendPaths) {
1024
+ assertStructuredPath(path, "useReason structured append");
1025
+ }
1026
+ return appendPaths;
1027
+ };
1028
+ var resolveSetFieldPaths = (cfg) => {
1029
+ const fields = cfg?.fields;
1030
+ if (!fields) return [];
1031
+ const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
1032
+ for (const path of setPaths) {
1033
+ assertStructuredPath(path, "useReason structured set");
1034
+ }
1035
+ return setPaths;
1036
+ };
1037
+ var resolveTextDeltaFieldPaths = (cfg) => {
1038
+ const fields = cfg?.fields;
1039
+ if (!fields) return [];
1040
+ const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
1041
+ for (const path of textPaths) {
1042
+ assertStructuredPath(path, "useReason structured text-delta");
1043
+ }
1044
+ return textPaths;
1045
+ };
1046
+ var extractCompletedStringValueAt = (args) => {
1047
+ let value = "";
1048
+ for (let i = args.start; i < args.text.length; i += 1) {
1049
+ const ch = args.text[i];
1050
+ if (ch === void 0) break;
1051
+ if (ch === '"') {
1052
+ return {
1053
+ complete: true,
1054
+ value
1055
+ };
1056
+ }
1057
+ if (ch !== "\\") {
1058
+ value += ch;
1059
+ continue;
1060
+ }
1061
+ const next = args.text[i + 1];
1062
+ if (!next) {
1063
+ return {
1064
+ complete: false
1065
+ };
1066
+ }
1067
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1068
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1069
+ i += 1;
1070
+ continue;
1071
+ }
1072
+ if (next === "u") {
1073
+ const hex = args.text.slice(i + 2, i + 6);
1074
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1075
+ return {
1076
+ complete: false
1077
+ };
1078
+ }
1079
+ value += String.fromCharCode(Number.parseInt(hex, 16));
1080
+ i += 5;
1081
+ continue;
1082
+ }
1083
+ return {
1084
+ complete: false
1085
+ };
1086
+ }
1087
+ return {
1088
+ complete: false
1089
+ };
1090
+ };
1091
+ var skipWhitespace = (text, start) => {
1092
+ let i = start;
1093
+ while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
1094
+ return i;
1095
+ };
1096
+ var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
1097
+ var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
1098
+ (part, index) => pathSegmentMatches(patternParts[index] ?? "", part)
1099
+ );
1100
+ var isPathPrefix = (prefix, full) => prefix.length <= full.length && prefix.every((part, index) => pathSegmentMatches(full[index] ?? "", part));
1101
+ var parseStringToken = (text, start) => {
1102
+ if (text[start] !== '"') return { complete: false };
1103
+ const parsed = extractCompletedStringValueAt({ text, start: start + 1 });
1104
+ if (!parsed.complete || parsed.value === void 0)
1105
+ return { complete: false };
1106
+ let inEscape = false;
1107
+ for (let i = start + 1; i < text.length; i += 1) {
1108
+ const ch = text[i];
1109
+ if (ch === void 0) break;
1110
+ if (inEscape) {
1111
+ inEscape = false;
1112
+ if (ch === "u") i += 4;
1113
+ continue;
1114
+ }
1115
+ if (ch === "\\") {
1116
+ inEscape = true;
1117
+ continue;
1118
+ }
1119
+ if (ch === '"') {
1120
+ return {
1121
+ complete: true,
1122
+ end: i + 1,
1123
+ value: parsed.value
1124
+ };
1125
+ }
1126
+ }
1127
+ return { complete: false };
1128
+ };
1129
+ var skipNestedValue = (text, start) => {
1130
+ const opener = text[start];
1131
+ const closer = opener === "{" ? "}" : "]";
1132
+ let inString = false;
1133
+ let isEscaped = false;
1134
+ let nesting = 0;
1135
+ for (let i = start; i < text.length; i += 1) {
1136
+ const ch = text[i];
1137
+ if (ch === void 0) break;
1138
+ if (inString) {
1139
+ if (isEscaped) {
1140
+ isEscaped = false;
1141
+ continue;
1142
+ }
1143
+ if (ch === "\\") {
1144
+ isEscaped = true;
1145
+ continue;
1146
+ }
1147
+ if (ch === '"') {
1148
+ inString = false;
1149
+ }
1150
+ continue;
1151
+ }
1152
+ if (ch === '"') {
1153
+ inString = true;
1154
+ continue;
1155
+ }
1156
+ if (ch === opener) {
1157
+ nesting += 1;
1158
+ continue;
1159
+ }
1160
+ if (ch === closer) {
1161
+ nesting -= 1;
1162
+ if (nesting === 0) {
1163
+ return { complete: true, end: i + 1 };
1164
+ }
1165
+ }
1166
+ }
1167
+ return { complete: false };
1168
+ };
1169
+ var skipCompleteValue = (text, start) => {
1170
+ const i = skipWhitespace(text, start);
1171
+ const first = text[i];
1172
+ if (!first) return { complete: false };
1173
+ if (first === '"') {
1174
+ const parsed = parseStringToken(text, i);
1175
+ return parsed.complete && parsed.end !== void 0 ? { complete: true, end: parsed.end } : { complete: false };
1176
+ }
1177
+ if (first === "{" || first === "[") {
1178
+ return skipNestedValue(text, i);
1179
+ }
1180
+ for (let j = i; j < text.length; j += 1) {
1181
+ const ch = text[j];
1182
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1183
+ const raw = text.slice(i, j).trim();
1184
+ if (!raw) return { complete: false };
1185
+ try {
1186
+ JSON.parse(raw);
1187
+ return { complete: true, end: j };
1188
+ } catch {
1189
+ return { complete: false };
1190
+ }
1191
+ }
1192
+ return { complete: false };
1193
+ };
1194
+ var locateValueStart = (args) => {
1195
+ const [first] = locateValueStarts(args);
1196
+ return first ? {
1197
+ found: true,
1198
+ start: first.start
1199
+ } : {
1200
+ found: false
1201
+ };
1202
+ };
1203
+ var locateValueStarts = (args) => {
1204
+ const targetParts = parseStructuredPath(
1205
+ args.path,
1206
+ "useReason structured field"
1207
+ );
1208
+ const matches = [];
1209
+ const visitValue = (start, currentParts) => {
1210
+ const valueStart = skipWhitespace(args.text, start);
1211
+ if (pathMatchesPattern(currentParts, targetParts)) {
1212
+ matches.push({
1213
+ path: currentParts.join("."),
1214
+ start: valueStart
1215
+ });
1216
+ return;
1217
+ }
1218
+ if (!isPathPrefix(currentParts, targetParts)) {
1219
+ return;
1220
+ }
1221
+ const first = args.text[valueStart];
1222
+ if (first === "{") visitObject(valueStart + 1, currentParts);
1223
+ if (first === "[") visitArray(valueStart + 1, currentParts);
1224
+ };
1225
+ const visitObject = (start, currentParts) => {
1226
+ let i = skipWhitespace(args.text, start);
1227
+ while (i < args.text.length) {
1228
+ const ch = args.text[i];
1229
+ if (ch === "}") return;
1230
+ if (ch === ",") {
1231
+ i = skipWhitespace(args.text, i + 1);
1232
+ continue;
1233
+ }
1234
+ if (ch !== '"') return;
1235
+ const key = parseStringToken(args.text, i);
1236
+ if (!key.complete || key.end === void 0 || key.value === void 0) {
1237
+ return;
1238
+ }
1239
+ i = skipWhitespace(args.text, key.end);
1240
+ if (args.text[i] !== ":") return;
1241
+ const valueStart = skipWhitespace(args.text, i + 1);
1242
+ const childParts = [...currentParts, key.value];
1243
+ if (pathMatchesPattern(childParts, targetParts)) {
1244
+ matches.push({
1245
+ path: childParts.join("."),
1246
+ start: valueStart
1247
+ });
1248
+ } else if (isPathPrefix(childParts, targetParts)) {
1249
+ visitValue(valueStart, childParts);
1250
+ }
1251
+ const skipped = skipCompleteValue(args.text, valueStart);
1252
+ if (!skipped.complete || skipped.end === void 0) {
1253
+ return;
1254
+ }
1255
+ i = skipWhitespace(args.text, skipped.end);
1256
+ if (args.text[i] === ",") {
1257
+ i = skipWhitespace(args.text, i + 1);
1258
+ continue;
1259
+ }
1260
+ if (args.text[i] === "}") return;
1261
+ return;
1262
+ }
1263
+ };
1264
+ const visitArray = (start, currentParts) => {
1265
+ let i = skipWhitespace(args.text, start);
1266
+ let index = 0;
1267
+ while (i < args.text.length) {
1268
+ if (args.text[i] === "]") return;
1269
+ const childParts = [...currentParts, String(index)];
1270
+ if (pathMatchesPattern(childParts, targetParts)) {
1271
+ matches.push({
1272
+ path: childParts.join("."),
1273
+ start: i
1274
+ });
1275
+ } else if (isPathPrefix(childParts, targetParts)) {
1276
+ visitValue(i, childParts);
1277
+ }
1278
+ const skipped = skipCompleteValue(args.text, i);
1279
+ if (!skipped.complete || skipped.end === void 0) {
1280
+ return;
1281
+ }
1282
+ i = skipWhitespace(args.text, skipped.end);
1283
+ if (args.text[i] === ",") {
1284
+ index += 1;
1285
+ i = skipWhitespace(args.text, i + 1);
1286
+ continue;
1287
+ }
1288
+ if (args.text[i] === "]") return;
1289
+ return;
1290
+ }
1291
+ };
1292
+ visitValue(0, []);
1293
+ return matches;
1294
+ };
1295
+ var extractCompletedValueAt = (args) => {
1296
+ const start = skipWhitespace(args.text, args.start);
1297
+ const first = args.text[start];
1298
+ if (!first) return { complete: false };
1299
+ if (first === '"') {
1300
+ const parsed = extractCompletedStringValueAt({
1301
+ text: args.text,
1302
+ start: start + 1
1303
+ });
1304
+ return parsed.complete ? {
1305
+ complete: true,
1306
+ value: parsed.value
1307
+ } : {
1308
+ complete: false
1309
+ };
1310
+ }
1311
+ if (first === "{") {
1312
+ return extractCompletedNestedValueAt({
1313
+ text: args.text,
1314
+ start,
1315
+ opener: "{",
1316
+ closer: "}"
1317
+ });
1318
+ }
1319
+ if (first === "[") {
1320
+ return extractCompletedNestedValueAt({
1321
+ text: args.text,
1322
+ start,
1323
+ opener: "[",
1324
+ closer: "]"
1325
+ });
1326
+ }
1327
+ for (let i = start; i < args.text.length; i += 1) {
1328
+ const ch = args.text[i];
1329
+ if (ch === void 0) break;
1330
+ if (ch !== "," && ch !== "}" && ch !== "]") continue;
1331
+ const raw = args.text.slice(start, i).trim();
1332
+ if (!raw) return { complete: false };
1333
+ try {
1334
+ return {
1335
+ complete: true,
1336
+ value: JSON.parse(raw)
1337
+ };
1338
+ } catch {
1339
+ return { complete: false };
1340
+ }
1341
+ }
1342
+ return { complete: false };
1343
+ };
1344
+ var extractCompletedNestedValueAt = (args) => {
1345
+ let inString = false;
1346
+ let isEscaped = false;
1347
+ let nesting = 0;
1348
+ for (let i = args.start; i < args.text.length; i += 1) {
1349
+ const ch = args.text[i];
1350
+ if (ch === void 0) break;
1351
+ if (inString) {
1352
+ if (isEscaped) {
1353
+ isEscaped = false;
1354
+ continue;
1355
+ }
1356
+ if (ch === "\\") {
1357
+ isEscaped = true;
1358
+ continue;
1359
+ }
1360
+ if (ch === '"') {
1361
+ inString = false;
1362
+ }
1363
+ continue;
1364
+ }
1365
+ if (ch === '"') {
1366
+ inString = true;
1367
+ continue;
1368
+ }
1369
+ if (ch === args.opener) {
1370
+ nesting += 1;
1371
+ continue;
1372
+ }
1373
+ if (ch === args.closer) {
1374
+ nesting -= 1;
1375
+ if (nesting === 0) {
1376
+ const raw = args.text.slice(args.start, i + 1);
1377
+ try {
1378
+ return {
1379
+ complete: true,
1380
+ value: JSON.parse(raw)
1381
+ };
1382
+ } catch {
1383
+ return {
1384
+ complete: false
1385
+ };
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+ return {
1391
+ complete: false
1392
+ };
1393
+ };
1394
+ var extractCompletedFieldValues = (args) => {
1395
+ const located = locateValueStarts(args);
1396
+ const values = [];
1397
+ for (const match of located) {
1398
+ const parsed = extractCompletedValueAt({
1399
+ text: args.text,
1400
+ start: match.start
1401
+ });
1402
+ if (!parsed.complete) continue;
1403
+ values.push({
1404
+ path: match.path,
1405
+ value: parsed.value
1406
+ });
1407
+ }
1408
+ return values;
1409
+ };
1410
+ var extractCompletedArrayItems = (args) => {
1411
+ const located = locateValueStart(args);
1412
+ if (!located.found || located.start === void 0) return [];
1413
+ const arrayStart = skipWhitespace(args.text, located.start);
1414
+ if (args.text[arrayStart] !== "[") return [];
1415
+ const start = arrayStart + 1;
1416
+ const items = [];
1417
+ let inString = false;
1418
+ let isEscaped = false;
1419
+ let nesting = 0;
1420
+ let itemStart = -1;
1421
+ for (let i = start; i < args.text.length; i += 1) {
1422
+ const ch = args.text[i];
1423
+ if (ch === void 0) break;
1424
+ if (inString) {
1425
+ if (isEscaped) {
1426
+ isEscaped = false;
1427
+ continue;
1428
+ }
1429
+ if (ch === "\\") {
1430
+ isEscaped = true;
1431
+ continue;
1432
+ }
1433
+ if (ch === '"') {
1434
+ inString = false;
1435
+ }
1436
+ continue;
1437
+ }
1438
+ if (ch === '"') {
1439
+ inString = true;
1440
+ if (itemStart < 0) itemStart = i;
1441
+ continue;
1442
+ }
1443
+ if (itemStart < 0) {
1444
+ if (/\s/.test(ch) || ch === ",") continue;
1445
+ if (ch === "]") break;
1446
+ itemStart = i;
1447
+ if (ch === "{" || ch === "[") {
1448
+ nesting = 1;
1449
+ } else {
1450
+ nesting = 0;
1451
+ }
1452
+ continue;
1453
+ }
1454
+ if (ch === "{" || ch === "[") {
1455
+ nesting += 1;
1456
+ continue;
1457
+ }
1458
+ if (ch === "}" || ch === "]") {
1459
+ if (nesting > 0) {
1460
+ nesting -= 1;
1461
+ continue;
1462
+ }
1463
+ }
1464
+ if (nesting === 0 && (ch === "," || ch === "]")) {
1465
+ const raw = args.text.slice(itemStart, i).trim();
1466
+ if (raw) {
1467
+ try {
1468
+ items.push(JSON.parse(raw));
1469
+ } catch {
1470
+ }
1471
+ }
1472
+ itemStart = -1;
1473
+ if (ch === "]") break;
1474
+ }
1475
+ }
1476
+ return items;
1477
+ };
1478
+ var extractCompletedArrayItemGroups = (args) => {
1479
+ return locateValueStarts(args).flatMap((match) => {
1480
+ const items = extractCompletedArrayItems({
1481
+ text: args.text,
1482
+ path: match.path
1483
+ });
1484
+ return items.length > 0 ? [
1485
+ {
1486
+ path: match.path,
1487
+ items
1488
+ }
1489
+ ] : [];
1490
+ });
1491
+ };
1492
+ var extractStreamingStringValue = (args) => {
1493
+ const located = locateValueStart(args);
1494
+ if (!located.found || located.start === void 0) {
1495
+ return {
1496
+ found: located.found,
1497
+ complete: false,
1498
+ value: ""
1499
+ };
1500
+ }
1501
+ const stringStart = skipWhitespace(args.text, located.start);
1502
+ if (args.text[stringStart] !== '"') {
1503
+ return {
1504
+ found: true,
1505
+ complete: false,
1506
+ value: ""
1507
+ };
1508
+ }
1509
+ let value = "";
1510
+ for (let i = stringStart + 1; i < args.text.length; i += 1) {
1511
+ const ch = args.text[i];
1512
+ if (ch === void 0) break;
1513
+ if (ch === '"') {
1514
+ return {
1515
+ found: true,
1516
+ complete: true,
1517
+ value
1518
+ };
1519
+ }
1520
+ if (ch !== "\\") {
1521
+ value += ch;
1522
+ continue;
1523
+ }
1524
+ const next = args.text[i + 1];
1525
+ if (!next) {
1526
+ return {
1527
+ found: true,
1528
+ complete: false,
1529
+ value
1530
+ };
1531
+ }
1532
+ if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
1533
+ value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
1534
+ i += 1;
1535
+ continue;
1536
+ }
1537
+ if (next === "u") {
1538
+ const hex = args.text.slice(i + 2, i + 6);
1539
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1540
+ return {
1541
+ found: true,
1542
+ complete: false,
1543
+ value
1544
+ };
1545
+ }
1546
+ value += String.fromCharCode(Number.parseInt(hex, 16));
1547
+ i += 5;
1548
+ continue;
1549
+ }
1550
+ return {
1551
+ found: true,
1552
+ complete: false,
1553
+ value
1554
+ };
1555
+ }
1556
+ return {
1557
+ found: true,
1558
+ complete: false,
1559
+ value
1560
+ };
1561
+ };
1562
+ var extractStreamingStringValues = (args) => {
1563
+ return locateValueStarts(args).flatMap((match) => {
1564
+ const current = extractStreamingStringValue({
1565
+ text: args.text,
1566
+ path: match.path
1567
+ });
1568
+ return current.found ? [
1569
+ {
1570
+ path: match.path,
1571
+ complete: current.complete,
1572
+ value: current.value
1573
+ }
1574
+ ] : [];
1575
+ });
1576
+ };
1577
+ var toErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
1578
+ var toToolDefinitions = (tools) => tools.map((tool) => ({
1579
+ name: tool.name,
1580
+ ...tool.title ? { title: tool.title } : {},
1581
+ ...tool.description ? { description: tool.description } : {},
1582
+ inputSchema: tool.inputSchema,
1583
+ ...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
1584
+ ...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
1585
+ ...tool.metadata !== void 0 ? { metadata: tool.metadata } : {}
1586
+ }));
1587
+ var resolveToolExecutionBoolean = (value, toolName, fallback) => {
1588
+ if (typeof value === "boolean") return value;
1589
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1590
+ return value[toolName] ?? fallback;
1591
+ }
1592
+ return fallback;
1593
+ };
1594
+ var normalizeToolResult = (toolCall, value) => {
1595
+ if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
1596
+ return value;
1597
+ }
1598
+ let content;
1599
+ try {
1600
+ content = typeof value === "string" ? value : JSON.stringify(value);
1601
+ } catch {
1602
+ content = String(value);
1603
+ }
1604
+ return {
1605
+ toolCallId: toolCall.id,
1606
+ name: toolCall.name,
1607
+ content,
1608
+ ...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
1609
+ };
1610
+ };
1611
+ var closeOwnedTools = async (tools) => {
1612
+ const closeFns = /* @__PURE__ */ new Set();
1613
+ for (const tool of tools) {
1614
+ if (tool.closeAfterUse === false || !tool.close) continue;
1615
+ closeFns.add(tool.close);
1616
+ }
1617
+ await Promise.all(
1618
+ [...closeFns].map(async (close) => {
1619
+ await close();
1620
+ })
1621
+ );
1622
+ };
1623
+ var createInitialMessages = (args) => [
1624
+ ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
1625
+ { role: "user", content: String(args.input ?? "") }
1626
+ ];
1627
+ var runReasonToolLoop = async (args) => {
1628
+ const { useReasonArgs, id, opId, traceSpan } = args;
1629
+ const ctx = getHookContext();
1630
+ const tools = useReasonArgs.tools ?? [];
1631
+ const toolByName = /* @__PURE__ */ new Map();
1632
+ let validationCompleted = false;
1633
+ try {
1634
+ for (const tool of tools) {
1635
+ if (toolByName.has(tool.name)) {
1636
+ throw new Error(
1637
+ `useReason received duplicate tool name "${tool.name}".`
1638
+ );
1639
+ }
1640
+ toolByName.set(tool.name, tool);
1641
+ }
1642
+ if (useReasonArgs.interrupt) {
1643
+ throw new Error(
1644
+ "useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
1645
+ );
1646
+ }
1647
+ validationCompleted = true;
1648
+ } finally {
1649
+ if (!validationCompleted) {
1650
+ await closeOwnedTools(tools);
1651
+ }
1652
+ }
1653
+ const toolDefinitions = toToolDefinitions(tools);
1654
+ const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
1655
+ const messages = createInitialMessages({
1656
+ system: useReasonArgs.system,
1657
+ input: useReasonArgs.input
1658
+ });
1659
+ const steps = [];
1660
+ const allToolCalls = [];
1661
+ const allToolResults = [];
1662
+ let finalText = "";
1663
+ let finalRaw;
1664
+ let finalOutput;
1665
+ let aggregatedUsage;
1666
+ let finalFinishReason;
1667
+ let aggregatedProviderMetadata;
1668
+ let aggregatedWarnings;
1669
+ const emitToolEvent = (event, payload) => {
1670
+ ctx.node.emit(event, {
1671
+ ...payload,
1672
+ node: ctx.node.graph.node,
1673
+ ...id ? { id } : {},
1674
+ opId
1675
+ });
1676
+ };
1677
+ try {
1678
+ for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
1679
+ traceSpan?.addEvent?.("useReason.tool-step.start", {
1680
+ stepIndex,
1681
+ toolCount: toolDefinitions.length
1682
+ });
1683
+ const step = await reasonEngine(
1684
+ {
1685
+ ...useReasonArgs,
1686
+ tools: toolDefinitions,
1687
+ messages,
1688
+ emit: false,
1689
+ stream: false
1690
+ },
1691
+ { ...id ? { id } : {}, opId }
1692
+ );
1693
+ finalText = step.text;
1694
+ finalRaw = step.raw;
1695
+ aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
1696
+ finalFinishReason = step.finishReason;
1697
+ aggregatedProviderMetadata = mergeProviderMetadata(
1698
+ aggregatedProviderMetadata,
1699
+ step.providerMetadata
1700
+ );
1701
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
1702
+ const toolCalls = step.toolCalls ?? [];
1703
+ const toolResults = [];
1704
+ steps.push({
1705
+ stepIndex,
1706
+ text: step.text,
1707
+ toolCalls,
1708
+ toolResults,
1709
+ ...step.usage ? { usage: step.usage } : {},
1710
+ ...step.finishReason ? { finishReason: step.finishReason } : {},
1711
+ ...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
1712
+ ...step.warnings ? { warnings: step.warnings } : {}
1713
+ });
1714
+ if (toolCalls.length === 0) {
1715
+ if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
1716
+ emitToolEvent("text-start", {});
1717
+ emitToolEvent("text-delta", { delta: step.text });
1718
+ emitToolEvent("text-end", {});
1719
+ }
1720
+ break;
1721
+ }
1722
+ allToolCalls.push(...toolCalls);
1723
+ messages.push({
1724
+ role: "assistant",
1725
+ content: step.text,
1726
+ toolCalls
1727
+ });
1728
+ for (const toolCall of toolCalls) {
1729
+ const tool = toolByName.get(toolCall.name);
1730
+ if (!tool) {
1731
+ throw new Error(
1732
+ `Model requested unknown tool "${toolCall.name}" from useReason.`
1733
+ );
1734
+ }
1735
+ const shouldEmitTool = resolveToolExecutionBoolean(
1736
+ useReasonArgs.toolExecution?.emit,
1737
+ tool.name,
1738
+ false
1739
+ );
1740
+ const shouldApproveTool = resolveToolExecutionBoolean(
1741
+ useReasonArgs.toolExecution?.approval,
1742
+ tool.name,
1743
+ false
1744
+ );
1745
+ traceSpan?.addEvent?.("useReason.tool-call.start", {
1746
+ stepIndex,
1747
+ tool: tool.name,
1748
+ toolCallId: toolCall.id
1749
+ });
1750
+ if (shouldEmitTool) {
1751
+ emitToolEvent("tool-call-start", {
1752
+ tool: tool.name,
1753
+ toolCallId: toolCall.id,
1754
+ input: toolCall.input
1755
+ });
1756
+ }
1757
+ if (shouldApproveTool) {
1758
+ const approval = await awaitInterruptInternal({
1759
+ id: `tool:${toolCall.id}`,
1760
+ request: {
1761
+ kind: "choice",
1762
+ question: `Approve ${tool.name}?`,
1763
+ options: [
1764
+ { id: "approve", label: "Approve" },
1765
+ { id: "deny", label: "Deny" }
1766
+ ],
1767
+ meta: {
1768
+ tool: tool.name,
1769
+ toolCallId: toolCall.id,
1770
+ input: toolCall.input
1771
+ }
1772
+ }
1773
+ });
1774
+ const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
1775
+ if (!approved) {
1776
+ const denied = {
1777
+ toolCallId: toolCall.id,
1778
+ name: tool.name,
1779
+ content: "Tool call denied by user.",
1780
+ isError: true
1781
+ };
1782
+ toolResults.push(denied);
1783
+ allToolResults.push(denied);
1784
+ if (shouldEmitTool) {
1785
+ emitToolEvent("tool-call-result", {
1786
+ tool: tool.name,
1787
+ toolCallId: toolCall.id,
1788
+ content: denied.content,
1789
+ isError: true
1790
+ });
1791
+ }
1792
+ continue;
1793
+ }
1794
+ }
1795
+ try {
1796
+ const rawResult = await tool.execute(toolCall.input, {
1797
+ toolCallId: toolCall.id,
1798
+ ...useReasonArgs.abortSignal ? { abortSignal: useReasonArgs.abortSignal } : {}
1799
+ });
1800
+ const result2 = normalizeToolResult(toolCall, rawResult);
1801
+ toolResults.push(result2);
1802
+ allToolResults.push(result2);
1803
+ if (shouldEmitTool) {
1804
+ emitToolEvent("tool-call-result", {
1805
+ tool: result2.name,
1806
+ toolCallId: result2.toolCallId,
1807
+ content: result2.content,
1808
+ ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
1809
+ ...result2.isError !== void 0 ? { isError: result2.isError } : {}
1810
+ });
1811
+ }
1812
+ traceSpan?.addEvent?.("useReason.tool-call.complete", {
1813
+ stepIndex,
1814
+ tool: result2.name,
1815
+ toolCallId: result2.toolCallId,
1816
+ isError: Boolean(result2.isError)
1817
+ });
1818
+ } catch (error) {
1819
+ const result2 = {
1820
+ toolCallId: toolCall.id,
1821
+ name: tool.name,
1822
+ content: toErrorMessage2(error),
1823
+ isError: true
1824
+ };
1825
+ toolResults.push(result2);
1826
+ allToolResults.push(result2);
1827
+ if (shouldEmitTool) {
1828
+ emitToolEvent("tool-call-error", {
1829
+ tool: tool.name,
1830
+ toolCallId: toolCall.id,
1831
+ message: result2.content
1832
+ });
1833
+ }
1834
+ traceSpan?.addEvent?.("useReason.tool-call.error", {
1835
+ stepIndex,
1836
+ tool: tool.name,
1837
+ toolCallId: toolCall.id,
1838
+ message: result2.content
1839
+ });
1840
+ }
1841
+ }
1842
+ const currentStep = steps[steps.length - 1];
1843
+ if (currentStep) {
1844
+ steps[steps.length - 1] = {
1845
+ ...currentStep,
1846
+ toolResults
1847
+ };
1848
+ }
1849
+ for (const result2 of toolResults) {
1850
+ messages.push({
1851
+ role: "tool",
1852
+ content: result2.content,
1853
+ toolCallId: result2.toolCallId,
1854
+ name: result2.name,
1855
+ ...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
1856
+ ...result2.isError !== void 0 ? { isError: result2.isError } : {},
1857
+ ...result2.raw !== void 0 ? { raw: result2.raw } : {}
1858
+ });
1859
+ }
1860
+ }
1861
+ if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
1862
+ throw new Error(
1863
+ `useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
1864
+ );
1865
+ }
1866
+ if (useReasonArgs.outputSchema) {
1867
+ finalOutput = parseReasonOutputWithSchema({
1868
+ text: finalText,
1869
+ schema: useReasonArgs.outputSchema,
1870
+ ...finalFinishReason ? { finishReason: finalFinishReason } : {},
1871
+ label: "useReason output"
1872
+ });
1873
+ }
1874
+ if (finalOutput !== void 0) {
1875
+ emitReasonStructuredOutput({
1876
+ ...id ? { id } : {},
1877
+ opId,
1878
+ output: finalOutput,
1879
+ structured: useReasonArgs.structured,
1880
+ emit: useReasonArgs.emit ?? true
1881
+ });
1882
+ }
1883
+ const result = {
1884
+ ...id ? { id } : {},
1885
+ opId,
1886
+ text: finalText,
1887
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
1888
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
1889
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
1890
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
1891
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
1892
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
1893
+ toolCalls: allToolCalls,
1894
+ toolResults: allToolResults,
1895
+ steps
1896
+ };
1897
+ traceSpan?.end?.({
1898
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
1899
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
1900
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
1901
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
1902
+ attributes: {
1903
+ textLength: finalText.length,
1904
+ toolCallCount: allToolCalls.length,
1905
+ toolResultCount: allToolResults.length,
1906
+ toolStepCount: steps.length
1907
+ },
1908
+ telemetry: {
1909
+ ...useReasonArgs.telemetry ?? {},
1910
+ output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
1911
+ }
1912
+ });
1913
+ return result;
1914
+ } catch (error) {
1915
+ traceSpan?.fail?.(error, {
1916
+ attributes: {
1917
+ toolStepCount: steps.length,
1918
+ toolCallCount: allToolCalls.length
1919
+ }
1920
+ });
1921
+ throw error;
1922
+ } finally {
1923
+ await closeOwnedTools(tools);
1924
+ }
1925
+ };
1926
+ var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
1927
+ var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
1928
+ var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
1929
+ var assertReasoningThoughtsCompatibility = (args) => {
1930
+ if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
1931
+ const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
1932
+ if (!usesStructuredOutput) return;
1933
+ throw new Error(
1934
+ "useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
1935
+ );
1936
+ };
1937
+ async function useReason(args) {
1938
+ assertReasoningThoughtsCompatibility(args);
1939
+ const ctx = getHookContext();
1940
+ const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
1941
+ const opId = createRuntimeId2();
1942
+ const checkpointKey = resolveReasonCheckpointKey({
1943
+ ...id ? { id } : {},
1944
+ autoIndex: ctx.reasonCallIndex++
1945
+ });
1946
+ const existingCompleted = readReasonCompletedCheckpoint(
1947
+ ctx.currentNodeState.byKey[checkpointKey]
1948
+ );
1949
+ if (existingCompleted) {
1950
+ return existingCompleted.result;
1951
+ }
1952
+ const existingCheckpoint = readReasonCheckpoint(
1953
+ ctx.currentNodeState.byKey[checkpointKey]
1954
+ );
1955
+ const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
1956
+ const traceSpan = getReasonTraceAdapter()?.startSpan({
1957
+ name: "useReason",
1958
+ attributes: {
1959
+ ...id ? { id } : {},
1960
+ opId,
1961
+ nodeId: ctx.node.graph.node,
1962
+ providerId: args.model.provider.id,
1963
+ modelId: args.model.modelId,
1964
+ stream: args.stream ?? args.model.options?.streaming ?? true,
1965
+ emit: args.emit ?? true,
1966
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
1967
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
1968
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
1969
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
1970
+ hasOutputSchema: Boolean(args.outputSchema),
1971
+ hasInterrupt: Boolean(args.interrupt),
1972
+ hasStructured: Boolean(args.structured),
1973
+ hasTools: Boolean(args.tools?.length)
1974
+ },
1975
+ telemetry: {
1976
+ ...args.telemetry ?? {},
1977
+ input: args.telemetry?.input ?? args.input
1978
+ }
1979
+ });
1980
+ if (args.tools?.length) {
1981
+ return runReasonToolLoop({
1982
+ useReasonArgs: args,
1983
+ ...id ? { id } : {},
1984
+ opId,
1985
+ ...traceSpan ? { traceSpan } : {}
1986
+ });
1987
+ }
1988
+ const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
1989
+ const setFieldPaths = resolveSetFieldPaths(args.structured);
1990
+ const appendFieldPaths = resolveAppendFieldPaths(args.structured);
1991
+ const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
1992
+ const useStructuredIncrementalStreaming = Boolean(
1993
+ args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
1994
+ );
1995
+ let firstText = "";
1996
+ let firstRaw;
1997
+ let firstOutput;
1998
+ let firstInterruptRequest;
1999
+ let firstInterruptRequired = false;
2000
+ let finalText = "";
2001
+ let finalRaw;
2002
+ let finalOutput;
2003
+ let aggregatedUsage;
2004
+ let finalFinishReason;
2005
+ let aggregatedProviderMetadata;
2006
+ let aggregatedWarnings;
2007
+ const emittedSetValues = /* @__PURE__ */ new Map();
2008
+ const emittedAppendCounts = /* @__PURE__ */ new Map();
2009
+ const emittedTextValues = /* @__PURE__ */ new Map();
2010
+ if (existingCheckpoint) {
2011
+ firstText = existingCheckpoint.firstText;
2012
+ firstRaw = existingCheckpoint.firstRaw;
2013
+ firstOutput = existingCheckpoint.firstOutput;
2014
+ finalText = firstText;
2015
+ finalRaw = firstRaw;
2016
+ finalOutput = firstOutput;
2017
+ aggregatedUsage = existingCheckpoint.firstUsage;
2018
+ finalFinishReason = existingCheckpoint.firstFinishReason;
2019
+ aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
2020
+ aggregatedWarnings = existingCheckpoint.firstWarnings;
2021
+ traceSpan?.addEvent?.("useReason.resume", {
2022
+ checkpointKey
2023
+ });
2024
+ } else {
2025
+ const first = await reasonEngine(
2026
+ {
2027
+ ...args,
2028
+ emit: suppressTextStream ? false : args.emit,
2029
+ stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
2030
+ ...useStructuredIncrementalStreaming ? {
2031
+ onTextChunk: (delta) => {
2032
+ firstText += delta;
2033
+ finalText = firstText;
2034
+ for (const fieldPath of setFieldPaths) {
2035
+ const values = extractCompletedFieldValues({
2036
+ text: firstText,
2037
+ path: fieldPath
2038
+ });
2039
+ for (const { path, value } of values) {
2040
+ const nextSerialized = JSON.stringify(value);
2041
+ if (emittedSetValues.get(path) === nextSerialized) continue;
2042
+ emittedSetValues.set(path, nextSerialized);
2043
+ emitStructuredData({
2044
+ kind: "set",
2045
+ path,
2046
+ value,
2047
+ dataType: args.structured?.dataType ?? "reason-output",
2048
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2049
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2050
+ ...id ? { id } : {},
2051
+ streamId: opId
2052
+ });
2053
+ }
2054
+ }
2055
+ for (const fieldPath of appendFieldPaths) {
2056
+ const groups = extractCompletedArrayItemGroups({
2057
+ text: firstText,
2058
+ path: fieldPath
2059
+ });
2060
+ for (const { path, items } of groups) {
2061
+ const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
2062
+ if (items.length <= emittedAppendCount) continue;
2063
+ const nextItems = items.slice(emittedAppendCount);
2064
+ emittedAppendCounts.set(path, items.length);
2065
+ emitStructuredData({
2066
+ kind: "append",
2067
+ path,
2068
+ items: nextItems,
2069
+ dataType: args.structured?.dataType ?? "reason-output",
2070
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2071
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2072
+ ...id ? { id } : {},
2073
+ streamId: opId
2074
+ });
2075
+ }
2076
+ }
2077
+ for (const fieldPath of textDeltaFieldPaths) {
2078
+ const values = extractStreamingStringValues({
2079
+ text: firstText,
2080
+ path: fieldPath
2081
+ });
2082
+ for (const { path, value } of values) {
2083
+ const emittedTextValue = emittedTextValues.get(path) ?? "";
2084
+ if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
2085
+ continue;
2086
+ }
2087
+ const nextDelta = value.slice(emittedTextValue.length);
2088
+ emittedTextValues.set(path, value);
2089
+ if (nextDelta.length === 0) continue;
2090
+ emitStructuredData({
2091
+ kind: "text-delta",
2092
+ path,
2093
+ delta: nextDelta,
2094
+ dataType: args.structured?.dataType ?? "reason-output",
2095
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
2096
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
2097
+ ...id ? { id } : {},
2098
+ streamId: opId
2099
+ });
2100
+ }
2101
+ }
2102
+ }
2103
+ } : {}
2104
+ },
2105
+ reasonMeta,
2106
+ args.interrupt ? defaultInterruptFirstPassInput({
2107
+ input: args.input,
2108
+ requestSchema: args.interrupt.requestSchema,
2109
+ mode: args.interrupt.mode ?? "required",
2110
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
2111
+ }) : args.outputSchema ? withOutputGuardrails(
2112
+ withStructuredStreamHints(args.input, {
2113
+ ...setFieldPaths.length > 0 ? { setFieldPaths } : {},
2114
+ ...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
2115
+ ...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
2116
+ }),
2117
+ args.outputSchema
2118
+ ) : void 0
2119
+ );
2120
+ firstRaw = first.raw;
2121
+ finalRaw = first.raw;
2122
+ accumulateTokenUsage(first.usage);
2123
+ aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
2124
+ finalFinishReason = first.finishReason;
2125
+ aggregatedProviderMetadata = mergeProviderMetadata(
2126
+ aggregatedProviderMetadata,
2127
+ first.providerMetadata
2128
+ );
2129
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
2130
+ traceSpan?.addEvent?.("useReason.first-pass.complete", {
2131
+ textLength: first.text.length,
2132
+ hasInterrupt: Boolean(args.interrupt)
2133
+ });
2134
+ if (args.interrupt) {
2135
+ const firstPass = parseInterruptFirstPassResult({
2136
+ text: first.text,
2137
+ requestSchema: args.interrupt.requestSchema,
2138
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
2139
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {},
2140
+ mode: args.interrupt.mode ?? "required"
2141
+ });
2142
+ firstText = firstPass.draftText;
2143
+ finalText = firstPass.draftText;
2144
+ firstInterruptRequired = firstPass.interruptRequired;
2145
+ firstInterruptRequest = firstPass.request;
2146
+ firstOutput = firstPass.output;
2147
+ finalOutput = firstPass.output;
2148
+ } else {
2149
+ firstText = first.text;
2150
+ finalText = first.text;
2151
+ if (args.outputSchema) {
2152
+ firstOutput = parseReasonOutputWithSchema({
2153
+ text: first.text,
2154
+ schema: args.outputSchema,
2155
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
2156
+ label: "useReason output"
2157
+ });
2158
+ }
2159
+ finalOutput = firstOutput;
2160
+ }
2161
+ }
2162
+ let interruptResponse;
2163
+ if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
2164
+ const requestSchema = args.interrupt.requestSchema;
2165
+ const interruptRequest = existingCheckpoint ? parseWithSchema(
2166
+ requestSchema,
2167
+ existingCheckpoint.request,
2168
+ "useReason interrupt.request"
2169
+ ) : firstInterruptRequest;
2170
+ if (!interruptRequest) {
2171
+ throw new Error(
2172
+ "useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
2173
+ );
2174
+ }
2175
+ if (!existingCheckpoint) {
2176
+ ctx.currentNodeState.byKey[checkpointKey] = {
2177
+ status: "awaiting_interrupt",
2178
+ request: interruptRequest,
2179
+ firstText,
2180
+ ...firstRaw !== void 0 ? { firstRaw } : {},
2181
+ ...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
2182
+ ...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
2183
+ ...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
2184
+ ...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
2185
+ ...firstOutput !== void 0 ? { firstOutput } : {}
2186
+ };
2187
+ ctx.stateDirty = true;
2188
+ }
2189
+ const resumeStatePatch = resolveHookStatePatch({
2190
+ nodeId: ctx.node.graph.node,
2191
+ currentNodeState: ctx.currentNodeState,
2192
+ workflowState: ctx.workflowState
2193
+ });
2194
+ interruptResponse = await awaitInterruptInternal({
2195
+ request: interruptRequest,
2196
+ requestSchema,
2197
+ responseSchema: args.interrupt.responseSchema,
2198
+ ...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
2199
+ ...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
2200
+ ...id ? { id } : {},
2201
+ meta: {
2202
+ __kortyxResumeStatePatch: resumeStatePatch
2203
+ }
2204
+ });
2205
+ traceSpan?.addEvent?.("useReason.interrupt.resolved", {
2206
+ checkpointKey
2207
+ });
2208
+ if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
2209
+ delete ctx.currentNodeState.byKey[checkpointKey];
2210
+ ctx.stateDirty = true;
2211
+ }
2212
+ const continuationInput = defaultContinuationInput({
2213
+ input: args.input,
2214
+ draftText: firstText,
2215
+ ...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
2216
+ request: interruptRequest,
2217
+ response: interruptResponse
2218
+ });
2219
+ const second = await reasonEngine(
2220
+ {
2221
+ ...args,
2222
+ emit: suppressTextStream ? false : args.emit,
2223
+ stream: suppressTextStream ? false : args.stream
2224
+ },
2225
+ reasonMeta,
2226
+ args.outputSchema ? withOutputGuardrails(
2227
+ continuationInput,
2228
+ args.outputSchema
2229
+ ) : continuationInput
2230
+ );
2231
+ finalText = second.text;
2232
+ finalRaw = second.raw;
2233
+ accumulateTokenUsage(second.usage);
2234
+ aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
2235
+ finalFinishReason = second.finishReason;
2236
+ aggregatedProviderMetadata = mergeProviderMetadata(
2237
+ aggregatedProviderMetadata,
2238
+ second.providerMetadata
2239
+ );
2240
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
2241
+ traceSpan?.addEvent?.("useReason.continuation.complete", {
2242
+ textLength: second.text.length
2243
+ });
2244
+ if (args.outputSchema) {
2245
+ finalOutput = parseReasonOutputWithSchema({
2246
+ text: finalText,
2247
+ schema: args.outputSchema,
2248
+ ...second.finishReason ? { finishReason: second.finishReason } : {},
2249
+ label: "useReason output"
2250
+ });
2251
+ }
2252
+ }
2253
+ if (finalOutput !== void 0) {
2254
+ emitReasonStructuredOutput({
2255
+ ...id ? { id } : {},
2256
+ opId,
2257
+ output: finalOutput,
2258
+ structured: args.structured,
2259
+ emit: args.emit ?? true
2260
+ });
2261
+ }
2262
+ const result = {
2263
+ ...id ? { id } : {},
2264
+ opId,
2265
+ text: finalText,
2266
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
2267
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2268
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2269
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2270
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2271
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
2272
+ ...interruptResponse !== void 0 ? { interruptResponse } : {}
2273
+ };
2274
+ if (args.interrupt) {
2275
+ ctx.currentNodeState.byKey[checkpointKey] = {
2276
+ status: "completed",
2277
+ result
2278
+ };
2279
+ ctx.stateDirty = true;
2280
+ }
2281
+ traceSpan?.end?.({
2282
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2283
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2284
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2285
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2286
+ attributes: {
2287
+ textLength: finalText.length,
2288
+ resumedFromCheckpoint: Boolean(existingCheckpoint),
2289
+ interrupted: Boolean(interruptResponse !== void 0)
2290
+ },
2291
+ telemetry: {
2292
+ ...args.telemetry ?? {},
2293
+ output: args.telemetry?.output ?? finalOutput ?? finalText
2294
+ }
2295
+ });
2296
+ return result;
2297
+ }
2298
+ function useStructuredData(args) {
2299
+ emitStructuredData(args);
2300
+ }
2301
+ function useReason2(args) {
2302
+ return useReason(args);
2303
+ }
2304
+ function useInterrupt(args) {
2305
+ return awaitInterruptInternal(args);
2306
+ }
2307
+ function useRuntimeContext() {
2308
+ const ctx = getHookContext();
2309
+ const config = ctx.state.config;
2310
+ const context = config && typeof config === "object" && "context" in config ? config.context : void 0;
2311
+ return context && typeof context === "object" && !Array.isArray(context) ? context : {};
2312
+ }
2313
+ function useNodeState(initialValue) {
2314
+ const ctx = getHookContext();
2315
+ const nodeState = ctx.currentNodeState;
2316
+ const index = ctx.nodeStateIndex++;
2317
+ if (index >= nodeState.byIndex.length) {
2318
+ nodeState.byIndex[index] = initialValue;
2319
+ ctx.stateDirty = true;
2320
+ }
2321
+ const getValue = () => nodeState.byIndex[index];
2322
+ const setValue = (next) => {
2323
+ const prev = getValue();
2324
+ const resolved = typeof next === "function" ? next(prev) : next;
2325
+ nodeState.byIndex[index] = resolved;
2326
+ ctx.stateDirty = true;
2327
+ };
2328
+ return [getValue(), setValue];
2329
+ }
2330
+ function useWorkflowState(key, ...rest) {
2331
+ const ctx = getHookContext();
2332
+ const workflowState = ctx.workflowState;
2333
+ const hasInitial = rest.length > 0;
2334
+ const initialValue = rest[0];
2335
+ if (!Object.hasOwn(workflowState, key) && hasInitial) {
2336
+ workflowState[key] = initialValue;
2337
+ ctx.stateDirty = true;
2338
+ }
2339
+ const getValue = () => workflowState[key];
2340
+ const setValue = (next) => {
2341
+ const prev = getValue();
2342
+ const resolved = typeof next === "function" ? next(prev) : next;
2343
+ workflowState[key] = resolved;
2344
+ ctx.stateDirty = true;
2345
+ };
2346
+ return [getValue(), setValue];
2347
+ }
2348
+ var import_node_crypto5 = require("crypto");
2349
+ var import_node_crypto42 = require("crypto");
2350
+ function emitWorkflowCall2(telemetry, type, payload) {
2351
+ const base = telemetry?.correlation;
2352
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
2353
+ return;
2354
+ try {
2355
+ const active = telemetry.trace?.getActiveContext?.();
2356
+ void Promise.resolve(
2357
+ telemetry.reporter.emit([
2358
+ {
2359
+ schemaVersion: 1,
2360
+ eventId: (0, import_node_crypto42.randomUUID)(),
2361
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
2362
+ environment: telemetry.environment,
2363
+ service: telemetry.service,
2364
+ correlation: {
2365
+ ...base,
2366
+ runId: base.runId,
2367
+ workflowId: base.workflowId,
2368
+ ...active
2369
+ },
2370
+ type,
2371
+ payload
2372
+ }
2373
+ ])
2374
+ ).catch(() => {
2375
+ });
2376
+ } catch {
2377
+ }
2378
+ }
2379
+ function workflowCallContent(telemetry, side, value) {
2380
+ const policy = telemetry?.captureContent;
2381
+ if (!(policy === true || policy && typeof policy === "object" && policy[side]))
2382
+ return {};
2383
+ const serialized = JSON.stringify(value);
2384
+ return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
2385
+ }
2386
+ var WorkflowCallError = class extends Error {
2387
+ constructor(workflow, message) {
2388
+ super(`Workflow '${workflow}': ${message}`);
2389
+ this.workflow = workflow;
2390
+ this.name = "WorkflowCallError";
2391
+ }
2392
+ workflow;
2393
+ };
2394
+ function workflowCallFingerprint(value) {
2395
+ const seen = /* @__PURE__ */ new Set();
2396
+ const canonical = (v) => {
2397
+ if (v === null || typeof v === "string" || typeof v === "boolean") return v;
2398
+ if (typeof v === "number" && Number.isFinite(v)) return v;
2399
+ if (typeof v !== "object" || !v || seen.has(v)) {
2400
+ throw new Error("Workflow input and output must be JSON serializable.");
2401
+ }
2402
+ if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
2403
+ throw new Error(
2404
+ "Workflow input and output must contain plain JSON objects."
2405
+ );
2406
+ }
2407
+ seen.add(v);
2408
+ const result = Array.isArray(v) ? v.map(canonical) : Object.fromEntries(
2409
+ Object.keys(v).sort().map((key) => [
2410
+ key,
2411
+ canonical(v[key])
2412
+ ])
2413
+ );
2414
+ seen.delete(v);
2415
+ return result;
2416
+ };
2417
+ return JSON.stringify(canonical(value));
2418
+ }
2419
+ function createWorkflowHooks(workflows) {
2420
+ return {
2421
+ useWorkflow: (args) => {
2422
+ const workflow = workflows[args.workflow];
2423
+ if (!workflow || workflow.id !== args.workflow)
2424
+ throw new Error("Workflow registry keys must match definition ids.");
2425
+ return useWorkflow({ ...args, workflow });
2426
+ }
2427
+ };
2428
+ }
2429
+ async function useWorkflow(args) {
2430
+ const ctx = getHookContext();
2431
+ const service = ctx.node.callWorkflow;
2432
+ if (!service)
2433
+ throw new Error("useWorkflow requires an agent workflow registry.");
2434
+ if (!args.id.trim())
2435
+ throw new Error("useWorkflow requires a stable, nonempty id.");
2436
+ if (ctx.workflowCallActive)
2437
+ throw new Error(
2438
+ "Concurrent workflow calls are not supported; await each call."
2439
+ );
2440
+ if (ctx.workflowCallIds.has(args.id))
2441
+ throw new Error(
2442
+ `Duplicate workflow call id '${args.id}' in one node activation.`
2443
+ );
2444
+ if (ctx.workflowCallIds.size >= 64)
2445
+ throw new Error(
2446
+ "A node may call at most 64 child workflows per activation."
2447
+ );
2448
+ ctx.workflowCallIds.add(args.id);
2449
+ const definition = typeof args.workflow === "string" ? void 0 : args.workflow;
2450
+ const workflow = definition?.id ?? args.workflow;
2451
+ const input = args.input;
2452
+ const fingerprint = workflowCallFingerprint({
2453
+ workflow,
2454
+ input,
2455
+ version: definition?.version ?? null
2456
+ });
2457
+ const key = `__useWorkflow:${args.id}`;
2458
+ let record = ctx.currentNodeState.byKey[key];
2459
+ if (record && record.fingerprint !== fingerprint)
2460
+ throw new WorkflowCallError(
2461
+ workflow,
2462
+ "call input or target changed during replay"
2463
+ );
2464
+ record ??= {
2465
+ fingerprint,
2466
+ invocationId: (0, import_node_crypto5.randomUUID)(),
2467
+ status: "running",
2468
+ interrupts: []
2469
+ };
2470
+ ctx.currentNodeState.byKey[key] = record;
2471
+ ctx.stateDirty = true;
2472
+ ctx.workflowCallActive = true;
2473
+ const telemetry = ctx.node.workflowCallTelemetry;
2474
+ const activationKey = "__useWorkflowActivation";
2475
+ ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
2476
+ const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
2477
+ const metadata2 = {
2478
+ invocationId: record.invocationId,
2479
+ parentInvocationId: telemetry?.correlation?.invocationId ?? null,
2480
+ branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
2481
+ callId: args.id,
2482
+ callerNodeId: ctx.node.graph.node,
2483
+ callerNodeExecutionId,
2484
+ sourceWorkflowId: ctx.node.graph.name,
2485
+ targetWorkflowId: workflow,
2486
+ targetVersion: definition?.version ?? null
2487
+ };
2488
+ record.telemetry = metadata2;
2489
+ const report = (status, content = {}) => {
2490
+ record.sequence = (record.sequence ?? 0) + 1;
2491
+ emitWorkflowCall2(telemetry, `workflow.call.${status}`, {
2492
+ ...metadata2,
2493
+ sequence: record.sequence,
2494
+ ...content
2495
+ });
2496
+ };
2497
+ const cached = record.status === "completed";
2498
+ try {
2499
+ for (const interruption of record.interrupts) {
2500
+ interruption.response = await awaitInterruptInternal({
2501
+ request: interruption.request,
2502
+ ...interruption.request.meta ? { meta: interruption.request.meta } : {}
2503
+ });
2504
+ }
2505
+ if (record.status === "failed")
2506
+ throw new WorkflowCallError(workflow, record.error ?? "child failed");
2507
+ while (record.status !== "completed") {
2508
+ let outcome;
2509
+ try {
2510
+ report(
2511
+ record.snapshot ? "resumed" : "started",
2512
+ workflowCallContent(telemetry, "input", input)
2513
+ );
2514
+ outcome = await service({
2515
+ callerNodeExecutionId,
2516
+ id: args.id,
2517
+ workflow,
2518
+ input,
2519
+ definition,
2520
+ invocationId: record.invocationId,
2521
+ ...record.snapshot ? { snapshot: record.snapshot } : {},
2522
+ ...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
2523
+ });
2524
+ } catch (error) {
2525
+ record.status = "failed";
2526
+ record.error = error instanceof Error ? error.message : String(error);
2527
+ report("failed", { error: "Child workflow failed" });
2528
+ throw new WorkflowCallError(workflow, record.error);
2529
+ }
2530
+ if (outcome.status === "completed") {
2531
+ record.data = outcome.data;
2532
+ accumulateTokenUsage(outcome.usage);
2533
+ workflowCallFingerprint(record.data);
2534
+ record.status = "completed";
2535
+ delete record.snapshot;
2536
+ report(
2537
+ "completed",
2538
+ workflowCallContent(telemetry, "output", record.data)
2539
+ );
2540
+ } else {
2541
+ record.status = "interrupted";
2542
+ record.snapshot = outcome.snapshot;
2543
+ report("suspended", {
2544
+ leaf: outcome.request.meta?.workflowCall ?? null
2545
+ });
2546
+ const interruption = {
2547
+ request: outcome.request
2548
+ };
2549
+ record.interrupts.push(interruption);
2550
+ interruption.response = await awaitInterruptInternal({
2551
+ request: interruption.request,
2552
+ ...interruption.request.meta ? { meta: interruption.request.meta } : {}
2553
+ });
2554
+ }
2555
+ }
2556
+ if (cached)
2557
+ report("reused", workflowCallContent(telemetry, "output", record.data));
2558
+ const data = record.data ?? {};
2559
+ return {
2560
+ data: JSON.parse(JSON.stringify(data))
2561
+ };
2562
+ } finally {
2563
+ ctx.workflowCallActive = false;
2564
+ }
2565
+ }
2566
+ }
2567
+ });
18
2568
 
19
2569
  // src/index.ts
20
2570
  var index_exports = {};
@@ -29,6 +2579,7 @@ __export(index_exports, {
29
2579
  parseChatRequestBody: () => parseChatRequestBody,
30
2580
  parseCheckpointRequestBody: () => parseCheckpointRequestBody,
31
2581
  parseResumeMeta: () => parseResumeMeta,
2582
+ projectWorkflowTopology: () => projectWorkflowTopology,
32
2583
  streamChat: () => streamChat,
33
2584
  streamChatFromRoute: () => streamChatFromRoute,
34
2585
  transformGraphStreamForUI: () => transformGraphStreamForUI,
@@ -187,6 +2738,329 @@ var import_providers = require("@kortyx/providers");
187
2738
  var import_runtime4 = require("@kortyx/runtime");
188
2739
  var import_zod2 = require("zod");
189
2740
 
2741
+ // src/telemetry/events.ts
2742
+ var import_node_crypto = require("crypto");
2743
+ var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2744
+ var createId = () => {
2745
+ try {
2746
+ return (0, import_node_crypto.randomUUID)();
2747
+ } catch {
2748
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
2749
+ }
2750
+ };
2751
+ var shouldCaptureTelemetryContent = (value, side) => {
2752
+ if (value === true) return true;
2753
+ return Boolean(
2754
+ value && typeof value === "object" && !Array.isArray(value) && value[side] === true
2755
+ );
2756
+ };
2757
+ var emitTelemetryEvent = (args) => {
2758
+ const telemetry = isRecord(args.config.telemetry) ? args.config.telemetry : void 0;
2759
+ const base = telemetry?.correlation;
2760
+ const runId = args.correlation?.runId ?? base?.runId;
2761
+ const workflowId = args.correlation?.workflowId ?? base?.workflowId;
2762
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !runId || !workflowId) {
2763
+ return;
2764
+ }
2765
+ const context = isRecord(args.config.context) ? args.config.context : {};
2766
+ const event = {
2767
+ schemaVersion: 1,
2768
+ eventId: createId(),
2769
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
2770
+ environment: telemetry.environment,
2771
+ service: telemetry.service,
2772
+ correlation: {
2773
+ runId,
2774
+ workflowId,
2775
+ ...base?.sessionId ? { sessionId: base.sessionId } : {},
2776
+ ...base?.workflowRevisionId ? { workflowRevisionId: base.workflowRevisionId } : {},
2777
+ ...base?.topologyHash ? { topologyHash: base.topologyHash } : {},
2778
+ ...base?.nodeId ? { nodeId: base.nodeId } : {},
2779
+ ...args.correlation ?? {}
2780
+ },
2781
+ ...telemetry.metadata || telemetry.tags || context.userId || context.tenantId ? {
2782
+ context: {
2783
+ ...typeof context.userId === "string" ? { userId: context.userId } : {},
2784
+ ...typeof context.tenantId === "string" ? { tenantId: context.tenantId } : {},
2785
+ ...telemetry.tags ? { tags: telemetry.tags } : {},
2786
+ ...telemetry.metadata ? { metadata: telemetry.metadata } : {}
2787
+ }
2788
+ } : {},
2789
+ type: args.type,
2790
+ payload: {
2791
+ ...args.payload,
2792
+ ...args.type === "run.cancelled" ? { branchId: args.config.executionBranchId ?? runId } : {}
2793
+ }
2794
+ };
2795
+ try {
2796
+ void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
2797
+ if (!args.flush) return;
2798
+ const flush = telemetry.flush;
2799
+ if (typeof flush === "function") await flush.call(telemetry);
2800
+ }).catch(() => {
2801
+ });
2802
+ } catch {
2803
+ }
2804
+ };
2805
+
2806
+ // src/telemetry/topology.ts
2807
+ var import_node_crypto2 = require("crypto");
2808
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2809
+ var stableValue = (value) => {
2810
+ if (Array.isArray(value)) return value.map(stableValue);
2811
+ if (!isRecord2(value)) return value;
2812
+ return Object.fromEntries(
2813
+ Object.entries(value).filter(([, nested]) => typeof nested !== "function").sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, stableValue(nested)])
2814
+ );
2815
+ };
2816
+ var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
2817
+ var metadata = (value) => isRecord2(value) ? stableValue(value) : void 0;
2818
+ var tagsFrom = (workflow) => {
2819
+ const raw = isRecord2(workflow.metadata) ? workflow.metadata.tags : void 0;
2820
+ if (!Array.isArray(raw)) return void 0;
2821
+ const tags = raw.filter((tag) => typeof tag === "string").sort();
2822
+ return tags.length > 0 ? tags : void 0;
2823
+ };
2824
+ var wordsFrom = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").split(/[^a-zA-Z0-9]+/).map((word) => word.toLowerCase()).filter(Boolean);
2825
+ var camelFromWorkflowId = (workflowId) => {
2826
+ const [first = "", ...rest] = wordsFrom(workflowId);
2827
+ return [
2828
+ first,
2829
+ ...rest.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
2830
+ ].join("");
2831
+ };
2832
+ var resolveMemberTransitionTarget = (memberName, knownWorkflowIds) => {
2833
+ const exact = knownWorkflowIds.find(
2834
+ (workflowId) => camelFromWorkflowId(workflowId) === memberName
2835
+ );
2836
+ if (exact) return exact;
2837
+ const memberWords = new Set(wordsFrom(memberName));
2838
+ const candidates = knownWorkflowIds.map((workflowId) => {
2839
+ const workflowWords = wordsFrom(workflowId);
2840
+ const matched = workflowWords.filter((word) => memberWords.has(word));
2841
+ return {
2842
+ workflowId,
2843
+ matched: matched.length,
2844
+ total: workflowWords.length
2845
+ };
2846
+ }).filter(
2847
+ (candidate) => candidate.total > 0 && candidate.matched === candidate.total
2848
+ ).sort(
2849
+ (a, b) => b.matched - a.matched || a.workflowId.localeCompare(b.workflowId)
2850
+ );
2851
+ if (candidates.length !== 1) return void 0;
2852
+ return candidates[0]?.workflowId;
2853
+ };
2854
+ var transitionTargetsFromRun = (run, knownWorkflowIds) => {
2855
+ if (typeof run !== "function") return [];
2856
+ const source = Function.prototype.toString.call(run);
2857
+ const targets = /* @__PURE__ */ new Set();
2858
+ for (const match of source.matchAll(
2859
+ /\btransitionTo\s*:\s*(['"`])([^'"`$]+)\1/g
2860
+ )) {
2861
+ const target = match[2]?.trim();
2862
+ if (target) targets.add(target);
2863
+ }
2864
+ for (const match of source.matchAll(
2865
+ /\btransitionTo\s*:\s*(?:[A-Za-z_$][\w$]*\.)+([A-Za-z_$][\w$]*)/g
2866
+ )) {
2867
+ const memberName = match[1];
2868
+ const target = resolveMemberTransitionTarget(memberName, knownWorkflowIds);
2869
+ if (target) targets.add(target);
2870
+ }
2871
+ for (const match of source.matchAll(
2872
+ /\btransitionTo\s*:\s*(?:[A-Za-z_$][\w$]*\.)*[A-Za-z_$][\w$]*\[['"`]([A-Za-z_$][\w$]*)['"`]\]/g
2873
+ )) {
2874
+ const memberName = match[1];
2875
+ const target = resolveMemberTransitionTarget(memberName, knownWorkflowIds);
2876
+ if (target) targets.add(target);
2877
+ }
2878
+ return [...targets].sort();
2879
+ };
2880
+ var transitionsFromNodes = (workflow, knownWorkflowIds) => {
2881
+ const transitions = /* @__PURE__ */ new Map();
2882
+ for (const [sourceNodeId, node] of Object.entries(
2883
+ workflow.nodes
2884
+ )) {
2885
+ for (const targetWorkflowId of transitionTargetsFromRun(
2886
+ node.run,
2887
+ knownWorkflowIds
2888
+ )) {
2889
+ if (targetWorkflowId === workflow.id) continue;
2890
+ const key = `${sourceNodeId}:${targetWorkflowId}`;
2891
+ transitions.set(key, { sourceNodeId, targetWorkflowId });
2892
+ }
2893
+ }
2894
+ return [...transitions.values()].sort(
2895
+ (a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))
2896
+ );
2897
+ };
2898
+ var projectWorkflowTopology = (args) => {
2899
+ const nodes = Object.entries(
2900
+ args.workflow.nodes
2901
+ ).map(([id, node]) => {
2902
+ const nodeMetadata = metadata(node.metadata);
2903
+ const params = metadata(node.params);
2904
+ const model = isRecord2(node.params) ? isRecord2(node.params.model) ? node.params.model : void 0 : void 0;
2905
+ const provider = isRecord2(model) ? asString(model.provider) ?? asString(model.providerId) : void 0;
2906
+ const modelName = isRecord2(model) ? asString(model.name) ?? asString(model.modelId) : void 0;
2907
+ return {
2908
+ id,
2909
+ ...asString(nodeMetadata?.label) ? { label: asString(nodeMetadata?.label) } : {},
2910
+ ...asString(nodeMetadata?.type) ? { type: asString(nodeMetadata?.type) } : {},
2911
+ ...provider ? { provider } : {},
2912
+ ...modelName ? { model: modelName } : {},
2913
+ ...nodeMetadata ? { metadata: nodeMetadata } : {},
2914
+ // These fields feed the hash but are intentionally not sent as Studio
2915
+ // display metadata. They are stripped below after hashing.
2916
+ _execution: {
2917
+ ...typeof node.run === "string" ? { run: node.run } : {},
2918
+ ...params ? { params } : {},
2919
+ ...node.behavior ? { behavior: stableValue(node.behavior) } : {}
2920
+ }
2921
+ };
2922
+ }).sort((a, b) => a.id.localeCompare(b.id));
2923
+ const edges = args.workflow.edges.map((edge) => {
2924
+ const condition = isRecord2(edge[2]) ? asString(edge[2].when) : void 0;
2925
+ return {
2926
+ sourceNodeId: edge[0],
2927
+ targetNodeId: edge[1],
2928
+ ...condition ? { condition } : {}
2929
+ };
2930
+ }).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
2931
+ const transitions = transitionsFromNodes(
2932
+ args.workflow,
2933
+ args.knownWorkflowIds ?? []
2934
+ );
2935
+ const tags = tagsFrom(args.workflow);
2936
+ const hashInput = stableValue({
2937
+ workflowId: args.workflow.id,
2938
+ nodes: nodes.map((node) => ({ id: node.id, ...node._execution })),
2939
+ edges,
2940
+ transitions
2941
+ });
2942
+ const topologyHash = (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(hashInput)).digest("hex");
2943
+ return {
2944
+ schemaVersion: 1,
2945
+ environment: args.environment,
2946
+ service: args.service,
2947
+ workflow: {
2948
+ id: args.workflow.id,
2949
+ declaredVersion: args.workflow.version,
2950
+ ...args.workflow.description ? { description: args.workflow.description } : {},
2951
+ ...tags ? { tags } : {},
2952
+ topologyHash,
2953
+ nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
2954
+ edges,
2955
+ ...transitions.length > 0 ? { transitions } : {}
2956
+ }
2957
+ };
2958
+ };
2959
+ var prepareWorkflowTelemetry = (args) => {
2960
+ const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
2961
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
2962
+ return args.config;
2963
+ }
2964
+ let snapshot;
2965
+ try {
2966
+ snapshot = projectWorkflowTopology({
2967
+ workflow: args.workflow,
2968
+ environment: telemetry.environment,
2969
+ service: telemetry.service,
2970
+ knownWorkflowIds: args.knownWorkflowIds
2971
+ });
2972
+ } catch {
2973
+ return args.config;
2974
+ }
2975
+ let workflowRevisionId;
2976
+ try {
2977
+ workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
2978
+ environment: snapshot.environment,
2979
+ workflowId: snapshot.workflow.id,
2980
+ topologyHash: snapshot.workflow.topologyHash
2981
+ });
2982
+ void Promise.resolve(
2983
+ telemetry.reporter.ensureWorkflowTopology(snapshot)
2984
+ ).catch(() => {
2985
+ });
2986
+ } catch {
2987
+ }
2988
+ return {
2989
+ ...args.config,
2990
+ prepareChildTelemetry: (workflow, config) => prepareWorkflowTelemetry({
2991
+ config,
2992
+ workflow,
2993
+ runId: args.runId,
2994
+ sessionId: args.sessionId,
2995
+ knownWorkflowIds: args.knownWorkflowIds
2996
+ }),
2997
+ telemetry: {
2998
+ ...telemetry,
2999
+ correlation: {
3000
+ ...telemetry.correlation ?? {},
3001
+ runId: args.runId,
3002
+ ...args.sessionId ? { sessionId: args.sessionId } : {},
3003
+ workflowId: snapshot.workflow.id,
3004
+ topologyHash: snapshot.workflow.topologyHash,
3005
+ ...workflowRevisionId ? { workflowRevisionId } : {}
3006
+ }
3007
+ }
3008
+ };
3009
+ };
3010
+
3011
+ // src/telemetry/workflow-call-branch.ts
3012
+ var import_node_crypto3 = require("crypto");
3013
+ var import_hooks = __toESM(require_dist());
3014
+ function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
3015
+ const branchId = (0, import_node_crypto3.randomUUID)();
3016
+ const emitted = /* @__PURE__ */ new Set();
3017
+ for (const request of requests) {
3018
+ if (request.state)
3019
+ request.state.config = {
3020
+ ...request.state.config,
3021
+ executionBranchId: branchId
3022
+ };
3023
+ const seen = /* @__PURE__ */ new Set();
3024
+ const visit = (value) => {
3025
+ if (!value || typeof value !== "object" || seen.has(value)) return;
3026
+ seen.add(value);
3027
+ const record = value;
3028
+ if (typeof record.invocationId === "string" && typeof record.fingerprint === "string" && Array.isArray(record.interrupts)) {
3029
+ const previous = record.telemetry;
3030
+ if (previous) {
3031
+ record.sequence = 0;
3032
+ record.telemetry = { ...previous, branchId };
3033
+ if (!emitted.has(record.invocationId)) {
3034
+ emitted.add(record.invocationId);
3035
+ (0, import_hooks.emitWorkflowCall)(
3036
+ telemetry ? {
3037
+ ...telemetry,
3038
+ correlation: {
3039
+ runId: request.runId,
3040
+ workflowId: request.workflow,
3041
+ ...request.sessionId ? { sessionId: request.sessionId } : {}
3042
+ }
3043
+ } : void 0,
3044
+ "workflow.call.restored",
3045
+ {
3046
+ ...previous,
3047
+ branchId,
3048
+ sourceBranchId: previous.branchId,
3049
+ sourceRunId,
3050
+ sequence: 0,
3051
+ status: record.status
3052
+ }
3053
+ );
3054
+ }
3055
+ }
3056
+ }
3057
+ for (const child of Object.values(record)) visit(child);
3058
+ };
3059
+ visit(request);
3060
+ }
3061
+ return branchId;
3062
+ }
3063
+
190
3064
  // src/chat/process-chat.ts
191
3065
  var import_runtime3 = require("@kortyx/runtime");
192
3066
 
@@ -263,50 +3137,71 @@ async function* transformGraphStreamForUI(stream, options = {}) {
263
3137
  }
264
3138
 
265
3139
  // src/orchestrator.ts
266
- var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
267
- var isGraphState = (value) => isRecord(value) && typeof value.currentWorkflow === "string";
3140
+ var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3141
+ var isGraphState = (value) => isRecord3(value) && typeof value.currentWorkflow === "string";
268
3142
  var getGraphCheckpointId = (snapshot) => {
269
- if (!isRecord(snapshot)) return void 0;
270
- const config = isRecord(snapshot.config) ? snapshot.config : void 0;
271
- const configurable = config && isRecord(config.configurable) ? config.configurable : void 0;
3143
+ if (!isRecord3(snapshot)) return void 0;
3144
+ const config = isRecord3(snapshot.config) ? snapshot.config : void 0;
3145
+ const configurable = config && isRecord3(config.configurable) ? config.configurable : void 0;
272
3146
  const checkpointId = configurable?.checkpoint_id;
273
3147
  return typeof checkpointId === "string" && checkpointId.length > 0 ? checkpointId : void 0;
274
3148
  };
275
3149
  var getTraceAdapter = (config) => {
276
- const telemetry = isRecord(config.telemetry) ? config.telemetry : void 0;
3150
+ const telemetry = isRecord3(config.telemetry) ? config.telemetry : void 0;
277
3151
  const traceAdapter = telemetry?.trace ?? config.reasonTrace;
278
- return isRecord(traceAdapter) ? traceAdapter : void 0;
3152
+ return isRecord3(traceAdapter) ? traceAdapter : void 0;
279
3153
  };
280
3154
  async function orchestrateGraphStream({
281
3155
  sessionId,
282
3156
  runId,
283
3157
  graph,
284
3158
  state,
285
- config,
3159
+ config: initialConfig,
286
3160
  selectWorkflow,
3161
+ knownWorkflowIds,
287
3162
  frameworkAdapter
288
3163
  }) {
289
3164
  const out = new import_node_stream.PassThrough({ objectMode: true });
3165
+ let config = initialConfig;
290
3166
  let currentGraph = graph;
291
3167
  let currentState = state;
292
3168
  let finished = false;
293
3169
  let structuredSeq = 0;
294
3170
  const debugEnabled = Boolean(config?.features?.tracing);
295
3171
  const traceAdapter = getTraceAdapter(config);
296
- const contextMeta = isRecord(config.context) ? config.context : {};
297
- const telemetryConfig = isRecord(config.telemetry) ? config.telemetry : {};
3172
+ const contextMeta = isRecord3(config.context) ? config.context : {};
3173
+ const telemetryConfig = isRecord3(config.telemetry) ? config.telemetry : {};
3174
+ const emitResumeFailure = (resumeError) => {
3175
+ const interruptId = typeof config.telemetryInterruptId === "string" ? config.telemetryInterruptId : void 0;
3176
+ if (!interruptId) return;
3177
+ emitTelemetryEvent({
3178
+ config,
3179
+ type: "interrupt.resolved",
3180
+ correlation: typeof config.telemetryInterruptNodeId === "string" ? { nodeId: config.telemetryInterruptNodeId } : void 0,
3181
+ payload: {
3182
+ interruptId,
3183
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
3184
+ resumeOutcome: "failed",
3185
+ resumeError: resumeError instanceof Error ? resumeError.message : String(resumeError)
3186
+ },
3187
+ flush: true
3188
+ });
3189
+ };
3190
+ const telemetryCorrelation = isRecord3(telemetryConfig.correlation) ? telemetryConfig.correlation : {};
298
3191
  const runSpanArgs = {
299
3192
  name: "kortyx.run",
300
3193
  attributes: {
301
3194
  ...sessionId ? { sessionId } : {},
302
3195
  runId,
303
3196
  workflowId: state.currentWorkflow,
3197
+ ...typeof telemetryCorrelation.topologyHash === "string" ? { topologyHash: telemetryCorrelation.topologyHash } : {},
3198
+ ...typeof telemetryCorrelation.workflowRevisionId === "string" ? { workflowRevisionId: telemetryCorrelation.workflowRevisionId } : {},
304
3199
  ...typeof contextMeta.userId === "string" ? { userId: contextMeta.userId } : {},
305
3200
  ...typeof contextMeta.tenantId === "string" ? { tenantId: contextMeta.tenantId } : {}
306
3201
  },
307
3202
  telemetry: {
308
3203
  metadata: {
309
- ...isRecord(telemetryConfig.metadata) ? telemetryConfig.metadata : {},
3204
+ ...isRecord3(telemetryConfig.metadata) ? telemetryConfig.metadata : {},
310
3205
  ...contextMeta
311
3206
  },
312
3207
  tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags : void 0,
@@ -328,7 +3223,9 @@ async function orchestrateGraphStream({
328
3223
  }
329
3224
  const pending = {
330
3225
  to: null,
331
- payload: {}
3226
+ payload: {},
3227
+ sourceNodeId: void 0,
3228
+ sourceWorkflowId: void 0
332
3229
  };
333
3230
  let lastStatusMsg = "";
334
3231
  let lastStatusAt = 0;
@@ -350,6 +3247,21 @@ async function orchestrateGraphStream({
350
3247
  let pendingRecordToken = null;
351
3248
  let wroteHumanInput = false;
352
3249
  const pendingStore = frameworkAdapter?.pendingRequests;
3250
+ const sealPendingSnapshots = async () => {
3251
+ if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
3252
+ for (const [token, request] of activePendingRequests) {
3253
+ const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
3254
+ frameworkAdapter.checkpointer,
3255
+ runId,
3256
+ request.graphCheckpointId
3257
+ );
3258
+ if (graphSnapshot) {
3259
+ const sealed = { ...request, graphSnapshot };
3260
+ activePendingRequests.set(token, sealed);
3261
+ await pendingStore?.update(token, { graphSnapshot });
3262
+ }
3263
+ }
3264
+ };
353
3265
  const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1e3;
354
3266
  const persistAndEmitInterrupt = async (payload) => {
355
3267
  if (wroteHumanInput) return;
@@ -430,6 +3342,37 @@ async function orchestrateGraphStream({
430
3342
  }))
431
3343
  }
432
3344
  });
3345
+ emitTelemetryEvent({
3346
+ config,
3347
+ type: "interrupt.created",
3348
+ correlation: { nodeId: record.node },
3349
+ payload: {
3350
+ workflowCall: record.schema.meta?.workflowCall ?? null,
3351
+ workflowCallPath: record.schema.meta?.workflowCallPath ?? null,
3352
+ branchId: config.executionBranchId ?? runId,
3353
+ interruptId: record.requestId,
3354
+ requestId: record.requestId,
3355
+ kind: record.schema.kind,
3356
+ interactionMode: record.schema.kind === "text" ? "freeform" : record.options.length > 0 ? "static-options" : record.schema.schemaId ? "dynamic-picker" : "unknown",
3357
+ ...record.schema.schemaId ? { schemaId: record.schema.schemaId } : {},
3358
+ ...record.schema.schemaVersion ? { schemaVersion: record.schema.schemaVersion } : {},
3359
+ ...typeof record.schema.question === "string" && shouldCaptureTelemetryContent(telemetryConfig.captureContent, "output") ? { question: record.schema.question } : {},
3360
+ ...shouldCaptureTelemetryContent(
3361
+ telemetryConfig.captureContent,
3362
+ "output"
3363
+ ) ? {
3364
+ options: record.options.map((option) => ({
3365
+ id: option.id,
3366
+ label: option.label,
3367
+ ...option.description ? { description: option.description } : {}
3368
+ }))
3369
+ } : {},
3370
+ optionCount: record.options.length,
3371
+ nodeId: record.node,
3372
+ expiresAt: new Date(record.createdAt + record.ttlMs).toISOString()
3373
+ },
3374
+ flush: true
3375
+ });
433
3376
  wroteHumanInput = true;
434
3377
  };
435
3378
  const forwardEmit = (event, payload) => {
@@ -595,6 +3538,9 @@ async function orchestrateGraphStream({
595
3538
  });
596
3539
  pending.to = payload?.transitionTo ?? null;
597
3540
  pending.payload = payload?.payload ?? {};
3541
+ const transition = payload;
3542
+ pending.sourceNodeId = typeof transition.node === "string" ? transition.node : void 0;
3543
+ pending.sourceWorkflowId = typeof transition.workflow === "string" ? transition.workflow : void 0;
598
3544
  return;
599
3545
  }
600
3546
  if (event === "interrupt") {
@@ -619,6 +3565,7 @@ async function orchestrateGraphStream({
619
3565
  let workflowDoneData;
620
3566
  currentGraph.config = currentGraph.config || {};
621
3567
  currentGraph.config.emit = forwardEmit;
3568
+ currentGraph.config.executionRunId = runId;
622
3569
  const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
623
3570
  const checkpointNs = "";
624
3571
  namespacesUsed.add(checkpointNs);
@@ -637,7 +3584,7 @@ async function orchestrateGraphStream({
637
3584
  checkpoint_ns: checkpointNs
638
3585
  }
639
3586
  });
640
- const values = isRecord(snapshot) && "values" in snapshot ? snapshot.values : snapshot;
3587
+ const values = isRecord3(snapshot) && "values" in snapshot ? snapshot.values : snapshot;
641
3588
  const checkpointId = getGraphCheckpointId(snapshot) ?? await readLatestGraphCheckpointId();
642
3589
  if (!isGraphState(values)) {
643
3590
  return checkpointId ? { state: currentState, checkpointId } : null;
@@ -696,11 +3643,40 @@ async function orchestrateGraphStream({
696
3643
  }
697
3644
  const transitionTo = pending.to;
698
3645
  const transitionPayload = pending.payload;
3646
+ const sourceNodeId = pending.sourceNodeId;
3647
+ const sourceWorkflowId = pending.sourceWorkflowId;
699
3648
  pending.to = null;
700
3649
  pending.payload = {};
3650
+ pending.sourceNodeId = void 0;
3651
+ pending.sourceWorkflowId = void 0;
701
3652
  if (transitionTo) {
702
3653
  try {
703
3654
  const nextWorkflow = await selectWorkflow(transitionTo);
3655
+ const sourceTelemetry = isRecord3(config.telemetry) ? config.telemetry : {};
3656
+ config = prepareWorkflowTelemetry({
3657
+ config,
3658
+ workflow: nextWorkflow,
3659
+ runId,
3660
+ sessionId,
3661
+ knownWorkflowIds
3662
+ });
3663
+ const targetTelemetry = isRecord3(config.telemetry) ? config.telemetry : {};
3664
+ emitTelemetryEvent({
3665
+ config,
3666
+ type: "workflow.transitioned",
3667
+ ...sourceNodeId ? { correlation: { nodeId: sourceNodeId } } : {},
3668
+ payload: {
3669
+ sourceNodeId: sourceNodeId ?? "",
3670
+ sourceWorkflowId: sourceWorkflowId ?? String(currentState.currentWorkflow),
3671
+ ...typeof sourceTelemetry.correlation === "object" && sourceTelemetry.correlation && typeof sourceTelemetry.correlation.workflowRevisionId === "string" ? {
3672
+ sourceWorkflowRevisionId: sourceTelemetry.correlation.workflowRevisionId
3673
+ } : {},
3674
+ targetWorkflowId: transitionTo,
3675
+ ...typeof targetTelemetry.correlation === "object" && targetTelemetry.correlation && typeof targetTelemetry.correlation.workflowRevisionId === "string" ? {
3676
+ targetWorkflowRevisionId: targetTelemetry.correlation.workflowRevisionId
3677
+ } : {}
3678
+ }
3679
+ });
704
3680
  const nextGraph = await (0, import_runtime.createExecutionGraph)(nextWorkflow, {
705
3681
  ...config,
706
3682
  emit: forwardEmit
@@ -715,6 +3691,7 @@ async function orchestrateGraphStream({
715
3691
  currentState = {
716
3692
  ...currentState,
717
3693
  currentWorkflow: transitionTo,
3694
+ config,
718
3695
  input: newInput,
719
3696
  data: mergedData,
720
3697
  ui: {}
@@ -783,6 +3760,7 @@ async function orchestrateGraphStream({
783
3760
  });
784
3761
  runTraceSpan.end?.(runTraceEndArgs());
785
3762
  const resolvedSessionId2 = config?.session?.id || sessionId || "";
3763
+ await sealPendingSnapshots();
786
3764
  if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
787
3765
  try {
788
3766
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
@@ -804,6 +3782,16 @@ async function orchestrateGraphStream({
804
3782
  turnIndex: checkpoint.turnIndex,
805
3783
  ...checkpoint.label ? { label: checkpoint.label } : {}
806
3784
  });
3785
+ emitTelemetryEvent({
3786
+ config,
3787
+ type: "session.checkpointed",
3788
+ payload: {
3789
+ checkpointId: checkpoint.id,
3790
+ turnIndex: checkpoint.turnIndex,
3791
+ ...checkpoint.parentCheckpointId ? { parentCheckpointId: checkpoint.parentCheckpointId } : {},
3792
+ nodes: checkpoint.nodes
3793
+ }
3794
+ });
807
3795
  } catch (error) {
808
3796
  console.error("[orchestrator] session checkpoint failed", error);
809
3797
  }
@@ -834,6 +3822,7 @@ async function orchestrateGraphStream({
834
3822
  }
835
3823
  }
836
3824
  const resolvedSessionId = config?.session?.id || sessionId || "";
3825
+ await sealPendingSnapshots();
837
3826
  if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
838
3827
  try {
839
3828
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
@@ -853,6 +3842,16 @@ async function orchestrateGraphStream({
853
3842
  turnIndex: checkpoint.turnIndex,
854
3843
  ...checkpoint.label ? { label: checkpoint.label } : {}
855
3844
  });
3845
+ emitTelemetryEvent({
3846
+ config,
3847
+ type: "session.checkpointed",
3848
+ payload: {
3849
+ checkpointId: checkpoint.id,
3850
+ turnIndex: checkpoint.turnIndex,
3851
+ ...checkpoint.parentCheckpointId ? { parentCheckpointId: checkpoint.parentCheckpointId } : {},
3852
+ nodes: checkpoint.nodes
3853
+ }
3854
+ });
856
3855
  } catch (error) {
857
3856
  console.error("[orchestrator] session checkpoint failed", error);
858
3857
  }
@@ -887,6 +3886,7 @@ async function orchestrateGraphStream({
887
3886
  }) : runLoop(fallbackRunSpan ?? {});
888
3887
  runPromise.catch((err) => {
889
3888
  fallbackRunSpan?.fail?.(err);
3889
+ emitResumeFailure(err);
890
3890
  console.error("[error:orchestrateGraphStream]", err);
891
3891
  out.write({
892
3892
  type: "error",
@@ -899,11 +3899,16 @@ async function orchestrateGraphStream({
899
3899
  }
900
3900
 
901
3901
  // src/interrupt/resume-handler.ts
902
- var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3902
+ var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3903
+ var responseFromSelection = (selected) => {
3904
+ if (selected.length === 0) return void 0;
3905
+ if (selected.length === 1) return selected[0];
3906
+ return JSON.stringify(selected);
3907
+ };
903
3908
  function parseResumeMeta(msg) {
904
3909
  if (!msg || !msg.metadata) return null;
905
3910
  const raw = msg.metadata.resume;
906
- if (!isRecord2(raw)) return null;
3911
+ if (!isRecord4(raw)) return null;
907
3912
  const token = typeof raw.token === "string" ? raw.token : "";
908
3913
  const requestId = typeof raw.requestId === "string" ? raw.requestId : "";
909
3914
  const cancel = raw.cancel === true;
@@ -911,10 +3916,10 @@ function parseResumeMeta(msg) {
911
3916
  const rawSelected = raw.selected;
912
3917
  if (typeof rawSelected === "string") selected = [rawSelected];
913
3918
  else if (Array.isArray(rawSelected)) selected = rawSelected.map(String);
914
- else if (isRecord2(raw.choice) && typeof raw.choice.id === "string")
3919
+ else if (isRecord4(raw.choice) && typeof raw.choice.id === "string")
915
3920
  selected = [raw.choice.id];
916
3921
  else if (Array.isArray(raw.choices))
917
- selected = raw.choices.map((c) => isRecord2(c) ? c.id : void 0).filter((id) => typeof id === "string");
3922
+ selected = raw.choices.map((c) => isRecord4(c) ? c.id : void 0).filter((id) => typeof id === "string");
918
3923
  if (!token || !requestId) return null;
919
3924
  return { token, requestId, selected, cancel };
920
3925
  }
@@ -923,6 +3928,7 @@ async function tryPrepareResumeStream({
923
3928
  sessionId,
924
3929
  config,
925
3930
  selectWorkflow,
3931
+ knownWorkflowIds,
926
3932
  defaultWorkflowId,
927
3933
  applyResumeSelection,
928
3934
  frameworkAdapter
@@ -933,14 +3939,34 @@ async function tryPrepareResumeStream({
933
3939
  if (!store) return null;
934
3940
  const pending = await store.get(meta.token);
935
3941
  if (!pending || pending.requestId !== meta.requestId) {
936
- console.log(
937
- `[resume] pending not found or mismatched. token=${meta.token} requestId=${meta.requestId}`
3942
+ throw new Error(
3943
+ "Interrupt is expired, already consumed, or does not match the request."
938
3944
  );
939
- return null;
940
3945
  }
3946
+ if (pending.sessionId && pending.sessionId !== sessionId)
3947
+ throw new Error("Interrupt belongs to another session.");
3948
+ if (store.take && !await store.take(meta.token))
3949
+ throw new Error("Interrupt has already been resumed or cancelled.");
941
3950
  if (meta.cancel) {
942
3951
  await store.delete(pending.token);
943
- return null;
3952
+ emitTelemetryEvent({
3953
+ config,
3954
+ type: "interrupt.cancelled",
3955
+ correlation: {
3956
+ runId: pending.runId,
3957
+ sessionId,
3958
+ workflowId: pending.workflow,
3959
+ nodeId: pending.node
3960
+ },
3961
+ payload: {
3962
+ interruptId: pending.requestId,
3963
+ reason: "cancelled_by_client"
3964
+ },
3965
+ flush: true
3966
+ });
3967
+ return (async function* () {
3968
+ yield { type: "done" };
3969
+ })();
944
3970
  }
945
3971
  console.log(
946
3972
  `[resume] token=${meta.token} requestId=${meta.requestId} selected=${JSON.stringify(
@@ -948,10 +3974,10 @@ async function tryPrepareResumeStream({
948
3974
  )} sessionId=${sessionId}`
949
3975
  );
950
3976
  const resumeData = applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
951
- const resumeDataPatch = isRecord2(resumeData) ? resumeData : {};
952
- const pendingMeta = isRecord2(pending.schema?.meta) ? pending.schema.meta : {};
953
- const resumeStatePatch = isRecord2(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
954
- const pendingData = isRecord2(pending.state?.data) ? pending.state?.data : {};
3977
+ const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
3978
+ const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
3979
+ const resumeStatePatch = isRecord4(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
3980
+ const pendingData = isRecord4(pending.state?.data) ? pending.state?.data : {};
955
3981
  const workflowId = typeof pending.workflow === "string" && pending.workflow.trim() ? pending.workflow : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() ? defaultWorkflowId : "job-search";
956
3982
  const resumedState = {
957
3983
  // For static breakpoints, resume with null input (set in orchestrator),
@@ -969,7 +3995,29 @@ async function tryPrepareResumeStream({
969
3995
  }
970
3996
  };
971
3997
  const wf = await selectWorkflow(resumedState.currentWorkflow);
972
- const resumeUpdate = {};
3998
+ const telemetryConfig = prepareWorkflowTelemetry({
3999
+ config: {
4000
+ ...config,
4001
+ ...pending.state?.config?.executionBranchId ? { executionBranchId: pending.state.config.executionBranchId } : {}
4002
+ },
4003
+ workflow: wf,
4004
+ runId: pending.runId,
4005
+ sessionId,
4006
+ knownWorkflowIds
4007
+ });
4008
+ resumedState.config = telemetryConfig;
4009
+ const resumeUpdate = pending.graphSnapshot ? {
4010
+ config: {
4011
+ ...telemetryConfig,
4012
+ context: pending.state?.config?.context ?? telemetryConfig.context
4013
+ }
4014
+ } : {};
4015
+ if (pending.graphSnapshot && frameworkAdapter)
4016
+ await (0, import_runtime2.restoreGraphSnapshot)(
4017
+ frameworkAdapter.checkpointer,
4018
+ pending.graphSnapshot,
4019
+ pending.runId
4020
+ );
973
4021
  if (Object.keys(resumeDataPatch).length > 0) {
974
4022
  resumeUpdate.data = {
975
4023
  ...pendingData,
@@ -983,26 +4031,52 @@ async function tryPrepareResumeStream({
983
4031
  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;
984
4032
  const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
985
4033
  const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
986
- ...config,
4034
+ ...telemetryConfig,
987
4035
  resume: true,
988
4036
  ...resumeValue !== void 0 ? { resumeValue } : {},
989
4037
  ...resumeCheckpointId ? { resumeCheckpointId } : {},
990
4038
  ...hasResumeUpdate ? { resumeUpdate } : {}
991
4039
  });
992
4040
  await store.delete(pending.token);
4041
+ const response = responseFromSelection(meta.selected);
4042
+ const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
4043
+ const responseCaptured = Boolean(
4044
+ response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
4045
+ );
4046
+ emitTelemetryEvent({
4047
+ config: telemetryConfig,
4048
+ type: "interrupt.resolved",
4049
+ correlation: {
4050
+ runId: pending.runId,
4051
+ sessionId,
4052
+ workflowId,
4053
+ nodeId: pending.node
4054
+ },
4055
+ payload: {
4056
+ interruptId: pending.requestId,
4057
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
4058
+ resumeOutcome: "resumed",
4059
+ responseCaptured,
4060
+ ...responseCaptured && response ? { response } : {}
4061
+ },
4062
+ flush: true
4063
+ });
993
4064
  const args = {
994
4065
  sessionId,
995
4066
  runId: pending.runId,
996
4067
  graph: resumedGraph,
997
4068
  state: resumedState,
998
4069
  config: {
999
- ...config,
4070
+ ...telemetryConfig,
1000
4071
  resume: true,
4072
+ telemetryInterruptId: pending.requestId,
4073
+ telemetryInterruptNodeId: pending.node,
1001
4074
  ...resumeValue !== void 0 ? { resumeValue } : {},
1002
4075
  ...resumeCheckpointId ? { resumeCheckpointId } : {},
1003
4076
  ...hasResumeUpdate ? { resumeUpdate } : {}
1004
4077
  },
1005
4078
  selectWorkflow,
4079
+ knownWorkflowIds,
1006
4080
  frameworkAdapter
1007
4081
  };
1008
4082
  const stream = await orchestrateGraphStream(args);
@@ -1030,12 +4104,13 @@ async function streamChat({
1030
4104
  loadRuntimeConfig,
1031
4105
  selectWorkflow,
1032
4106
  workflowRegistry,
4107
+ knownWorkflowIds,
1033
4108
  frameworkAdapter,
1034
4109
  getProvider,
1035
4110
  applyResumeSelection
1036
4111
  }) {
1037
4112
  const config = await loadRuntimeConfig(options);
1038
- const runtimeConfig = {
4113
+ let runtimeConfig = {
1039
4114
  ...config,
1040
4115
  getProvider,
1041
4116
  ...frameworkAdapter ? { checkpointer: frameworkAdapter.checkpointer } : {}
@@ -1046,6 +4121,7 @@ async function streamChat({
1046
4121
  "streamChat requires selectWorkflow or workflowRegistry to resolve workflows."
1047
4122
  );
1048
4123
  }
4124
+ runtimeConfig.selectWorkflow = workflowSelector;
1049
4125
  const fallbackSessionId = options?.sessionId;
1050
4126
  const resolvedSessionId = sessionId || fallbackSessionId || "anonymous-session";
1051
4127
  const last = messages[messages.length - 1];
@@ -1078,6 +4154,7 @@ async function streamChat({
1078
4154
  sessionId: resolvedSessionId,
1079
4155
  config: runtimeConfig,
1080
4156
  selectWorkflow: workflowSelector,
4157
+ knownWorkflowIds,
1081
4158
  ...frameworkAdapter ? { frameworkAdapter } : {},
1082
4159
  ...defaultWorkflowId ? { defaultWorkflowId } : {},
1083
4160
  ...applyResumeSelection ? { applyResumeSelection } : {}
@@ -1103,6 +4180,13 @@ async function streamChat({
1103
4180
  }
1104
4181
  const currentWorkflow = requestedWorkflowId ? baseState.currentWorkflow : headCheckpoint?.state.currentWorkflow ?? baseState.currentWorkflow;
1105
4182
  const selectedWorkflow = await workflowSelector(currentWorkflow);
4183
+ runtimeConfig = prepareWorkflowTelemetry({
4184
+ config: runtimeConfig,
4185
+ workflow: selectedWorkflow,
4186
+ runId,
4187
+ sessionId: resolvedSessionId,
4188
+ knownWorkflowIds
4189
+ });
1106
4190
  const graph = await (0, import_runtime3.createExecutionGraph)(selectedWorkflow, runtimeConfig);
1107
4191
  const initialState = headCheckpoint ? {
1108
4192
  ...headCheckpoint.state,
@@ -1115,7 +4199,7 @@ async function streamChat({
1115
4199
  ...runtime.requestedWorkflow ? { requestedWorkflow: runtime.requestedWorkflow } : {},
1116
4200
  ...runtime.priorMessages ? { priorMessages: runtime.priorMessages } : {}
1117
4201
  }
1118
- } : { ...baseState, currentWorkflow };
4202
+ } : { ...baseState, config: runtimeConfig, currentWorkflow };
1119
4203
  const orchestratedStream = await orchestrateGraphStream({
1120
4204
  sessionId: resolvedSessionId,
1121
4205
  runId,
@@ -1123,6 +4207,7 @@ async function streamChat({
1123
4207
  state: initialState,
1124
4208
  config: runtimeConfig,
1125
4209
  selectWorkflow: workflowSelector,
4210
+ knownWorkflowIds,
1126
4211
  ...frameworkAdapter ? { frameworkAdapter } : {}
1127
4212
  });
1128
4213
  return orchestratedStream;
@@ -1186,7 +4271,7 @@ var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
1186
4271
  return graphCheckpointId ? { ...request, graphCheckpointId } : request;
1187
4272
  };
1188
4273
  var clearPendingGraphWrites = async (frameworkAdapter, request) => {
1189
- if (!request.graphCheckpointId) return;
4274
+ if (request.graphSnapshot || !request.graphCheckpointId) return;
1190
4275
  const checkpointer = frameworkAdapter.checkpointer;
1191
4276
  if (!checkpointer?.deleteCheckpointWrites) return;
1192
4277
  await Promise.all([
@@ -1236,6 +4321,22 @@ function createAgent(args) {
1236
4321
  fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
1237
4322
  });
1238
4323
  })();
4324
+ const registeredWorkflowsPromise = registryPromise.then(
4325
+ (registry) => registry?.list?.()
4326
+ );
4327
+ const projectTopology = async (options) => {
4328
+ const registeredWorkflows = await registeredWorkflowsPromise;
4329
+ if (!registeredWorkflows?.length) return [];
4330
+ const knownWorkflowIds = registeredWorkflows.map((workflow) => workflow.id);
4331
+ return registeredWorkflows.map(
4332
+ (workflow) => projectWorkflowTopology({
4333
+ workflow,
4334
+ environment: options.environment,
4335
+ service: options.service,
4336
+ knownWorkflowIds
4337
+ })
4338
+ );
4339
+ };
1239
4340
  const streamChat2 = async (messages, options) => {
1240
4341
  const parsedOptions = parseAgentProcessOptions(options);
1241
4342
  const registry = await registryPromise;
@@ -1244,11 +4345,18 @@ function createAgent(args) {
1244
4345
  "createAgent requires workflows, workflowsDir, or workflowRegistry."
1245
4346
  );
1246
4347
  }
4348
+ const registeredWorkflows = await registeredWorkflowsPromise.catch(
4349
+ () => void 0
4350
+ );
4351
+ const knownWorkflowIds = registeredWorkflows?.map(
4352
+ (workflow) => workflow.id
4353
+ );
1247
4354
  return streamChat({
1248
4355
  ...resolvedDefaultWorkflowId ? { defaultWorkflowId: resolvedDefaultWorkflowId } : {},
1249
4356
  messages,
1250
4357
  options: parsedOptions,
1251
4358
  workflowRegistry: registry,
4359
+ ...knownWorkflowIds ? { knownWorkflowIds } : {},
1252
4360
  frameworkAdapter: resolvedFrameworkAdapter,
1253
4361
  getProvider: resolvedGetProvider,
1254
4362
  loadRuntimeConfig: (runtimeOptions) => ({
@@ -1265,12 +4373,18 @@ function createAgent(args) {
1265
4373
  const listCheckpoints = (sessionId) => resolvedFrameworkAdapter.sessionCheckpoints.list(sessionId);
1266
4374
  const getCheckpoint = (id) => resolvedFrameworkAdapter.sessionCheckpoints.get(id);
1267
4375
  const rollbackTo = async (id) => {
4376
+ const target = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
1268
4377
  const result = await resolvedFrameworkAdapter.sessionCheckpoints.rollbackTo(id);
1269
4378
  const activePendingRequests = await Promise.all(
1270
4379
  result.activePendingRequests.map(
1271
4380
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1272
4381
  )
1273
4382
  );
4383
+ restoreWorkflowCallBranch(
4384
+ activePendingRequests,
4385
+ telemetry,
4386
+ target?.runId ?? "unknown"
4387
+ );
1274
4388
  await Promise.all(
1275
4389
  activePendingRequests.map(
1276
4390
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
@@ -1284,9 +4398,25 @@ function createAgent(args) {
1284
4398
  (request) => resolvedFrameworkAdapter.pendingRequests.save(request)
1285
4399
  )
1286
4400
  ]);
4401
+ if (target) {
4402
+ emitTelemetryEvent({
4403
+ config: { ...telemetry ? { telemetry } : {} },
4404
+ type: "session.rolled_back",
4405
+ correlation: {
4406
+ runId: target.runId,
4407
+ sessionId: target.sessionId,
4408
+ workflowId: target.workflow
4409
+ },
4410
+ payload: {
4411
+ checkpointId: target.id,
4412
+ invalidatedInterruptIds: result.invalidatedInterruptTokens
4413
+ }
4414
+ });
4415
+ }
1287
4416
  return { ...result, activePendingRequests };
1288
4417
  };
1289
4418
  const fork = async (id, options) => {
4419
+ const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
1290
4420
  const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
1291
4421
  id,
1292
4422
  options
@@ -1296,6 +4426,11 @@ function createAgent(args) {
1296
4426
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1297
4427
  )
1298
4428
  );
4429
+ restoreWorkflowCallBranch(
4430
+ activePendingRequests,
4431
+ telemetry,
4432
+ source?.runId ?? "unknown"
4433
+ );
1299
4434
  await Promise.all(
1300
4435
  activePendingRequests.map(
1301
4436
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
@@ -1306,12 +4441,33 @@ function createAgent(args) {
1306
4441
  (request) => resolvedFrameworkAdapter.pendingRequests.save(request)
1307
4442
  )
1308
4443
  );
4444
+ emitTelemetryEvent({
4445
+ config: { ...telemetry ? { telemetry } : {} },
4446
+ type: "session.forked",
4447
+ correlation: {
4448
+ runId: result.checkpoint.runId,
4449
+ sessionId: result.checkpoint.sessionId,
4450
+ workflowId: result.checkpoint.workflow
4451
+ },
4452
+ payload: {
4453
+ newSessionId: result.sessionId,
4454
+ parentSessionId: result.parentSessionId,
4455
+ forkedFromCheckpointId: result.forkedFrom
4456
+ }
4457
+ });
1309
4458
  return {
1310
4459
  ...result,
1311
4460
  checkpoint: { ...result.checkpoint, activePendingRequests }
1312
4461
  };
1313
4462
  };
1314
- return { streamChat: streamChat2, listCheckpoints, getCheckpoint, rollbackTo, fork };
4463
+ return {
4464
+ projectTopology,
4465
+ streamChat: streamChat2,
4466
+ listCheckpoints,
4467
+ getCheckpoint,
4468
+ rollbackTo,
4469
+ fork
4470
+ };
1315
4471
  }
1316
4472
  // Annotate the CommonJS export names for ESM import in node:
1317
4473
  0 && (module.exports = {
@@ -1325,6 +4481,7 @@ function createAgent(args) {
1325
4481
  parseChatRequestBody,
1326
4482
  parseCheckpointRequestBody,
1327
4483
  parseResumeMeta,
4484
+ projectWorkflowTopology,
1328
4485
  streamChat,
1329
4486
  streamChatFromRoute,
1330
4487
  transformGraphStreamForUI,