@kortyx/agent 0.15.0 → 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 = {};
@@ -237,7 +2787,10 @@ var emitTelemetryEvent = (args) => {
237
2787
  }
238
2788
  } : {},
239
2789
  type: args.type,
240
- payload: args.payload
2790
+ payload: {
2791
+ ...args.payload,
2792
+ ...args.type === "run.cancelled" ? { branchId: args.config.executionBranchId ?? runId } : {}
2793
+ }
241
2794
  };
242
2795
  try {
243
2796
  void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
@@ -434,6 +2987,13 @@ var prepareWorkflowTelemetry = (args) => {
434
2987
  }
435
2988
  return {
436
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
+ }),
437
2997
  telemetry: {
438
2998
  ...telemetry,
439
2999
  correlation: {
@@ -448,6 +3008,59 @@ var prepareWorkflowTelemetry = (args) => {
448
3008
  };
449
3009
  };
450
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
+
451
3064
  // src/chat/process-chat.ts
452
3065
  var import_runtime3 = require("@kortyx/runtime");
453
3066
 
@@ -634,6 +3247,21 @@ async function orchestrateGraphStream({
634
3247
  let pendingRecordToken = null;
635
3248
  let wroteHumanInput = false;
636
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
+ };
637
3265
  const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1e3;
638
3266
  const persistAndEmitInterrupt = async (payload) => {
639
3267
  if (wroteHumanInput) return;
@@ -719,6 +3347,9 @@ async function orchestrateGraphStream({
719
3347
  type: "interrupt.created",
720
3348
  correlation: { nodeId: record.node },
721
3349
  payload: {
3350
+ workflowCall: record.schema.meta?.workflowCall ?? null,
3351
+ workflowCallPath: record.schema.meta?.workflowCallPath ?? null,
3352
+ branchId: config.executionBranchId ?? runId,
722
3353
  interruptId: record.requestId,
723
3354
  requestId: record.requestId,
724
3355
  kind: record.schema.kind,
@@ -934,6 +3565,7 @@ async function orchestrateGraphStream({
934
3565
  let workflowDoneData;
935
3566
  currentGraph.config = currentGraph.config || {};
936
3567
  currentGraph.config.emit = forwardEmit;
3568
+ currentGraph.config.executionRunId = runId;
937
3569
  const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
938
3570
  const checkpointNs = "";
939
3571
  namespacesUsed.add(checkpointNs);
@@ -1128,6 +3760,7 @@ async function orchestrateGraphStream({
1128
3760
  });
1129
3761
  runTraceSpan.end?.(runTraceEndArgs());
1130
3762
  const resolvedSessionId2 = config?.session?.id || sessionId || "";
3763
+ await sealPendingSnapshots();
1131
3764
  if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
1132
3765
  try {
1133
3766
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
@@ -1189,6 +3822,7 @@ async function orchestrateGraphStream({
1189
3822
  }
1190
3823
  }
1191
3824
  const resolvedSessionId = config?.session?.id || sessionId || "";
3825
+ await sealPendingSnapshots();
1192
3826
  if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
1193
3827
  try {
1194
3828
  const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
@@ -1305,11 +3939,14 @@ async function tryPrepareResumeStream({
1305
3939
  if (!store) return null;
1306
3940
  const pending = await store.get(meta.token);
1307
3941
  if (!pending || pending.requestId !== meta.requestId) {
1308
- console.log(
1309
- `[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."
1310
3944
  );
1311
- return null;
1312
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.");
1313
3950
  if (meta.cancel) {
1314
3951
  await store.delete(pending.token);
1315
3952
  emitTelemetryEvent({
@@ -1327,7 +3964,9 @@ async function tryPrepareResumeStream({
1327
3964
  },
1328
3965
  flush: true
1329
3966
  });
1330
- return null;
3967
+ return (async function* () {
3968
+ yield { type: "done" };
3969
+ })();
1331
3970
  }
1332
3971
  console.log(
1333
3972
  `[resume] token=${meta.token} requestId=${meta.requestId} selected=${JSON.stringify(
@@ -1357,14 +3996,28 @@ async function tryPrepareResumeStream({
1357
3996
  };
1358
3997
  const wf = await selectWorkflow(resumedState.currentWorkflow);
1359
3998
  const telemetryConfig = prepareWorkflowTelemetry({
1360
- config,
3999
+ config: {
4000
+ ...config,
4001
+ ...pending.state?.config?.executionBranchId ? { executionBranchId: pending.state.config.executionBranchId } : {}
4002
+ },
1361
4003
  workflow: wf,
1362
4004
  runId: pending.runId,
1363
4005
  sessionId,
1364
4006
  knownWorkflowIds
1365
4007
  });
1366
4008
  resumedState.config = telemetryConfig;
1367
- const resumeUpdate = {};
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
+ );
1368
4021
  if (Object.keys(resumeDataPatch).length > 0) {
1369
4022
  resumeUpdate.data = {
1370
4023
  ...pendingData,
@@ -1468,6 +4121,7 @@ async function streamChat({
1468
4121
  "streamChat requires selectWorkflow or workflowRegistry to resolve workflows."
1469
4122
  );
1470
4123
  }
4124
+ runtimeConfig.selectWorkflow = workflowSelector;
1471
4125
  const fallbackSessionId = options?.sessionId;
1472
4126
  const resolvedSessionId = sessionId || fallbackSessionId || "anonymous-session";
1473
4127
  const last = messages[messages.length - 1];
@@ -1617,7 +4271,7 @@ var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
1617
4271
  return graphCheckpointId ? { ...request, graphCheckpointId } : request;
1618
4272
  };
1619
4273
  var clearPendingGraphWrites = async (frameworkAdapter, request) => {
1620
- if (!request.graphCheckpointId) return;
4274
+ if (request.graphSnapshot || !request.graphCheckpointId) return;
1621
4275
  const checkpointer = frameworkAdapter.checkpointer;
1622
4276
  if (!checkpointer?.deleteCheckpointWrites) return;
1623
4277
  await Promise.all([
@@ -1726,6 +4380,11 @@ function createAgent(args) {
1726
4380
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1727
4381
  )
1728
4382
  );
4383
+ restoreWorkflowCallBranch(
4384
+ activePendingRequests,
4385
+ telemetry,
4386
+ target?.runId ?? "unknown"
4387
+ );
1729
4388
  await Promise.all(
1730
4389
  activePendingRequests.map(
1731
4390
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
@@ -1757,6 +4416,7 @@ function createAgent(args) {
1757
4416
  return { ...result, activePendingRequests };
1758
4417
  };
1759
4418
  const fork = async (id, options) => {
4419
+ const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
1760
4420
  const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
1761
4421
  id,
1762
4422
  options
@@ -1766,6 +4426,11 @@ function createAgent(args) {
1766
4426
  (request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
1767
4427
  )
1768
4428
  );
4429
+ restoreWorkflowCallBranch(
4430
+ activePendingRequests,
4431
+ telemetry,
4432
+ source?.runId ?? "unknown"
4433
+ );
1769
4434
  await Promise.all(
1770
4435
  activePendingRequests.map(
1771
4436
  (request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)