@kortyx/agent 0.24.0 → 0.25.1

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
@@ -29,6 +29,476 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  ));
30
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
31
 
32
+ // ../hooks/dist/internal.js
33
+ var require_internal = __commonJS({
34
+ "../hooks/dist/internal.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 internal_exports = {};
53
+ __export2(internal_exports, {
54
+ errorDiagnostics: () => errorDiagnostics,
55
+ exceptionDiagnostics: () => exceptionDiagnostics,
56
+ isModelTraceSpan: () => isModelTraceSpan,
57
+ runReasonEngine: () => runReasonEngine,
58
+ safeSpan: () => safeSpan,
59
+ safeStartSpan: () => safeStartSpan2,
60
+ safeTelemetryMetadata: () => safeTelemetryMetadata2,
61
+ withSafeTraceSpan: () => withSafeTraceSpan2
62
+ });
63
+ module2.exports = __toCommonJS2(internal_exports);
64
+ var import_errors6 = require("@kortyx/core/errors");
65
+ var MAX_STACK_LENGTH = 32768;
66
+ var MAX_CAUSE_DEPTH = 4;
67
+ var projectDetails = (value, depth, seen) => {
68
+ const objectValue = value && typeof value === "object" ? value : void 0;
69
+ if (objectValue) {
70
+ if (seen.has(objectValue))
71
+ return { type: "Error", message: "Circular error cause." };
72
+ seen.add(objectValue);
73
+ }
74
+ const name = (0, import_errors6.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
75
+ const message = (0, import_errors6.errorProperty)(value, "message");
76
+ const stack = (0, import_errors6.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
77
+ try {
78
+ return value.stack;
79
+ } catch {
80
+ return void 0;
81
+ }
82
+ })() : void 0);
83
+ const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors6.errorProperty)(value, "cause") : null;
84
+ return {
85
+ type: typeof name === "string" ? name.slice(0, 256) : "Error",
86
+ message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
87
+ ...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
88
+ ...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
89
+ };
90
+ };
91
+ var sanitizeProjectedDetails = (details, depth = 0) => ({
92
+ ...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
93
+ message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
94
+ ...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
95
+ ...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
96
+ });
97
+ function exceptionDiagnostics(error, project) {
98
+ try {
99
+ const details = project ? project(error) : projectDetails(error, 0, /* @__PURE__ */ new WeakSet());
100
+ return details ? sanitizeProjectedDetails(details) : null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ function errorDiagnostics(error, project) {
106
+ const details = exceptionDiagnostics(error, project);
107
+ return details ? {
108
+ errorMessage: details.message,
109
+ ...details.type ? { errorType: details.type } : {}
110
+ } : {};
111
+ }
112
+ function isModelTraceSpan(name) {
113
+ return name === "runReasonEngine" || name === "useReason";
114
+ }
115
+ var import_node_crypto4 = require("crypto");
116
+ var import_core6 = require("@kortyx/core");
117
+ var import_errors22 = require("@kortyx/core/errors");
118
+ function safeSpan(span) {
119
+ const guard = (fn) => (...args) => {
120
+ try {
121
+ fn?.(...args);
122
+ } catch {
123
+ }
124
+ };
125
+ let ended = false;
126
+ const finish = (fn) => (...args) => {
127
+ if (ended) return;
128
+ ended = true;
129
+ guard(fn)(...args);
130
+ };
131
+ return {
132
+ addEvent: guard(
133
+ (name, attributes) => span?.addEvent?.(name, attributes)
134
+ ),
135
+ setAttributes: guard(
136
+ (attributes) => span?.setAttributes?.(attributes)
137
+ ),
138
+ end: finish((args) => span?.end?.(args)),
139
+ fail: finish((error, args) => {
140
+ if (span?.fail) span.fail(error, args);
141
+ else span?.end?.(args);
142
+ })
143
+ };
144
+ }
145
+ function safeStartSpan2(adapter, args) {
146
+ try {
147
+ return safeSpan(adapter?.startSpan?.(args));
148
+ } catch {
149
+ return safeSpan(void 0);
150
+ }
151
+ }
152
+ async function withSafeTraceSpan2(adapter, args, fn) {
153
+ let execution;
154
+ const invoke = (span) => {
155
+ execution ??= Promise.resolve().then(async () => {
156
+ const observed = safeSpan(span);
157
+ try {
158
+ const result = await fn(observed);
159
+ observed.end?.();
160
+ return result;
161
+ } catch (error) {
162
+ observed.fail?.(error);
163
+ throw error;
164
+ }
165
+ });
166
+ return execution;
167
+ };
168
+ let activeScope;
169
+ try {
170
+ activeScope = adapter?.withSpan?.bind(adapter);
171
+ } catch {
172
+ }
173
+ if (activeScope) {
174
+ let started;
175
+ const invoked = new Promise((resolve2) => {
176
+ started = resolve2;
177
+ });
178
+ const observe = async () => {
179
+ try {
180
+ await activeScope(args, (span) => {
181
+ const result = invoke(span);
182
+ started?.();
183
+ return result;
184
+ });
185
+ } catch {
186
+ }
187
+ };
188
+ const observation = observe();
189
+ if (!execution) await Promise.race([invoked, observation]);
190
+ } else {
191
+ await invoke(safeStartSpan2(adapter, args)).catch(() => {
192
+ });
193
+ }
194
+ return execution ?? invoke(safeSpan(void 0));
195
+ }
196
+ function assertModelFinish(result) {
197
+ if (result.finishReason?.unified !== "content-filter") return;
198
+ throw Object.assign(
199
+ new import_errors22.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
200
+ category: "provider",
201
+ retryable: false,
202
+ safeMessage: "The provider refused the request."
203
+ }),
204
+ {
205
+ ...result.usage ? { usage: result.usage } : {},
206
+ finishReason: result.finishReason
207
+ }
208
+ );
209
+ }
210
+ var toError = (value) => value instanceof Error ? value : new Error(String(value));
211
+ var createRuntimeId = () => {
212
+ try {
213
+ return (0, import_node_crypto4.randomUUID)();
214
+ } catch {
215
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
216
+ }
217
+ };
218
+ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
219
+ if (!emitEvent) return;
220
+ emitEvent(event, {
221
+ ...payload,
222
+ ...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
223
+ });
224
+ };
225
+ var resolveProviderOptions = (defaults, overrides) => {
226
+ const providerOptions = defaults || overrides ? { ...defaults, ...overrides } : void 0;
227
+ for (const [key, value] of Object.entries(overrides ?? {})) {
228
+ const previous = defaults?.[key];
229
+ if (previous && value && typeof previous === "object" && typeof value === "object" && !Array.isArray(previous) && !Array.isArray(value)) {
230
+ if (providerOptions) providerOptions[key] = { ...previous, ...value };
231
+ }
232
+ }
233
+ return providerOptions;
234
+ };
235
+ async function runReasonEngine(args) {
236
+ let stream = args.stream ?? args.model.options?.streaming ?? true;
237
+ const emit = args.emit ?? true;
238
+ const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
239
+ const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
240
+ const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
241
+ const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
242
+ const reasoning = args.reasoning ?? args.model.options?.reasoning;
243
+ const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
244
+ const providerOptions = resolveProviderOptions(
245
+ args.model.options?.providerOptions,
246
+ args.providerOptions
247
+ );
248
+ const tools = args.tools ?? args.model.options?.tools;
249
+ (0, import_core6.throwIfExecutionAborted)(abortSignal);
250
+ const model = args.model.provider.getModel(args.model.modelId, {
251
+ ...temperature !== void 0 ? { temperature } : {},
252
+ streaming: stream,
253
+ ...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
254
+ ...stopSequences !== void 0 ? { stopSequences } : {},
255
+ ...abortSignal !== void 0 ? { abortSignal } : {},
256
+ ...reasoning !== void 0 ? { reasoning } : {},
257
+ ...responseFormat !== void 0 ? { responseFormat } : {},
258
+ ...tools !== void 0 ? { tools } : {},
259
+ ...providerOptions !== void 0 ? { providerOptions } : {}
260
+ });
261
+ if (tools?.length && !model.supportsToolStreaming) stream = false;
262
+ const messages = args.messages !== void 0 ? args.messages : [
263
+ ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
264
+ { role: "user", content: String(args.input ?? "") }
265
+ ];
266
+ const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
267
+ const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
268
+ const commonMeta = {
269
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
270
+ opId,
271
+ segmentId
272
+ };
273
+ const traceSpan = safeStartSpan2(args.reasonTrace, {
274
+ name: "runReasonEngine",
275
+ attributes: {
276
+ ...commonMeta,
277
+ providerId: args.model.provider.id,
278
+ modelId: args.model.modelId,
279
+ stream,
280
+ emit,
281
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
282
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
283
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
284
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
285
+ ...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
286
+ },
287
+ telemetry: {
288
+ ...args.telemetry ?? {},
289
+ input: args.telemetry?.input ?? messages.map(
290
+ ({ continuation: _continuation, raw: _raw, ...message }) => message
291
+ )
292
+ }
293
+ });
294
+ const modelRequestStartedAt = Date.now();
295
+ let firstTextTokenAt;
296
+ let lastTextTokenAt;
297
+ const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
298
+ ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
299
+ streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
300
+ timeToLastTokenMs: Math.max(
301
+ 0,
302
+ lastTextTokenAt - modelRequestStartedAt
303
+ )
304
+ };
305
+ const endTrace = (span, result) => {
306
+ span?.end?.({
307
+ ...result.usage !== void 0 ? { usage: result.usage } : {},
308
+ ...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
309
+ ...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
310
+ ...result.warnings !== void 0 ? { warnings: result.warnings } : {},
311
+ attributes: {
312
+ textLength: result.text.length,
313
+ ...generationTimingAttributes()
314
+ },
315
+ telemetry: {
316
+ ...args.telemetry ?? {},
317
+ output: args.telemetry?.output ?? result.text
318
+ }
319
+ });
320
+ };
321
+ const failTrace = (span, error) => {
322
+ const detail = error;
323
+ try {
324
+ span?.fail?.(error, {
325
+ ...detail?.usage ? { usage: detail.usage } : {},
326
+ ...detail?.finishReason ? { finishReason: detail.finishReason } : {},
327
+ ...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
328
+ attributes: {
329
+ providerId: args.model.provider.id,
330
+ modelId: args.model.modelId
331
+ }
332
+ });
333
+ } catch {
334
+ }
335
+ };
336
+ try {
337
+ if (stream) {
338
+ let continuation;
339
+ let final = "";
340
+ let raw;
341
+ let usage;
342
+ let finishReason;
343
+ let toolCalls;
344
+ let providerMetadata;
345
+ let warnings;
346
+ if (emit) {
347
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
348
+ ...commonMeta
349
+ });
350
+ }
351
+ const response2 = await model.stream(messages);
352
+ for await (const chunk of response2) {
353
+ (0, import_core6.throwIfExecutionAborted)(abortSignal);
354
+ switch (chunk.type) {
355
+ case "text-delta": {
356
+ if (chunk.delta.length === 0) break;
357
+ const tokenAt = Date.now();
358
+ firstTextTokenAt ??= tokenAt;
359
+ lastTextTokenAt = tokenAt;
360
+ final += chunk.delta;
361
+ args.onTextChunk?.(chunk.delta);
362
+ if (chunk.raw !== void 0) {
363
+ raw = chunk.raw;
364
+ }
365
+ if (chunk.providerMetadata !== void 0) {
366
+ providerMetadata = chunk.providerMetadata;
367
+ }
368
+ if (emit) {
369
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
370
+ ...commonMeta,
371
+ delta: chunk.delta
372
+ });
373
+ }
374
+ break;
375
+ }
376
+ case "finish": {
377
+ continuation = chunk.continuation;
378
+ if (chunk.raw !== void 0) {
379
+ raw = chunk.raw;
380
+ }
381
+ if (chunk.usage !== void 0) {
382
+ usage = chunk.usage;
383
+ }
384
+ if (chunk.finishReason !== void 0) {
385
+ finishReason = chunk.finishReason;
386
+ }
387
+ if (chunk.toolCalls !== void 0) {
388
+ toolCalls = chunk.toolCalls;
389
+ }
390
+ if (chunk.providerMetadata !== void 0) {
391
+ providerMetadata = chunk.providerMetadata;
392
+ }
393
+ if (chunk.warnings !== void 0) {
394
+ warnings = chunk.warnings;
395
+ }
396
+ break;
397
+ }
398
+ case "raw": {
399
+ raw = chunk.raw;
400
+ if (chunk.providerMetadata !== void 0) {
401
+ providerMetadata = chunk.providerMetadata;
402
+ }
403
+ break;
404
+ }
405
+ case "error": {
406
+ if (chunk.raw !== void 0) {
407
+ raw = chunk.raw;
408
+ }
409
+ if (chunk.providerMetadata !== void 0) {
410
+ providerMetadata = chunk.providerMetadata;
411
+ }
412
+ if (chunk.warnings !== void 0) {
413
+ warnings = chunk.warnings;
414
+ }
415
+ throw toError(chunk.error);
416
+ }
417
+ }
418
+ }
419
+ (0, import_core6.throwIfExecutionAborted)(abortSignal);
420
+ assertModelFinish({ finishReason, usage });
421
+ if (emit) {
422
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
423
+ ...commonMeta
424
+ });
425
+ }
426
+ const result2 = {
427
+ text: final,
428
+ ...continuation ? { continuation } : {},
429
+ ...raw !== void 0 ? { raw } : {},
430
+ ...usage !== void 0 ? { usage } : {},
431
+ ...finishReason !== void 0 ? { finishReason } : {},
432
+ ...toolCalls !== void 0 ? { toolCalls } : {},
433
+ ...providerMetadata !== void 0 ? { providerMetadata } : {},
434
+ ...warnings !== void 0 ? { warnings } : {}
435
+ };
436
+ (0, import_core6.throwIfExecutionAborted)(abortSignal);
437
+ endTrace(traceSpan, result2);
438
+ return result2;
439
+ }
440
+ const response = await model.invoke(messages);
441
+ (0, import_core6.throwIfExecutionAborted)(abortSignal);
442
+ assertModelFinish(response);
443
+ if (response.content) args.onTextChunk?.(response.content);
444
+ if (emit) {
445
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
446
+ ...commonMeta
447
+ });
448
+ if (response.content) {
449
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
450
+ ...commonMeta,
451
+ delta: response.content
452
+ });
453
+ }
454
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
455
+ ...commonMeta
456
+ });
457
+ }
458
+ const result = {
459
+ text: response.content,
460
+ ...response.continuation ? { continuation: response.continuation } : {},
461
+ ...response.raw !== void 0 ? { raw: response.raw } : {},
462
+ ...response.usage !== void 0 ? { usage: response.usage } : {},
463
+ ...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
464
+ ...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
465
+ ...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
466
+ ...response.warnings !== void 0 ? { warnings: response.warnings } : {}
467
+ };
468
+ endTrace(traceSpan, result);
469
+ return result;
470
+ } catch (error) {
471
+ const failure = abortSignal?.aborted ? (0, import_core6.createExecutionCancelledError)() : error;
472
+ failTrace(traceSpan, failure);
473
+ throw failure;
474
+ }
475
+ }
476
+ var privateField = /^(?:password|passwd|secret|clientsecret|privatekey|credential|credentials|authorization|bearer|bearertoken|jwt|token|accesstoken|refreshtoken|authtoken|apikey|apisecret|cookie|setcookie|resumehandle|resumetoken)$/;
477
+ function safeTelemetryMetadata2(value) {
478
+ const seen = /* @__PURE__ */ new WeakSet();
479
+ const visit = (input, depth) => {
480
+ if (depth > 8 || typeof input === "function" || typeof input === "symbol")
481
+ return void 0;
482
+ if (!input || typeof input !== "object") return input;
483
+ if (seen.has(input)) return void 0;
484
+ seen.add(input);
485
+ if (Array.isArray(input))
486
+ return input.map((item) => visit(item, depth + 1));
487
+ return Object.fromEntries(
488
+ Object.entries(input).flatMap(
489
+ ([key, nested]) => privateField.test(key.toLowerCase().replace(/[^a-z0-9]/g, "")) ? [] : [[key, visit(nested, depth + 1)]]
490
+ )
491
+ );
492
+ };
493
+ try {
494
+ return visit(value, 0);
495
+ } catch {
496
+ return {};
497
+ }
498
+ }
499
+ }
500
+ });
501
+
32
502
  // ../hooks/dist/index.js
33
503
  var require_dist = __commonJS({
34
504
  "../hooks/dist/index.js"(exports2, module2) {
@@ -57,6 +527,7 @@ var require_dist = __commonJS({
57
527
  createWorkflowHooks: () => createWorkflowHooks,
58
528
  emitWorkflowCall: () => emitWorkflowCall2,
59
529
  parallel: () => parallel,
530
+ reportError: () => reportError,
60
531
  runWithHookContext: () => runWithHookContext,
61
532
  useAbortSignal: () => useAbortSignal,
62
533
  useInterrupt: () => useInterrupt,
@@ -64,6 +535,7 @@ var require_dist = __commonJS({
64
535
  useReason: () => useReason2,
65
536
  useRuntimeContext: () => useRuntimeContext,
66
537
  useStructuredData: () => useStructuredData,
538
+ useTool: () => useTool,
67
539
  useWorkflow: () => useWorkflow,
68
540
  useWorkflowState: () => useWorkflowState,
69
541
  workflowCallFingerprint: () => workflowCallFingerprint
@@ -127,6 +599,7 @@ var require_dist = __commonJS({
127
599
  workflowContextClosed: false,
128
600
  nodeStateIndex: 0,
129
601
  reasonCallIndex: 0,
602
+ toolCallIndex: 0,
130
603
  currentNodeState,
131
604
  workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
132
605
  tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
@@ -217,6 +690,12 @@ var require_dist = __commonJS({
217
690
  }
218
691
  });
219
692
  cleanupCompletedReasonCheckpoints(internal);
693
+ for (const key of Object.keys(internal.currentNodeState.byKey)) {
694
+ if (key.startsWith("__useTool:")) {
695
+ delete internal.currentNodeState.byKey[key];
696
+ internal.stateDirty = true;
697
+ }
698
+ }
220
699
  for (const key of Object.keys(internal.currentNodeState.byKey)) {
221
700
  if (key.startsWith("__useWorkflow:") || key.startsWith("__parallel:") || key === "__useWorkflowActivation") {
222
701
  internal.currentNodeState.byKey[key] = null;
@@ -357,7 +836,85 @@ var require_dist = __commonJS({
357
836
  );
358
837
  return Promise.resolve(response);
359
838
  };
360
- var import_core52 = require("@kortyx/core");
839
+ var import_core62 = require("@kortyx/core");
840
+ function safeSpan(span) {
841
+ const guard = (fn) => (...args) => {
842
+ try {
843
+ fn?.(...args);
844
+ } catch {
845
+ }
846
+ };
847
+ let ended = false;
848
+ const finish = (fn) => (...args) => {
849
+ if (ended) return;
850
+ ended = true;
851
+ guard(fn)(...args);
852
+ };
853
+ return {
854
+ addEvent: guard(
855
+ (name, attributes) => span?.addEvent?.(name, attributes)
856
+ ),
857
+ setAttributes: guard(
858
+ (attributes) => span?.setAttributes?.(attributes)
859
+ ),
860
+ end: finish((args) => span?.end?.(args)),
861
+ fail: finish((error, args) => {
862
+ if (span?.fail) span.fail(error, args);
863
+ else span?.end?.(args);
864
+ })
865
+ };
866
+ }
867
+ function safeStartSpan2(adapter, args) {
868
+ try {
869
+ return safeSpan(adapter?.startSpan?.(args));
870
+ } catch {
871
+ return safeSpan(void 0);
872
+ }
873
+ }
874
+ async function withSafeTraceSpan2(adapter, args, fn) {
875
+ let execution;
876
+ const invoke = (span) => {
877
+ execution ??= Promise.resolve().then(async () => {
878
+ const observed = safeSpan(span);
879
+ try {
880
+ const result = await fn(observed);
881
+ observed.end?.();
882
+ return result;
883
+ } catch (error) {
884
+ observed.fail?.(error);
885
+ throw error;
886
+ }
887
+ });
888
+ return execution;
889
+ };
890
+ let activeScope;
891
+ try {
892
+ activeScope = adapter?.withSpan?.bind(adapter);
893
+ } catch {
894
+ }
895
+ if (activeScope) {
896
+ let started;
897
+ const invoked = new Promise((resolve2) => {
898
+ started = resolve2;
899
+ });
900
+ const observe = async () => {
901
+ try {
902
+ await activeScope(args, (span) => {
903
+ const result = invoke(span);
904
+ started?.();
905
+ return result;
906
+ });
907
+ } catch {
908
+ }
909
+ };
910
+ const observation = observe();
911
+ if (!execution) await Promise.race([invoked, observation]);
912
+ } else {
913
+ await invoke(safeStartSpan2(adapter, args)).catch(() => {
914
+ });
915
+ }
916
+ return execution ?? invoke(safeSpan(void 0));
917
+ }
361
918
  var import_node_crypto4 = require("crypto");
362
919
  var parseStructuredPath = (path, source) => {
363
920
  if (path.length === 0) {
@@ -444,15 +1001,408 @@ var require_dist = __commonJS({
444
1001
  };
445
1002
  var shouldEmitStructured = (cfg) => Boolean(cfg);
446
1003
  var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
1004
+ var import_node_async_hooks2 = require("async_hooks");
447
1005
  var import_node_crypto32 = require("crypto");
1006
+ var import_core22 = require("@kortyx/core");
1007
+ var import_errors32 = require("@kortyx/core/errors");
1008
+ var import_errors22 = require("@kortyx/core/errors");
1009
+ var MAX_STACK_LENGTH = 32768;
1010
+ var MAX_CAUSE_DEPTH = 4;
1011
+ var projectDetails = (value, depth, seen) => {
1012
+ const objectValue = value && typeof value === "object" ? value : void 0;
1013
+ if (objectValue) {
1014
+ if (seen.has(objectValue))
1015
+ return { type: "Error", message: "Circular error cause." };
1016
+ seen.add(objectValue);
1017
+ }
1018
+ const name = (0, import_errors22.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
1019
+ const message = (0, import_errors22.errorProperty)(value, "message");
1020
+ const stack = (0, import_errors22.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
1021
+ try {
1022
+ return value.stack;
1023
+ } catch {
1024
+ return void 0;
1025
+ }
1026
+ })() : void 0);
1027
+ const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors22.errorProperty)(value, "cause") : null;
1028
+ return {
1029
+ type: typeof name === "string" ? name.slice(0, 256) : "Error",
1030
+ message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
1031
+ ...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
1032
+ ...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
1033
+ };
1034
+ };
1035
+ var sanitizeProjectedDetails = (details, depth = 0) => ({
1036
+ ...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
1037
+ message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
1038
+ ...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
1039
+ ...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
1040
+ });
1041
+ function exceptionDiagnostics(error, project) {
1042
+ try {
1043
+ const details = project ? project(error) : projectDetails(error, 0, /* @__PURE__ */ new WeakSet());
1044
+ return details ? sanitizeProjectedDetails(details) : null;
1045
+ } catch {
1046
+ return null;
1047
+ }
1048
+ }
1049
+ function errorDiagnostics(error, project) {
1050
+ const details = exceptionDiagnostics(error, project);
1051
+ return details ? {
1052
+ errorMessage: details.message,
1053
+ ...details.type ? { errorType: details.type } : {}
1054
+ } : {};
1055
+ }
1056
+ var import_node_crypto22 = require("crypto");
1057
+ function emitWorkflowCall2(telemetry, type, payload) {
1058
+ const base = telemetry?.correlation;
1059
+ if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
1060
+ return;
1061
+ try {
1062
+ const active = telemetry.trace?.getActiveContext?.();
1063
+ void Promise.resolve(
1064
+ telemetry.reporter.emit([
1065
+ {
1066
+ schemaVersion: 1,
1067
+ eventId: (0, import_node_crypto22.randomUUID)(),
1068
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
1069
+ environment: telemetry.environment,
1070
+ service: telemetry.service,
1071
+ correlation: {
1072
+ ...base,
1073
+ runId: base.runId,
1074
+ workflowId: base.workflowId,
1075
+ ...active
1076
+ },
1077
+ type,
1078
+ payload
1079
+ }
1080
+ ])
1081
+ ).catch(() => {
1082
+ });
1083
+ } catch {
1084
+ }
1085
+ }
1086
+ function workflowCallContent(telemetry, side, value) {
1087
+ const policy = telemetry?.captureContent;
1088
+ if (!(policy === true || policy && typeof policy === "object" && policy[side]))
1089
+ return {};
1090
+ const serialized = JSON.stringify(value);
1091
+ return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
1092
+ }
1093
+ var tools = new import_node_async_hooks2.AsyncLocalStorage();
1094
+ function classification(tool, value, thrown, policy = tool.outcomes) {
1095
+ const fallback = {
1096
+ outcome: thrown ? "fault" : "success"
1097
+ };
1098
+ try {
1099
+ if (value && typeof value === "object" && "isError" in value && value.isError === true)
1100
+ fallback.outcome = "fault";
1101
+ const result = thrown ? policy?.classifyError?.(value) : policy?.classifyResult?.(value);
1102
+ if (!result || !["success", "denied", "fault"].includes(result.outcome))
1103
+ return fallback;
1104
+ if (result.outcome !== "denied")
1105
+ return thrown || fallback.outcome === "fault" ? fallback : { outcome: result.outcome };
1106
+ const code = result.code;
1107
+ return {
1108
+ outcome: "denied",
1109
+ code: typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code) && policy?.denialCodes?.includes(code) ? code : "DENIED"
1110
+ };
1111
+ } catch {
1112
+ return fallback;
1113
+ }
1114
+ }
1115
+ async function closeOwnedTools(owned) {
1116
+ const closers = /* @__PURE__ */ new Map();
1117
+ for (const tool of owned) {
1118
+ if (tool.closeAfterUse !== false && tool.close && !closers.has(tool.close))
1119
+ closers.set(tool.close, tool);
1120
+ }
1121
+ await Promise.all(
1122
+ [...closers].map(async ([close, tool]) => {
1123
+ try {
1124
+ await close.call(tool);
1125
+ } catch {
1126
+ const ctx = getHookContext();
1127
+ try {
1128
+ ctx.reasonTrace?.startSpan({
1129
+ name: "kortyx.tool.cleanup",
1130
+ attributes: { cleanupFailed: true }
1131
+ })?.end?.();
1132
+ } catch {
1133
+ }
1134
+ }
1135
+ })
1136
+ );
1137
+ }
1138
+ function captureToolError(observation, error, policy, returned = false) {
1139
+ try {
1140
+ const reported = returned && error && typeof error === "object" && "isError" in error && error.isError === true && "content" in error && typeof error.content === "string" ? { name: "ToolError", message: error.content } : error;
1141
+ Object.assign(
1142
+ observation,
1143
+ errorDiagnostics(policy?.error ? error : reported, policy?.error)
1144
+ );
1145
+ } catch {
1146
+ }
1147
+ }
1148
+ function ownership() {
1149
+ const ctx = getHookContext();
1150
+ const correlation = ctx.node.workflowCallTelemetry?.correlation;
1151
+ return {
1152
+ workflowId: ctx.node.graph.name,
1153
+ nodeId: ctx.node.graph.node,
1154
+ ...correlation?.workflowRevisionId ? { workflowRevisionId: correlation.workflowRevisionId } : {},
1155
+ ...correlation?.parentInvocationId ? { parentInvocationId: correlation.parentInvocationId } : {},
1156
+ ...correlation?.runId ? { runId: correlation.runId } : {},
1157
+ ...correlation?.invocationId ? { invocationId: correlation.invocationId } : {},
1158
+ ...correlation?.branchId ? { branchId: correlation.branchId } : {}
1159
+ };
1160
+ }
1161
+ function rememberToolObservation(observation) {
1162
+ const ctx = getHookContext();
1163
+ const existing = ctx.workflowState.__kortyxToolObservations;
1164
+ const observations = Array.isArray(existing) ? existing : [];
1165
+ if (!observations.some((item) => item.attemptId === observation.attemptId))
1166
+ observations.push({ ...observation });
1167
+ ctx.workflowState.__kortyxToolObservations = observations;
1168
+ ctx.stateDirty = true;
1169
+ }
1170
+ function toolTrace(observation) {
1171
+ const ctx = getHookContext();
1172
+ const adapter = ctx.reasonTrace ?? ctx.node.workflowCallTelemetry?.trace;
1173
+ if (adapter) return adapter;
1174
+ const telemetry = ctx.node.workflowCallTelemetry;
1175
+ if (!telemetry?.reporter) return void 0;
1176
+ const correlation = { ...telemetry.correlation, ...observation };
1177
+ const spanId = (0, import_node_crypto32.randomUUID)();
1178
+ const emit = (type, payload) => emitWorkflowCall2(
1179
+ {
1180
+ ...telemetry,
1181
+ correlation,
1182
+ trace: {
1183
+ startSpan: () => void 0,
1184
+ getActiveContext: () => ({
1185
+ traceId: correlation.runId ?? spanId,
1186
+ spanId
1187
+ })
1188
+ }
1189
+ },
1190
+ type,
1191
+ payload
1192
+ );
1193
+ return {
1194
+ startSpan: ({ attributes = {} }) => {
1195
+ if (attributes.executed === true) emit("tool.started", attributes);
1196
+ let ended = false;
1197
+ return {
1198
+ end: (args) => {
1199
+ if (ended) return;
1200
+ ended = true;
1201
+ const payload = { ...attributes, ...args?.attributes };
1202
+ const type = payload.observationKind === "reused" ? "tool.reused" : payload.observationKind === "waiting" ? "tool.waiting" : payload.suspended ? "tool.suspended" : payload.outcome === "denied" ? "tool.denied" : payload.outcome === "fault" ? "tool.failed" : payload.outcome === "cancelled" ? "tool.cancelled" : "tool.completed";
1203
+ emit(type, payload);
1204
+ }
1205
+ };
1206
+ }
1207
+ };
1208
+ }
1209
+ async function executeObservedTool(args) {
1210
+ const ctx = getHookContext();
1211
+ const observation = {
1212
+ version: 1,
1213
+ name: args.tool.name,
1214
+ toolCallId: args.toolCallId,
1215
+ attemptId: (0, import_node_crypto32.randomUUID)(),
1216
+ callingMode: args.callingMode,
1217
+ executed: false,
1218
+ ...ownership()
1219
+ };
1220
+ const scope = {
1221
+ tool: args.tool,
1222
+ input: args.input,
1223
+ signal: args.abortSignal,
1224
+ mode: args.callingMode,
1225
+ providerToolCallId: args.providerToolCallId ?? args.toolCallId,
1226
+ adopted: false
1227
+ };
1228
+ try {
1229
+ (0, import_core22.throwIfExecutionAborted)(args.abortSignal);
1230
+ } catch (error) {
1231
+ observation.outcome = "cancelled";
1232
+ await observeToolFact(observation);
1233
+ args.onObservation?.(observation);
1234
+ throw error;
1235
+ }
1236
+ ctx.node.consumeExecution?.("maxToolCalls");
1237
+ observation.executed = true;
1238
+ const started = performance.now();
1239
+ return withSafeTraceSpan2(
1240
+ toolTrace(observation),
1241
+ {
1242
+ name: "kortyx.tool",
1243
+ attributes: { ...observation, nodeId: ctx.node.graph.node }
1244
+ },
1245
+ async (span) => tools.run(scope, async () => {
1246
+ try {
1247
+ const result = await args.tool.execute(args.input, {
1248
+ toolCallId: args.providerToolCallId ?? args.toolCallId,
1249
+ ...args.abortSignal ? { abortSignal: args.abortSignal } : {}
1250
+ });
1251
+ (0, import_core22.throwIfExecutionAborted)(args.abortSignal);
1252
+ const outcome = classification(
1253
+ args.tool,
1254
+ result,
1255
+ false,
1256
+ scope.classify ?? args.tool.outcomes
1257
+ );
1258
+ observation.outcome = outcome.outcome;
1259
+ if (outcome.code) observation.denialCode = outcome.code;
1260
+ if (observation.outcome === "fault") {
1261
+ captureToolError(
1262
+ observation,
1263
+ result,
1264
+ scope.telemetry ?? args.tool.telemetry,
1265
+ true
1266
+ );
1267
+ }
1268
+ return result;
1269
+ } catch (error) {
1270
+ if (args.abortSignal?.aborted) observation.outcome = "cancelled";
1271
+ else if (!(0, import_errors32.isControlFlowError)(error)) {
1272
+ const outcome = classification(
1273
+ args.tool,
1274
+ error,
1275
+ true,
1276
+ scope.classify ?? args.tool.outcomes
1277
+ );
1278
+ observation.outcome = outcome.outcome;
1279
+ if (outcome.code) observation.denialCode = outcome.code;
1280
+ if (observation.outcome === "fault")
1281
+ captureToolError(
1282
+ observation,
1283
+ error,
1284
+ scope.telemetry ?? args.tool.telemetry
1285
+ );
1286
+ }
1287
+ throw error;
1288
+ } finally {
1289
+ observation.durationMs = Math.max(0, performance.now() - started);
1290
+ span.end?.({
1291
+ attributes: {
1292
+ ...observation,
1293
+ ...observation.outcome ? {} : { suspended: true }
1294
+ }
1295
+ });
1296
+ rememberToolObservation(observation);
1297
+ args.onObservation?.({ ...observation });
1298
+ }
1299
+ })
1300
+ );
1301
+ }
1302
+ async function observeToolFact(observation, kind, preserveOwner = false) {
1303
+ const ctx = getHookContext();
1304
+ if (!preserveOwner) Object.assign(observation, ownership());
1305
+ if (observation.outcome && kind !== "reused")
1306
+ rememberToolObservation(observation);
1307
+ await withSafeTraceSpan2(
1308
+ toolTrace(observation),
1309
+ {
1310
+ name: "kortyx.tool",
1311
+ attributes: {
1312
+ ...observation,
1313
+ executed: false,
1314
+ ...kind ? { observationKind: kind } : {},
1315
+ nodeId: observation.nodeId ?? ctx.node.graph.node
1316
+ }
1317
+ },
1318
+ async (span) => {
1319
+ span.end?.({
1320
+ attributes: {
1321
+ ...observation,
1322
+ executed: false,
1323
+ ...kind ? { observationKind: kind } : {}
1324
+ }
1325
+ });
1326
+ }
1327
+ );
1328
+ }
1329
+ async function useTool(args) {
1330
+ const ctx = getHookContext();
1331
+ const scope = tools.getStore();
1332
+ if (scope?.mode === "model" && !scope.adopted && scope.tool.name === args.tool.name && scope.input === args.input) {
1333
+ scope.adopted = true;
1334
+ scope.classify = args.tool.outcomes ?? scope.classify;
1335
+ scope.telemetry = args.tool.telemetry ?? scope.telemetry;
1336
+ return tools.run(
1337
+ { ...scope, mode: "direct" },
1338
+ () => args.tool.execute(args.input, {
1339
+ toolCallId: scope.providerToolCallId,
1340
+ ...scope.signal ? { abortSignal: scope.signal } : {}
1341
+ })
1342
+ );
1343
+ }
1344
+ const index = ctx.toolCallIndex++;
1345
+ const key = `__useTool:${args.id ?? `auto:${index}`}`;
1346
+ let id = ctx.currentNodeState.byKey[key];
1347
+ if (typeof id !== "string") {
1348
+ id = (0, import_node_crypto32.randomUUID)();
1349
+ ctx.currentNodeState.byKey[key] = id;
1350
+ ctx.stateDirty = true;
1351
+ }
1352
+ try {
1353
+ return await executeObservedTool({
1354
+ tool: args.tool,
1355
+ input: args.input,
1356
+ toolCallId: String(id),
1357
+ callingMode: "direct",
1358
+ abortSignal: (0, import_core22.combineAbortSignals)(ctx.node.abortSignal, args.abortSignal)
1359
+ });
1360
+ } finally {
1361
+ await closeOwnedTools([args.tool]);
1362
+ }
1363
+ }
1364
+ async function replayToolObservations(observations, child = false) {
1365
+ for (const original of observations) {
1366
+ const source = original.source ?? {
1367
+ ...ownershipFrom(original),
1368
+ toolCallId: original.toolCallId,
1369
+ attemptId: original.attemptId
1370
+ };
1371
+ await observeToolFact(
1372
+ {
1373
+ ...original,
1374
+ ...ownership(),
1375
+ ...child ? {
1376
+ workflowId: original.workflowId,
1377
+ nodeId: original.nodeId,
1378
+ workflowRevisionId: original.workflowRevisionId,
1379
+ invocationId: original.invocationId,
1380
+ parentInvocationId: original.parentInvocationId
1381
+ } : {},
1382
+ attemptId: (0, import_node_crypto32.randomUUID)(),
1383
+ executed: false,
1384
+ source,
1385
+ durationMs: void 0
1386
+ },
1387
+ "reused",
1388
+ child
1389
+ );
1390
+ }
1391
+ }
1392
+ var ownershipFrom = (observation) => ({
1393
+ runId: observation.runId,
1394
+ invocationId: observation.invocationId,
1395
+ branchId: observation.branchId
1396
+ });
1397
+ var import_node_crypto5 = require("crypto");
1398
+ var import_core42 = require("@kortyx/core");
1399
+ var import_node_crypto42 = require("crypto");
448
1400
  var import_core32 = require("@kortyx/core");
449
- var import_node_crypto22 = require("crypto");
450
- var import_core22 = require("@kortyx/core");
451
- var import_errors22 = require("@kortyx/core/errors");
1401
+ var import_errors42 = require("@kortyx/core/errors");
452
1402
  function assertModelFinish(result) {
453
1403
  if (result.finishReason?.unified !== "content-filter") return;
454
1404
  throw Object.assign(
455
- new import_errors22.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
1405
+ new import_errors42.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
456
1406
  category: "provider",
457
1407
  retryable: false,
458
1408
  safeMessage: "The provider refused the request."
@@ -466,7 +1416,7 @@ var require_dist = __commonJS({
466
1416
  var toError = (value) => value instanceof Error ? value : new Error(String(value));
467
1417
  var createRuntimeId = () => {
468
1418
  try {
469
- return (0, import_node_crypto22.randomUUID)();
1419
+ return (0, import_node_crypto42.randomUUID)();
470
1420
  } catch {
471
1421
  return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
472
1422
  }
@@ -501,8 +1451,8 @@ var require_dist = __commonJS({
501
1451
  args.model.options?.providerOptions,
502
1452
  args.providerOptions
503
1453
  );
504
- const tools = args.tools ?? args.model.options?.tools;
505
- (0, import_core22.throwIfExecutionAborted)(abortSignal);
1454
+ const tools2 = args.tools ?? args.model.options?.tools;
1455
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
506
1456
  const model = args.model.provider.getModel(args.model.modelId, {
507
1457
  ...temperature !== void 0 ? { temperature } : {},
508
1458
  streaming: stream,
@@ -511,10 +1461,10 @@ var require_dist = __commonJS({
511
1461
  ...abortSignal !== void 0 ? { abortSignal } : {},
512
1462
  ...reasoning !== void 0 ? { reasoning } : {},
513
1463
  ...responseFormat !== void 0 ? { responseFormat } : {},
514
- ...tools !== void 0 ? { tools } : {},
1464
+ ...tools2 !== void 0 ? { tools: tools2 } : {},
515
1465
  ...providerOptions !== void 0 ? { providerOptions } : {}
516
1466
  });
517
- if (tools?.length && !model.supportsToolStreaming) stream = false;
1467
+ if (tools2?.length && !model.supportsToolStreaming) stream = false;
518
1468
  const messages = args.messages !== void 0 ? args.messages : [
519
1469
  ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
520
1470
  { role: "user", content: String(args.input ?? "") }
@@ -526,7 +1476,7 @@ var require_dist = __commonJS({
526
1476
  opId,
527
1477
  segmentId
528
1478
  };
529
- const traceSpan = args.reasonTrace?.startSpan({
1479
+ const traceSpan = safeStartSpan2(args.reasonTrace, {
530
1480
  name: "runReasonEngine",
531
1481
  attributes: {
532
1482
  ...commonMeta,
@@ -606,7 +1556,7 @@ var require_dist = __commonJS({
606
1556
  }
607
1557
  const response2 = await model.stream(messages);
608
1558
  for await (const chunk of response2) {
609
- (0, import_core22.throwIfExecutionAborted)(abortSignal);
1559
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
610
1560
  switch (chunk.type) {
611
1561
  case "text-delta": {
612
1562
  if (chunk.delta.length === 0) break;
@@ -672,7 +1622,7 @@ var require_dist = __commonJS({
672
1622
  }
673
1623
  }
674
1624
  }
675
- (0, import_core22.throwIfExecutionAborted)(abortSignal);
1625
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
676
1626
  assertModelFinish({ finishReason, usage });
677
1627
  if (emit) {
678
1628
  emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
@@ -689,12 +1639,12 @@ var require_dist = __commonJS({
689
1639
  ...providerMetadata !== void 0 ? { providerMetadata } : {},
690
1640
  ...warnings !== void 0 ? { warnings } : {}
691
1641
  };
692
- (0, import_core22.throwIfExecutionAborted)(abortSignal);
1642
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
693
1643
  endTrace(traceSpan, result2);
694
1644
  return result2;
695
1645
  }
696
1646
  const response = await model.invoke(messages);
697
- (0, import_core22.throwIfExecutionAborted)(abortSignal);
1647
+ (0, import_core32.throwIfExecutionAborted)(abortSignal);
698
1648
  assertModelFinish(response);
699
1649
  if (response.content) args.onTextChunk?.(response.content);
700
1650
  if (emit) {
@@ -724,26 +1674,26 @@ var require_dist = __commonJS({
724
1674
  endTrace(traceSpan, result);
725
1675
  return result;
726
1676
  } catch (error) {
727
- const failure = abortSignal?.aborted ? (0, import_core22.createExecutionCancelledError)() : error;
1677
+ const failure = abortSignal?.aborted ? (0, import_core32.createExecutionCancelledError)() : error;
728
1678
  failTrace(traceSpan, failure);
729
1679
  throw failure;
730
1680
  }
731
1681
  }
732
1682
  var createRuntimeId2 = () => {
733
1683
  try {
734
- return (0, import_node_crypto32.randomUUID)();
1684
+ return (0, import_node_crypto5.randomUUID)();
735
1685
  } catch {
736
1686
  return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
737
1687
  }
738
1688
  };
739
1689
  async function reasonEngine(args, meta, inputOverride) {
740
1690
  const ctx = getHookContext();
741
- const abortSignal = (0, import_core32.combineAbortSignals)(
1691
+ const abortSignal = (0, import_core42.combineAbortSignals)(
742
1692
  ctx.node.abortSignal,
743
1693
  args.abortSignal,
744
1694
  args.model.options?.abortSignal
745
1695
  );
746
- (0, import_core32.throwIfExecutionAborted)(abortSignal);
1696
+ (0, import_core42.throwIfExecutionAborted)(abortSignal);
747
1697
  ctx.node.consumeExecution?.("maxModelPasses");
748
1698
  try {
749
1699
  return await runReasonEngine({
@@ -1485,7 +2435,7 @@ var require_dist = __commonJS({
1485
2435
  }
1486
2436
  };
1487
2437
  }
1488
- var import_errors32 = require("@kortyx/core/errors");
2438
+ var import_errors52 = require("@kortyx/core/errors");
1489
2439
  var tryJsonParse = (value) => {
1490
2440
  try {
1491
2441
  return { ok: true, value: JSON.parse(value) };
@@ -1523,12 +2473,12 @@ var require_dist = __commonJS({
1523
2473
  }
1524
2474
  return text;
1525
2475
  };
1526
- var createTruncatedStructuredOutputError = (label, cause) => new import_errors32.ValidationError(
2476
+ var createTruncatedStructuredOutputError = (label, cause) => new import_errors52.ValidationError(
1527
2477
  "OUTPUT_TRUNCATED",
1528
2478
  `${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`,
1529
2479
  cause
1530
2480
  );
1531
- var createInvalidStructuredOutputError = (label, cause) => new import_errors32.ValidationError(
2481
+ var createInvalidStructuredOutputError = (label, cause) => new import_errors52.ValidationError(
1532
2482
  "INVALID_MODEL_JSON",
1533
2483
  `${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check outputSchema and provider structured-output settings.`,
1534
2484
  cause
@@ -1540,7 +2490,7 @@ var require_dist = __commonJS({
1540
2490
  try {
1541
2491
  return parseWithSchema(args.schema, candidate, args.label);
1542
2492
  } catch (error) {
1543
- if (!(error instanceof import_errors32.ValidationError)) throw error;
2493
+ if (!(error instanceof import_errors52.ValidationError)) throw error;
1544
2494
  const failure = args.finishReason?.unified === "length" ? createTruncatedStructuredOutputError(args.label, error) : typeof candidate === "string" && !validJson ? createInvalidStructuredOutputError(args.label, error) : error;
1545
2495
  if (failure && typeof failure === "object")
1546
2496
  Object.assign(failure, {
@@ -1560,7 +2510,7 @@ var require_dist = __commonJS({
1560
2510
  if (typeof candidate === "string" && candidate.trim().length > 0) {
1561
2511
  throw createInvalidStructuredOutputError("useReason output");
1562
2512
  }
1563
- throw new import_errors32.ValidationError(
2513
+ throw new import_errors52.ValidationError(
1564
2514
  "MODEL_OUTPUT_SCHEMA",
1565
2515
  "useReason first pass with interrupt must return a JSON object."
1566
2516
  );
@@ -1569,7 +2519,7 @@ var require_dist = __commonJS({
1569
2519
  let interruptRequired = true;
1570
2520
  if (mode === "optional") {
1571
2521
  if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
1572
- throw new import_errors32.ValidationError(
2522
+ throw new import_errors52.ValidationError(
1573
2523
  "MODEL_OUTPUT_SCHEMA",
1574
2524
  'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
1575
2525
  );
@@ -1582,7 +2532,7 @@ var require_dist = __commonJS({
1582
2532
  candidate.interrupt
1583
2533
  ].some((value) => value !== null && value !== void 0);
1584
2534
  if (hasUnexpectedRequest) {
1585
- throw new import_errors32.ValidationError(
2535
+ throw new import_errors52.ValidationError(
1586
2536
  "MODEL_OUTPUT_SCHEMA",
1587
2537
  'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
1588
2538
  );
@@ -1616,7 +2566,7 @@ var require_dist = __commonJS({
1616
2566
  "useReason output"
1617
2567
  );
1618
2568
  } catch (error) {
1619
- if (!(error instanceof import_errors32.ValidationError)) throw error;
2569
+ if (!(error instanceof import_errors52.ValidationError)) throw error;
1620
2570
  if (args.finishReason?.unified === "length") {
1621
2571
  throw createTruncatedStructuredOutputError("useReason output", error);
1622
2572
  }
@@ -1625,7 +2575,7 @@ var require_dist = __commonJS({
1625
2575
  }
1626
2576
  const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
1627
2577
  if (!args.outputSchema && !explicitDraft) {
1628
- throw new import_errors32.ValidationError(
2578
+ throw new import_errors52.ValidationError(
1629
2579
  "MODEL_OUTPUT_SCHEMA",
1630
2580
  "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
1631
2581
  );
@@ -1811,6 +2761,7 @@ ${tryStringify2(args.response)}`
1811
2761
  const reasoning = sum(left.reasoning, right.reasoning);
1812
2762
  const cacheRead = sum(left.cacheRead, right.cacheRead);
1813
2763
  const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
2764
+ const cacheWrite1h = sum(left.cacheWrite1h, right.cacheWrite1h);
1814
2765
  const merged = {
1815
2766
  ...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
1816
2767
  ...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
@@ -1822,6 +2773,7 @@ ${tryStringify2(args.response)}`
1822
2773
  if (reasoning !== void 0) merged.reasoning = reasoning;
1823
2774
  if (cacheRead !== void 0) merged.cacheRead = cacheRead;
1824
2775
  if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
2776
+ if (cacheWrite1h !== void 0) merged.cacheWrite1h = cacheWrite1h;
1825
2777
  if (raw) merged.raw = raw;
1826
2778
  return merged;
1827
2779
  };
@@ -1858,9 +2810,9 @@ ${tryStringify2(args.response)}`
1858
2810
  streamId: args.opId
1859
2811
  });
1860
2812
  };
1861
- var import_core42 = require("@kortyx/core");
1862
- var import_errors42 = require("@kortyx/core/errors");
1863
- var toToolDefinitions = (tools) => tools.map((tool) => ({
2813
+ var import_core52 = require("@kortyx/core");
2814
+ var import_errors62 = require("@kortyx/core/errors");
2815
+ var toToolDefinitions = (tools2) => tools2.map((tool) => ({
1864
2816
  name: tool.name,
1865
2817
  ...tool.title ? { title: tool.title } : {},
1866
2818
  ...tool.description ? { description: tool.description } : {},
@@ -1877,12 +2829,13 @@ ${tryStringify2(args.response)}`
1877
2829
  return fallback;
1878
2830
  };
1879
2831
  var normalizeToolResult = (toolCall, value) => {
1880
- if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
1881
- return value;
2832
+ if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value && typeof value.content === "string") {
2833
+ const result = value;
2834
+ return { ...result, toolCallId: toolCall.id, name: toolCall.name };
1882
2835
  }
1883
2836
  let content;
1884
2837
  try {
1885
- content = typeof value === "string" ? value : JSON.stringify(value);
2838
+ content = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
1886
2839
  } catch {
1887
2840
  content = String(value);
1888
2841
  }
@@ -1893,28 +2846,6 @@ ${tryStringify2(args.response)}`
1893
2846
  ...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
1894
2847
  };
1895
2848
  };
1896
- var closeOwnedTools = async (tools, preserveFailure = false, traceSpan) => {
1897
- const closeFns = /* @__PURE__ */ new Set();
1898
- for (const tool of tools) {
1899
- if (tool.closeAfterUse === false || !tool.close) continue;
1900
- closeFns.add(tool.close);
1901
- }
1902
- try {
1903
- await Promise.all(
1904
- [...closeFns].map(async (close) => {
1905
- await close();
1906
- })
1907
- );
1908
- } catch (error) {
1909
- if (!preserveFailure) throw error;
1910
- try {
1911
- traceSpan?.addEvent?.("useReason.cleanup.failed", {
1912
- failure: (0, import_errors42.serializeFailure)(error)
1913
- });
1914
- } catch {
1915
- }
1916
- }
1917
- };
1918
2849
  var createInitialMessages = (args) => [
1919
2850
  ...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
1920
2851
  { role: "user", content: String(args.input ?? "") }
@@ -1922,7 +2853,7 @@ ${tryStringify2(args.response)}`
1922
2853
  var runReasonToolLoop = async (args) => {
1923
2854
  const { useReasonArgs, id, traceSpan, checkpointKey } = args;
1924
2855
  const ctx = getHookContext();
1925
- const abortSignal = (0, import_core42.combineAbortSignals)(
2856
+ const abortSignal = (0, import_core52.combineAbortSignals)(
1926
2857
  ctx.node.abortSignal,
1927
2858
  useReasonArgs.abortSignal,
1928
2859
  useReasonArgs.model.options?.abortSignal
@@ -1930,43 +2861,32 @@ ${tryStringify2(args.response)}`
1930
2861
  const saved = ctx.currentNodeState.byKey[checkpointKey];
1931
2862
  const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
1932
2863
  const opId = checkpoint?.opId ?? args.opId;
1933
- let primaryFailure = false;
1934
- const tools = useReasonArgs.tools ?? [];
2864
+ const tools2 = useReasonArgs.tools ?? [];
1935
2865
  const toolByName = /* @__PURE__ */ new Map();
1936
- let validationCompleted = false;
1937
2866
  let separateOutput = false;
1938
- try {
1939
- for (const tool of tools) {
1940
- if (toolByName.has(tool.name)) {
1941
- throw new Error(
1942
- `useReason received duplicate tool name "${tool.name}".`
1943
- );
1944
- }
1945
- toolByName.set(tool.name, tool);
2867
+ for (const tool of tools2) {
2868
+ if (toolByName.has(tool.name)) {
2869
+ throw new Error(`useReason received duplicate tool name "${tool.name}".`);
1946
2870
  }
1947
- if (useReasonArgs.interrupt) {
1948
- throw new Error(
1949
- "useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
1950
- );
1951
- }
1952
- separateOutput = Boolean(
1953
- useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
1954
- ...useReasonArgs.model.options,
1955
- ...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
1956
- ...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
1957
- providerOptions: resolveProviderOptions(
1958
- useReasonArgs.model.options?.providerOptions,
1959
- useReasonArgs.providerOptions
1960
- ) ?? {}
1961
- }).requiresSeparateStructuredOutput
2871
+ toolByName.set(tool.name, tool);
2872
+ }
2873
+ if (useReasonArgs.interrupt) {
2874
+ throw new Error(
2875
+ "useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
1962
2876
  );
1963
- validationCompleted = true;
1964
- } finally {
1965
- if (!validationCompleted) {
1966
- await closeOwnedTools(tools, true, traceSpan);
1967
- }
1968
2877
  }
1969
- const toolDefinitions = toToolDefinitions(tools);
2878
+ separateOutput = Boolean(
2879
+ useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
2880
+ ...useReasonArgs.model.options,
2881
+ ...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
2882
+ ...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
2883
+ providerOptions: resolveProviderOptions(
2884
+ useReasonArgs.model.options?.providerOptions,
2885
+ useReasonArgs.providerOptions
2886
+ ) ?? {}
2887
+ }).requiresSeparateStructuredOutput
2888
+ );
2889
+ const toolDefinitions = toToolDefinitions(tools2);
1970
2890
  let finalizing = checkpoint?.finalizing ?? false;
1971
2891
  let completed = false;
1972
2892
  const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
@@ -2018,7 +2938,7 @@ ${tryStringify2(args.response)}`
2018
2938
  };
2019
2939
  try {
2020
2940
  for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
2021
- (0, import_core42.throwIfExecutionAborted)(abortSignal);
2941
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
2022
2942
  traceSpan?.addEvent?.("useReason.tool-step.start", {
2023
2943
  stepIndex,
2024
2944
  toolCount: toolDefinitions.length
@@ -2062,6 +2982,12 @@ ${tryStringify2(args.response)}`
2062
2982
  );
2063
2983
  aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
2064
2984
  const toolCalls = step.toolCalls ?? [];
2985
+ const ids = /* @__PURE__ */ new Set();
2986
+ for (const call of toolCalls) {
2987
+ if (!call.id || ids.has(call.id) || !reused && allToolCalls.some((prior) => prior.id === call.id))
2988
+ throw new Error("Model returned duplicate or empty tool call IDs.");
2989
+ ids.add(call.id);
2990
+ }
2065
2991
  const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
2066
2992
  if (!reused)
2067
2993
  steps.push({
@@ -2130,9 +3056,15 @@ ${tryStringify2(args.response)}`
2130
3056
  pending = step;
2131
3057
  save();
2132
3058
  for (const toolCall of toolCalls) {
2133
- if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
3059
+ if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
3060
+ await replayToolObservations(
3061
+ steps[stepIndex]?.toolObservations?.filter(
3062
+ (observation) => observation.toolCallId === `${opId}:${toolCall.id}`
3063
+ ) ?? []
3064
+ );
2134
3065
  continue;
2135
- (0, import_core42.throwIfExecutionAborted)(abortSignal);
3066
+ }
3067
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
2136
3068
  const tool = toolByName.get(toolCall.name);
2137
3069
  if (!tool) {
2138
3070
  throw new Error(
@@ -2149,11 +3081,6 @@ ${tryStringify2(args.response)}`
2149
3081
  tool.name,
2150
3082
  false
2151
3083
  );
2152
- traceSpan?.addEvent?.("useReason.tool-call.start", {
2153
- stepIndex,
2154
- tool: tool.name,
2155
- toolCallId: toolCall.id
2156
- });
2157
3084
  if (shouldEmitTool) {
2158
3085
  emitToolEvent("tool-call-start", {
2159
3086
  tool: tool.name,
@@ -2162,6 +3089,17 @@ ${tryStringify2(args.response)}`
2162
3089
  });
2163
3090
  }
2164
3091
  if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
3092
+ await observeToolFact(
3093
+ {
3094
+ version: 1,
3095
+ name: tool.name,
3096
+ toolCallId: `${opId}:${toolCall.id}`,
3097
+ attemptId: `${opId}:${toolCall.id}:approval`,
3098
+ callingMode: "model",
3099
+ executed: false
3100
+ },
3101
+ "waiting"
3102
+ );
2165
3103
  const approval = await awaitInterruptInternal({
2166
3104
  id: `tool:${toolCall.id}`,
2167
3105
  request: {
@@ -2180,6 +3118,22 @@ ${tryStringify2(args.response)}`
2180
3118
  });
2181
3119
  const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
2182
3120
  if (!approved) {
3121
+ const deniedObservation = {
3122
+ version: 1,
3123
+ name: tool.name,
3124
+ toolCallId: `${opId}:${toolCall.id}`,
3125
+ attemptId: `${opId}:${toolCall.id}:approval`,
3126
+ callingMode: "model",
3127
+ executed: false,
3128
+ outcome: "denied",
3129
+ denialCode: "APPROVAL_DENIED"
3130
+ };
3131
+ await observeToolFact(deniedObservation);
3132
+ const entry = steps[stepIndex];
3133
+ if (entry) {
3134
+ entry.toolObservations ??= [];
3135
+ entry.toolObservations.push(deniedObservation);
3136
+ }
2183
3137
  const denied = {
2184
3138
  toolCallId: toolCall.id,
2185
3139
  name: tool.name,
@@ -2210,13 +3164,24 @@ ${tryStringify2(args.response)}`
2210
3164
  save();
2211
3165
  }
2212
3166
  try {
2213
- (0, import_core42.throwIfExecutionAborted)(abortSignal);
2214
- ctx.node.consumeExecution?.("maxToolCalls");
2215
- const rawResult = await tool.execute(toolCall.input, {
2216
- toolCallId: toolCall.id,
2217
- ...abortSignal ? { abortSignal } : {}
3167
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
3168
+ const rawResult = await executeObservedTool({
3169
+ tool,
3170
+ input: toolCall.input,
3171
+ toolCallId: `${opId}:${toolCall.id}`,
3172
+ providerToolCallId: toolCall.id,
3173
+ callingMode: "model",
3174
+ abortSignal,
3175
+ onObservation: (observation) => {
3176
+ const entry = steps[stepIndex];
3177
+ if (entry) {
3178
+ entry.toolObservations ??= [];
3179
+ entry.toolObservations.push(observation);
3180
+ save();
3181
+ }
3182
+ }
2218
3183
  });
2219
- (0, import_core42.throwIfExecutionAborted)(abortSignal);
3184
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
2220
3185
  const result2 = normalizeToolResult(toolCall, rawResult);
2221
3186
  toolResults.push(result2);
2222
3187
  allToolResults.push(result2);
@@ -2229,20 +3194,14 @@ ${tryStringify2(args.response)}`
2229
3194
  ...result2.isError !== void 0 ? { isError: result2.isError } : {}
2230
3195
  });
2231
3196
  }
2232
- traceSpan?.addEvent?.("useReason.tool-call.complete", {
2233
- stepIndex,
2234
- tool: result2.name,
2235
- toolCallId: result2.toolCallId,
2236
- isError: Boolean(result2.isError)
2237
- });
2238
3197
  } catch (error) {
2239
- (0, import_core42.throwIfExecutionAborted)(abortSignal);
2240
- if ((0, import_errors42.isControlFlowError)(error)) throw error;
3198
+ (0, import_core52.throwIfExecutionAborted)(abortSignal);
3199
+ if ((0, import_errors62.isControlFlowError)(error)) throw error;
2241
3200
  const result2 = {
2242
3201
  toolCallId: toolCall.id,
2243
3202
  name: tool.name,
2244
- content: (0, import_errors42.serializeFailure)(error).message,
2245
- failure: (0, import_errors42.serializeFailure)(error),
3203
+ content: (0, import_errors62.serializeFailure)(error).message,
3204
+ failure: (0, import_errors62.serializeFailure)(error),
2246
3205
  isError: true
2247
3206
  };
2248
3207
  toolResults.push(result2);
@@ -2255,13 +3214,6 @@ ${tryStringify2(args.response)}`
2255
3214
  failure: result2.failure
2256
3215
  });
2257
3216
  }
2258
- traceSpan?.addEvent?.("useReason.tool-call.error", {
2259
- stepIndex,
2260
- tool: tool.name,
2261
- toolCallId: toolCall.id,
2262
- message: result2.content,
2263
- failure: result2.failure
2264
- });
2265
3217
  }
2266
3218
  const completed2 = toolResults.at(-1);
2267
3219
  if (completed2)
@@ -2334,7 +3286,6 @@ ${tryStringify2(args.response)}`
2334
3286
  ctx.stateDirty = true;
2335
3287
  return result;
2336
3288
  } catch (error) {
2337
- primaryFailure = true;
2338
3289
  try {
2339
3290
  traceSpan?.fail?.(error, {
2340
3291
  attributes: {
@@ -2345,8 +3296,6 @@ ${tryStringify2(args.response)}`
2345
3296
  } catch {
2346
3297
  }
2347
3298
  throw error;
2348
- } finally {
2349
- await closeOwnedTools(tools, primaryFailure, traceSpan);
2350
3299
  }
2351
3300
  };
2352
3301
  var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
@@ -2360,15 +3309,15 @@ ${tryStringify2(args.response)}`
2360
3309
  "useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
2361
3310
  );
2362
3311
  };
2363
- async function useReason(args) {
3312
+ async function runReason(args) {
2364
3313
  assertReasoningThoughtsCompatibility(args);
2365
3314
  const ctx = getHookContext();
2366
- const abortSignal = (0, import_core52.combineAbortSignals)(
3315
+ const abortSignal = (0, import_core62.combineAbortSignals)(
2367
3316
  ctx.node.abortSignal,
2368
3317
  args.abortSignal,
2369
3318
  args.model.options?.abortSignal
2370
3319
  );
2371
- (0, import_core52.throwIfExecutionAborted)(abortSignal);
3320
+ (0, import_core62.throwIfExecutionAborted)(abortSignal);
2372
3321
  const allowValidatedToolOutput = Boolean(
2373
3322
  args.outputSchema && !(args.responseFormat ?? args.model.options?.responseFormat)
2374
3323
  );
@@ -2391,286 +3340,303 @@ ${tryStringify2(args.response)}`
2391
3340
  ctx.currentNodeState.byKey[checkpointKey]
2392
3341
  );
2393
3342
  if (existingCompleted) {
3343
+ await replayToolObservations(
3344
+ existingCompleted.result.steps?.flatMap(
3345
+ (step) => step.toolObservations ?? []
3346
+ ) ?? []
3347
+ );
2394
3348
  return existingCompleted.result;
2395
3349
  }
2396
3350
  const existingCheckpoint = readReasonCheckpoint(
2397
3351
  ctx.currentNodeState.byKey[checkpointKey]
2398
3352
  );
2399
3353
  const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
2400
- const traceSpan = getReasonTraceAdapter()?.startSpan({
2401
- name: "useReason",
2402
- attributes: {
2403
- ...id ? { id } : {},
2404
- opId,
2405
- nodeId: ctx.node.graph.node,
2406
- providerId: args.model.provider.id,
2407
- modelId: args.model.modelId,
2408
- stream: args.stream ?? args.model.options?.streaming ?? true,
2409
- emit: args.emit ?? true,
2410
- ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
2411
- ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
2412
- ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
2413
- ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
2414
- hasOutputSchema: Boolean(args.outputSchema),
2415
- hasInterrupt: Boolean(args.interrupt),
2416
- hasStructured: Boolean(args.structured),
2417
- hasTools: Boolean(args.tools?.length)
2418
- },
2419
- telemetry: {
2420
- ...args.telemetry ?? {},
2421
- input: args.telemetry?.input ?? args.input
2422
- }
2423
- });
2424
- if (args.tools?.length) {
2425
- return runReasonToolLoop({
2426
- useReasonArgs: args,
2427
- allowValidatedToolOutput,
2428
- checkpointKey,
2429
- initialWarnings: inferred.warnings,
2430
- ...id ? { id } : {},
2431
- opId,
2432
- ...traceSpan ? { traceSpan } : {}
2433
- });
2434
- }
2435
- const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
2436
- const setFieldPaths = resolveSetFieldPaths(args.structured);
2437
- const appendFieldPaths = resolveAppendFieldPaths(args.structured);
2438
- const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
2439
- const useStructuredIncrementalStreaming = Boolean(
2440
- args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
2441
- );
2442
- let firstText = "";
2443
- let firstRaw;
2444
- let firstOutput;
2445
- let firstInterruptRequest;
2446
- let firstInterruptRequired = false;
2447
- let finalText = "";
2448
- let finalRaw;
2449
- let finalOutput;
2450
- let aggregatedUsage;
2451
- let finalFinishReason;
2452
- let aggregatedProviderMetadata;
2453
- let aggregatedWarnings = inferred.warnings;
2454
- if (existingCheckpoint) {
2455
- firstText = existingCheckpoint.firstText;
2456
- firstRaw = existingCheckpoint.firstRaw;
2457
- firstOutput = existingCheckpoint.firstOutput;
2458
- finalText = firstText;
2459
- finalRaw = firstRaw;
2460
- finalOutput = firstOutput;
2461
- aggregatedUsage = existingCheckpoint.firstUsage;
2462
- finalFinishReason = existingCheckpoint.firstFinishReason;
2463
- aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
2464
- aggregatedWarnings = existingCheckpoint.firstWarnings;
2465
- traceSpan?.addEvent?.("useReason.resume", {
2466
- checkpointKey
2467
- });
2468
- } else {
2469
- const first = await reasonEngine(
2470
- {
2471
- ...args,
2472
- emit: suppressTextStream ? false : args.emit,
2473
- stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
2474
- ...useStructuredIncrementalStreaming ? {
2475
- onTextChunk: createStructuredOutputStreamer(args, id, opId)
2476
- } : {}
3354
+ return withSafeTraceSpan2(
3355
+ getReasonTraceAdapter(),
3356
+ {
3357
+ name: "useReason",
3358
+ attributes: {
3359
+ ...id ? { id } : {},
3360
+ opId,
3361
+ nodeId: ctx.node.graph.node,
3362
+ providerId: args.model.provider.id,
3363
+ modelId: args.model.modelId,
3364
+ stream: args.stream ?? args.model.options?.streaming ?? true,
3365
+ emit: args.emit ?? true,
3366
+ ...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
3367
+ ...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
3368
+ ...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
3369
+ ...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
3370
+ hasOutputSchema: Boolean(args.outputSchema),
3371
+ hasInterrupt: Boolean(args.interrupt),
3372
+ hasStructured: Boolean(args.structured),
3373
+ hasTools: Boolean(args.tools?.length)
2477
3374
  },
2478
- reasonMeta,
2479
- args.interrupt ? defaultInterruptFirstPassInput({
2480
- input: args.input,
2481
- requestSchema: args.interrupt.requestSchema,
2482
- mode: args.interrupt.mode ?? "required",
2483
- ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
2484
- }) : args.outputSchema ? withOutputGuardrails(
2485
- withStructuredStreamHints(args.input, {
2486
- ...setFieldPaths.length > 0 ? { setFieldPaths } : {},
2487
- ...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
2488
- ...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
2489
- }),
2490
- args.outputSchema
2491
- ) : void 0
2492
- );
2493
- firstRaw = first.raw;
2494
- finalRaw = first.raw;
2495
- accumulateTokenUsage(first.usage);
2496
- aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
2497
- finalFinishReason = first.finishReason;
2498
- aggregatedProviderMetadata = mergeProviderMetadata(
2499
- aggregatedProviderMetadata,
2500
- first.providerMetadata
2501
- );
2502
- aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
2503
- traceSpan?.addEvent?.("useReason.first-pass.complete", {
2504
- textLength: first.text.length,
2505
- hasInterrupt: Boolean(args.interrupt)
2506
- });
2507
- if (args.interrupt) {
2508
- const firstPass = parseInterruptFirstPassResult({
2509
- text: first.text,
2510
- requestSchema: args.interrupt.requestSchema,
2511
- ...first.finishReason ? { finishReason: first.finishReason } : {},
2512
- ...first.usage ? { usage: first.usage } : {},
2513
- ...args.outputSchema ? { outputSchema: args.outputSchema } : {},
2514
- mode: args.interrupt.mode ?? "required"
2515
- });
2516
- firstText = firstPass.draftText;
2517
- finalText = firstPass.draftText;
2518
- firstInterruptRequired = firstPass.interruptRequired;
2519
- firstInterruptRequest = firstPass.request;
2520
- firstOutput = firstPass.output;
2521
- finalOutput = firstPass.output;
2522
- } else {
2523
- firstText = first.text;
2524
- finalText = first.text;
2525
- if (args.outputSchema) {
2526
- firstOutput = parseReasonOutputWithSchema({
2527
- text: first.text,
2528
- schema: args.outputSchema,
2529
- ...first.finishReason ? { finishReason: first.finishReason } : {},
2530
- ...first.usage ? { usage: first.usage } : {},
2531
- label: "useReason output"
3375
+ telemetry: {
3376
+ ...args.telemetry ?? {},
3377
+ input: args.telemetry?.input ?? args.input
3378
+ }
3379
+ },
3380
+ async (traceSpan) => {
3381
+ if (args.tools?.length) {
3382
+ return runReasonToolLoop({
3383
+ useReasonArgs: args,
3384
+ allowValidatedToolOutput,
3385
+ checkpointKey,
3386
+ initialWarnings: inferred.warnings,
3387
+ ...id ? { id } : {},
3388
+ opId,
3389
+ ...traceSpan ? { traceSpan } : {}
2532
3390
  });
2533
3391
  }
2534
- finalOutput = firstOutput;
2535
- }
2536
- }
2537
- let interruptResponse;
2538
- if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
2539
- const requestSchema = args.interrupt.requestSchema;
2540
- const interruptRequest = existingCheckpoint ? parseWithSchema(
2541
- requestSchema,
2542
- existingCheckpoint.request,
2543
- "useReason interrupt.request"
2544
- ) : firstInterruptRequest;
2545
- if (!interruptRequest) {
2546
- throw new Error(
2547
- "useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
3392
+ const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
3393
+ const setFieldPaths = resolveSetFieldPaths(args.structured);
3394
+ const appendFieldPaths = resolveAppendFieldPaths(args.structured);
3395
+ const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
3396
+ const useStructuredIncrementalStreaming = Boolean(
3397
+ args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
2548
3398
  );
2549
- }
2550
- if (!existingCheckpoint) {
2551
- ctx.currentNodeState.byKey[checkpointKey] = {
2552
- status: "awaiting_interrupt",
2553
- request: interruptRequest,
2554
- firstText,
2555
- ...firstRaw !== void 0 ? { firstRaw } : {},
2556
- ...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
2557
- ...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
2558
- ...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
2559
- ...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
2560
- ...firstOutput !== void 0 ? { firstOutput } : {}
2561
- };
2562
- ctx.stateDirty = true;
2563
- }
2564
- const resumeStatePatch = resolveHookStatePatch({
2565
- nodeId: ctx.node.graph.node,
2566
- currentNodeState: ctx.currentNodeState,
2567
- workflowState: ctx.workflowState
2568
- });
2569
- interruptResponse = await awaitInterruptInternal({
2570
- request: interruptRequest,
2571
- requestSchema,
2572
- responseSchema: args.interrupt.responseSchema,
2573
- ...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
2574
- ...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
2575
- ...id ? { id } : {},
2576
- meta: {
2577
- __kortyxResumeStatePatch: resumeStatePatch
3399
+ let firstText = "";
3400
+ let firstRaw;
3401
+ let firstOutput;
3402
+ let firstInterruptRequest;
3403
+ let firstInterruptRequired = false;
3404
+ let finalText = "";
3405
+ let finalRaw;
3406
+ let finalOutput;
3407
+ let aggregatedUsage;
3408
+ let finalFinishReason;
3409
+ let aggregatedProviderMetadata;
3410
+ let aggregatedWarnings = inferred.warnings;
3411
+ if (existingCheckpoint) {
3412
+ firstText = existingCheckpoint.firstText;
3413
+ firstRaw = existingCheckpoint.firstRaw;
3414
+ firstOutput = existingCheckpoint.firstOutput;
3415
+ finalText = firstText;
3416
+ finalRaw = firstRaw;
3417
+ finalOutput = firstOutput;
3418
+ aggregatedUsage = existingCheckpoint.firstUsage;
3419
+ finalFinishReason = existingCheckpoint.firstFinishReason;
3420
+ aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
3421
+ aggregatedWarnings = existingCheckpoint.firstWarnings;
3422
+ traceSpan?.addEvent?.("useReason.resume", {
3423
+ checkpointKey
3424
+ });
3425
+ } else {
3426
+ const first = await reasonEngine(
3427
+ {
3428
+ ...args,
3429
+ emit: suppressTextStream ? false : args.emit,
3430
+ stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
3431
+ ...useStructuredIncrementalStreaming ? {
3432
+ onTextChunk: createStructuredOutputStreamer(args, id, opId)
3433
+ } : {}
3434
+ },
3435
+ reasonMeta,
3436
+ args.interrupt ? defaultInterruptFirstPassInput({
3437
+ input: args.input,
3438
+ requestSchema: args.interrupt.requestSchema,
3439
+ mode: args.interrupt.mode ?? "required",
3440
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
3441
+ }) : args.outputSchema ? withOutputGuardrails(
3442
+ withStructuredStreamHints(args.input, {
3443
+ ...setFieldPaths.length > 0 ? { setFieldPaths } : {},
3444
+ ...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
3445
+ ...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
3446
+ }),
3447
+ args.outputSchema
3448
+ ) : void 0
3449
+ );
3450
+ firstRaw = first.raw;
3451
+ finalRaw = first.raw;
3452
+ accumulateTokenUsage(first.usage);
3453
+ aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
3454
+ finalFinishReason = first.finishReason;
3455
+ aggregatedProviderMetadata = mergeProviderMetadata(
3456
+ aggregatedProviderMetadata,
3457
+ first.providerMetadata
3458
+ );
3459
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
3460
+ traceSpan?.addEvent?.("useReason.first-pass.complete", {
3461
+ textLength: first.text.length,
3462
+ hasInterrupt: Boolean(args.interrupt)
3463
+ });
3464
+ if (args.interrupt) {
3465
+ const firstPass = parseInterruptFirstPassResult({
3466
+ text: first.text,
3467
+ requestSchema: args.interrupt.requestSchema,
3468
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
3469
+ ...first.usage ? { usage: first.usage } : {},
3470
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {},
3471
+ mode: args.interrupt.mode ?? "required"
3472
+ });
3473
+ firstText = firstPass.draftText;
3474
+ finalText = firstPass.draftText;
3475
+ firstInterruptRequired = firstPass.interruptRequired;
3476
+ firstInterruptRequest = firstPass.request;
3477
+ firstOutput = firstPass.output;
3478
+ finalOutput = firstPass.output;
3479
+ } else {
3480
+ firstText = first.text;
3481
+ finalText = first.text;
3482
+ if (args.outputSchema) {
3483
+ firstOutput = parseReasonOutputWithSchema({
3484
+ text: first.text,
3485
+ schema: args.outputSchema,
3486
+ ...first.finishReason ? { finishReason: first.finishReason } : {},
3487
+ ...first.usage ? { usage: first.usage } : {},
3488
+ label: "useReason output"
3489
+ });
3490
+ }
3491
+ finalOutput = firstOutput;
3492
+ }
2578
3493
  }
2579
- });
2580
- traceSpan?.addEvent?.("useReason.interrupt.resolved", {
2581
- checkpointKey
2582
- });
2583
- if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
2584
- delete ctx.currentNodeState.byKey[checkpointKey];
2585
- ctx.stateDirty = true;
2586
- }
2587
- const continuationInput = defaultContinuationInput({
2588
- input: args.input,
2589
- draftText: firstText,
2590
- ...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
2591
- request: interruptRequest,
2592
- response: interruptResponse
2593
- });
2594
- const second = await reasonEngine(
2595
- {
2596
- ...args,
2597
- ...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
2598
- emit: suppressTextStream ? false : args.emit,
2599
- stream: suppressTextStream ? false : args.stream
2600
- },
2601
- reasonMeta,
2602
- args.outputSchema ? withOutputGuardrails(
2603
- continuationInput,
2604
- args.outputSchema
2605
- ) : continuationInput
2606
- );
2607
- finalText = second.text;
2608
- finalRaw = second.raw;
2609
- accumulateTokenUsage(second.usage);
2610
- aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
2611
- finalFinishReason = second.finishReason;
2612
- aggregatedProviderMetadata = mergeProviderMetadata(
2613
- aggregatedProviderMetadata,
2614
- second.providerMetadata
2615
- );
2616
- aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
2617
- traceSpan?.addEvent?.("useReason.continuation.complete", {
2618
- textLength: second.text.length
2619
- });
2620
- if (args.outputSchema) {
2621
- finalOutput = parseReasonOutputWithSchema({
3494
+ let interruptResponse;
3495
+ if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
3496
+ const requestSchema = args.interrupt.requestSchema;
3497
+ const interruptRequest = existingCheckpoint ? parseWithSchema(
3498
+ requestSchema,
3499
+ existingCheckpoint.request,
3500
+ "useReason interrupt.request"
3501
+ ) : firstInterruptRequest;
3502
+ if (!interruptRequest) {
3503
+ throw new Error(
3504
+ "useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
3505
+ );
3506
+ }
3507
+ if (!existingCheckpoint) {
3508
+ ctx.currentNodeState.byKey[checkpointKey] = {
3509
+ status: "awaiting_interrupt",
3510
+ request: interruptRequest,
3511
+ firstText,
3512
+ ...firstRaw !== void 0 ? { firstRaw } : {},
3513
+ ...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
3514
+ ...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
3515
+ ...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
3516
+ ...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
3517
+ ...firstOutput !== void 0 ? { firstOutput } : {}
3518
+ };
3519
+ ctx.stateDirty = true;
3520
+ }
3521
+ const resumeStatePatch = resolveHookStatePatch({
3522
+ nodeId: ctx.node.graph.node,
3523
+ currentNodeState: ctx.currentNodeState,
3524
+ workflowState: ctx.workflowState
3525
+ });
3526
+ interruptResponse = await awaitInterruptInternal({
3527
+ request: interruptRequest,
3528
+ requestSchema,
3529
+ responseSchema: args.interrupt.responseSchema,
3530
+ ...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
3531
+ ...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
3532
+ ...id ? { id } : {},
3533
+ meta: {
3534
+ __kortyxResumeStatePatch: resumeStatePatch
3535
+ }
3536
+ });
3537
+ traceSpan?.addEvent?.("useReason.interrupt.resolved", {
3538
+ checkpointKey
3539
+ });
3540
+ if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
3541
+ delete ctx.currentNodeState.byKey[checkpointKey];
3542
+ ctx.stateDirty = true;
3543
+ }
3544
+ const continuationInput = defaultContinuationInput({
3545
+ input: args.input,
3546
+ draftText: firstText,
3547
+ ...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
3548
+ request: interruptRequest,
3549
+ response: interruptResponse
3550
+ });
3551
+ const second = await reasonEngine(
3552
+ {
3553
+ ...args,
3554
+ ...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
3555
+ emit: suppressTextStream ? false : args.emit,
3556
+ stream: suppressTextStream ? false : args.stream
3557
+ },
3558
+ reasonMeta,
3559
+ args.outputSchema ? withOutputGuardrails(
3560
+ continuationInput,
3561
+ args.outputSchema
3562
+ ) : continuationInput
3563
+ );
3564
+ finalText = second.text;
3565
+ finalRaw = second.raw;
3566
+ accumulateTokenUsage(second.usage);
3567
+ aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
3568
+ finalFinishReason = second.finishReason;
3569
+ aggregatedProviderMetadata = mergeProviderMetadata(
3570
+ aggregatedProviderMetadata,
3571
+ second.providerMetadata
3572
+ );
3573
+ aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
3574
+ traceSpan?.addEvent?.("useReason.continuation.complete", {
3575
+ textLength: second.text.length
3576
+ });
3577
+ if (args.outputSchema) {
3578
+ finalOutput = parseReasonOutputWithSchema({
3579
+ text: finalText,
3580
+ schema: args.outputSchema,
3581
+ ...second.finishReason ? { finishReason: second.finishReason } : {},
3582
+ ...aggregatedUsage ? { usage: aggregatedUsage } : {},
3583
+ label: "useReason output"
3584
+ });
3585
+ }
3586
+ }
3587
+ if (finalOutput !== void 0) {
3588
+ emitReasonStructuredOutput({
3589
+ ...id ? { id } : {},
3590
+ opId,
3591
+ output: finalOutput,
3592
+ structured: args.structured,
3593
+ emit: args.emit ?? true
3594
+ });
3595
+ }
3596
+ const result = {
3597
+ ...id ? { id } : {},
3598
+ opId,
2622
3599
  text: finalText,
2623
- schema: args.outputSchema,
2624
- ...second.finishReason ? { finishReason: second.finishReason } : {},
2625
- ...aggregatedUsage ? { usage: aggregatedUsage } : {},
2626
- label: "useReason output"
3600
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
3601
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
3602
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
3603
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
3604
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
3605
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
3606
+ ...interruptResponse !== void 0 ? { interruptResponse } : {}
3607
+ };
3608
+ if (args.interrupt) {
3609
+ ctx.currentNodeState.byKey[checkpointKey] = {
3610
+ status: "completed",
3611
+ result
3612
+ };
3613
+ ctx.stateDirty = true;
3614
+ }
3615
+ traceSpan?.end?.({
3616
+ ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
3617
+ ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
3618
+ ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
3619
+ ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
3620
+ attributes: {
3621
+ textLength: finalText.length,
3622
+ resumedFromCheckpoint: Boolean(existingCheckpoint),
3623
+ interrupted: Boolean(interruptResponse !== void 0)
3624
+ },
3625
+ telemetry: {
3626
+ ...args.telemetry ?? {},
3627
+ output: args.telemetry?.output ?? finalOutput ?? finalText
3628
+ }
2627
3629
  });
3630
+ return result;
2628
3631
  }
3632
+ );
3633
+ }
3634
+ async function useReason(args) {
3635
+ try {
3636
+ return await runReason(args);
3637
+ } finally {
3638
+ await closeOwnedTools(args.tools ?? []);
2629
3639
  }
2630
- if (finalOutput !== void 0) {
2631
- emitReasonStructuredOutput({
2632
- ...id ? { id } : {},
2633
- opId,
2634
- output: finalOutput,
2635
- structured: args.structured,
2636
- emit: args.emit ?? true
2637
- });
2638
- }
2639
- const result = {
2640
- ...id ? { id } : {},
2641
- opId,
2642
- text: finalText,
2643
- ...finalRaw !== void 0 ? { raw: finalRaw } : {},
2644
- ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2645
- ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2646
- ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2647
- ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2648
- ...finalOutput !== void 0 ? { output: finalOutput } : {},
2649
- ...interruptResponse !== void 0 ? { interruptResponse } : {}
2650
- };
2651
- if (args.interrupt) {
2652
- ctx.currentNodeState.byKey[checkpointKey] = {
2653
- status: "completed",
2654
- result
2655
- };
2656
- ctx.stateDirty = true;
2657
- }
2658
- traceSpan?.end?.({
2659
- ...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
2660
- ...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
2661
- ...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
2662
- ...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
2663
- attributes: {
2664
- textLength: finalText.length,
2665
- resumedFromCheckpoint: Boolean(existingCheckpoint),
2666
- interrupted: Boolean(interruptResponse !== void 0)
2667
- },
2668
- telemetry: {
2669
- ...args.telemetry ?? {},
2670
- output: args.telemetry?.output ?? finalOutput ?? finalText
2671
- }
2672
- });
2673
- return result;
2674
3640
  }
2675
3641
  function useStructuredData(args) {
2676
3642
  emitStructuredData(args);
@@ -2725,8 +3691,8 @@ ${tryStringify2(args.response)}`
2725
3691
  };
2726
3692
  return [getValue(), setValue];
2727
3693
  }
2728
- var import_core62 = require("@kortyx/core");
2729
- var import_errors52 = require("@kortyx/core/errors");
3694
+ var import_core7 = require("@kortyx/core");
3695
+ var import_errors7 = require("@kortyx/core/errors");
2730
3696
  var ParallelChildWaiting = class extends Error {
2731
3697
  name = "ParallelChildWaiting";
2732
3698
  };
@@ -2740,14 +3706,14 @@ ${tryStringify2(args.response)}`
2740
3706
  );
2741
3707
  this.results = results;
2742
3708
  this.name = "ParallelError";
2743
- this.failure = (0, import_errors52.serializeFailure)({
3709
+ this.failure = (0, import_errors7.serializeFailure)({
2744
3710
  version: 1,
2745
3711
  code: this.code,
2746
3712
  category: "workflow",
2747
3713
  message: this.message,
2748
3714
  retryable: null,
2749
3715
  results: results.map(
2750
- (result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors52.serializeFailure)(result.reason) }
3716
+ (result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors7.serializeFailure)(result.reason) }
2751
3717
  )
2752
3718
  });
2753
3719
  }
@@ -2755,7 +3721,7 @@ ${tryStringify2(args.response)}`
2755
3721
  code = "PARALLEL_FAILED";
2756
3722
  failure;
2757
3723
  toJSON() {
2758
- return (0, import_errors52.serializeFailure)(this);
3724
+ return (0, import_errors7.serializeFailure)(this);
2759
3725
  }
2760
3726
  };
2761
3727
  function registerWorkflowTask(id) {
@@ -2851,13 +3817,13 @@ ${tryStringify2(args.response)}`
2851
3817
  );
2852
3818
  }
2853
3819
  if (ready[0]?.status === "rejected") throw ready[0].reason;
2854
- (0, import_core62.throwIfExecutionAborted)(ctx.node.abortSignal);
3820
+ (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
2855
3821
  if (ctx.workflowControlError) throw ctx.workflowControlError;
2856
3822
  const rejected = outcomes.filter(
2857
3823
  (outcome) => outcome.status === "rejected"
2858
3824
  );
2859
3825
  const control = rejected.find(
2860
- (outcome) => (0, import_core62.isExecutionCancelled)(outcome.reason) || (0, import_core62.isExecutionLimitReached)(outcome.reason)
3826
+ (outcome) => (0, import_core7.isExecutionCancelled)(outcome.reason) || (0, import_core7.isExecutionLimitReached)(outcome.reason)
2861
3827
  );
2862
3828
  if (control) {
2863
3829
  ctx.workflowControlError = control.reason;
@@ -2892,50 +3858,19 @@ ${tryStringify2(args.response)}`
2892
3858
  });
2893
3859
  return promise;
2894
3860
  }
2895
- var import_node_crypto5 = require("crypto");
2896
- var import_core7 = require("@kortyx/core");
2897
- var import_errors62 = require("@kortyx/core/errors");
2898
- var import_node_crypto42 = require("crypto");
2899
- function emitWorkflowCall2(telemetry, type, payload) {
2900
- const base = telemetry?.correlation;
2901
- if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
2902
- return;
3861
+ function reportError(error, options = {}) {
2903
3862
  try {
2904
- const active = telemetry.trace?.getActiveContext?.();
2905
- void Promise.resolve(
2906
- telemetry.reporter.emit([
2907
- {
2908
- schemaVersion: 1,
2909
- eventId: (0, import_node_crypto42.randomUUID)(),
2910
- occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
2911
- environment: telemetry.environment,
2912
- service: telemetry.service,
2913
- correlation: {
2914
- ...base,
2915
- runId: base.runId,
2916
- workflowId: base.workflowId,
2917
- ...active
2918
- },
2919
- type,
2920
- payload
2921
- }
2922
- ])
2923
- ).catch(() => {
2924
- });
3863
+ getReasonTraceAdapter()?.reportError?.(error, options);
2925
3864
  } catch {
2926
3865
  }
2927
3866
  }
2928
- function workflowCallContent(telemetry, side, value) {
2929
- const policy = telemetry?.captureContent;
2930
- if (!(policy === true || policy && typeof policy === "object" && policy[side]))
2931
- return {};
2932
- const serialized = JSON.stringify(value);
2933
- return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
2934
- }
2935
- var WorkflowCallError = class extends import_errors62.KortyxError {
3867
+ var import_node_crypto6 = require("crypto");
3868
+ var import_core8 = require("@kortyx/core");
3869
+ var import_errors8 = require("@kortyx/core/errors");
3870
+ var WorkflowCallError = class extends import_errors8.KortyxError {
2936
3871
  constructor(workflow, message, cause, failure) {
2937
- const detail = (0, import_errors62.serializeFailure)(
2938
- failure ?? cause ?? new import_errors62.WorkflowContractError(message)
3872
+ const detail = (0, import_errors8.serializeFailure)(
3873
+ failure ?? cause ?? new import_errors8.WorkflowContractError(message)
2939
3874
  );
2940
3875
  super(detail.code, `Workflow '${workflow}': ${message}`, {
2941
3876
  ...detail,
@@ -2956,12 +3891,12 @@ ${tryStringify2(args.response)}`
2956
3891
  if (v === null || typeof v === "string" || typeof v === "boolean") return v;
2957
3892
  if (typeof v === "number" && Number.isFinite(v)) return v;
2958
3893
  if (typeof v !== "object" || !v || seen.has(v)) {
2959
- throw new import_errors62.WorkflowContractError(
3894
+ throw new import_errors8.WorkflowContractError(
2960
3895
  "Workflow input and output must be JSON serializable."
2961
3896
  );
2962
3897
  }
2963
3898
  if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
2964
- throw new import_errors62.WorkflowContractError(
3899
+ throw new import_errors8.WorkflowContractError(
2965
3900
  "Workflow input and output must contain plain JSON objects."
2966
3901
  );
2967
3902
  }
@@ -2982,7 +3917,7 @@ ${tryStringify2(args.response)}`
2982
3917
  useWorkflow: (args) => {
2983
3918
  const workflow = workflows[args.workflow];
2984
3919
  if (!workflow || workflow.id !== args.workflow)
2985
- throw new import_errors62.WorkflowContractError(
3920
+ throw new import_errors8.WorkflowContractError(
2986
3921
  "Workflow registry keys must match definition ids."
2987
3922
  );
2988
3923
  return useWorkflow({ ...args, workflow });
@@ -2996,7 +3931,7 @@ ${tryStringify2(args.response)}`
2996
3931
  if (task.group) await task.group.ready;
2997
3932
  return await runWorkflowCall(args, task);
2998
3933
  } catch (error) {
2999
- if ((0, import_core7.isExecutionCancelled)(error) || (0, import_core7.isExecutionLimitReached)(error))
3934
+ if ((0, import_core8.isExecutionCancelled)(error) || (0, import_core8.isExecutionLimitReached)(error))
3000
3935
  getHookContext().workflowControlError = error;
3001
3936
  throw error;
3002
3937
  } finally {
@@ -3005,26 +3940,26 @@ ${tryStringify2(args.response)}`
3005
3940
  }
3006
3941
  async function runWorkflowCall(args, task) {
3007
3942
  const ctx = getHookContext();
3008
- (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
3943
+ (0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
3009
3944
  const service = ctx.node.callWorkflow;
3010
3945
  if (!service)
3011
- throw new import_errors62.WorkflowContractError(
3946
+ throw new import_errors8.WorkflowContractError(
3012
3947
  "useWorkflow requires an agent workflow registry."
3013
3948
  );
3014
3949
  if (!args.id.trim())
3015
- throw new import_errors62.WorkflowContractError(
3950
+ throw new import_errors8.WorkflowContractError(
3016
3951
  "useWorkflow requires a stable, nonempty id."
3017
3952
  );
3018
3953
  if (ctx.workflowCallActive && !task.group)
3019
- throw new import_errors62.WorkflowContractError(
3954
+ throw new import_errors8.WorkflowContractError(
3020
3955
  "Concurrent workflow calls require parallel([...]); otherwise await each call."
3021
3956
  );
3022
3957
  if (ctx.workflowCallIds.has(args.id))
3023
- throw new import_errors62.WorkflowContractError(
3958
+ throw new import_errors8.WorkflowContractError(
3024
3959
  `Duplicate workflow call id '${args.id}' in one node activation.`
3025
3960
  );
3026
3961
  if (ctx.workflowCallIds.size >= 64)
3027
- throw new import_errors62.WorkflowContractError(
3962
+ throw new import_errors8.WorkflowContractError(
3028
3963
  "A node may call at most 64 child workflows per activation."
3029
3964
  );
3030
3965
  ctx.workflowCallIds.add(args.id);
@@ -3051,7 +3986,7 @@ ${tryStringify2(args.response)}`
3051
3986
  if (!record) ctx.node.consumeExecution?.("maxChildInvocations");
3052
3987
  record ??= {
3053
3988
  fingerprint,
3054
- invocationId: (0, import_node_crypto5.randomUUID)(),
3989
+ invocationId: (0, import_node_crypto6.randomUUID)(),
3055
3990
  status: "running",
3056
3991
  interrupts: [],
3057
3992
  ...task.group ? { parallelKey: task.group.key } : {}
@@ -3061,7 +3996,7 @@ ${tryStringify2(args.response)}`
3061
3996
  if (!task.group) ctx.workflowCallActive = true;
3062
3997
  const telemetry = ctx.node.workflowCallTelemetry;
3063
3998
  const activationKey = "__useWorkflowActivation";
3064
- ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
3999
+ ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto6.randomUUID)();
3065
4000
  const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
3066
4001
  const metadata2 = {
3067
4002
  invocationId: record.invocationId,
@@ -3105,7 +4040,7 @@ ${tryStringify2(args.response)}`
3105
4040
  workflow,
3106
4041
  record.error ?? "child failed",
3107
4042
  void 0,
3108
- record.failure ?? (0, import_errors62.serializeFailure)(void 0)
4043
+ record.failure ?? (0, import_errors8.serializeFailure)(void 0)
3109
4044
  );
3110
4045
  if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
3111
4046
  throw new ParallelChildWaiting();
@@ -3129,9 +4064,9 @@ ${tryStringify2(args.response)}`
3129
4064
  } catch (error) {
3130
4065
  const child = error;
3131
4066
  recordUsage(child?.__kortyxChildUsage);
3132
- (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
3133
- if ((0, import_core7.isExecutionCancelled)(error)) throw error;
3134
- if ((0, import_core7.isExecutionLimitReached)(error)) {
4067
+ (0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
4068
+ if ((0, import_core8.isExecutionCancelled)(error)) throw error;
4069
+ if ((0, import_core8.isExecutionLimitReached)(error)) {
3135
4070
  if (child?.__kortyxChildSnapshot)
3136
4071
  record.snapshot = child.__kortyxChildSnapshot;
3137
4072
  report("suspended", {
@@ -3141,7 +4076,7 @@ ${tryStringify2(args.response)}`
3141
4076
  throw error;
3142
4077
  }
3143
4078
  record.status = "failed";
3144
- record.failure = (0, import_errors62.serializeFailure)(error);
4079
+ record.failure = (0, import_errors8.serializeFailure)(error);
3145
4080
  record.error = record.failure.message;
3146
4081
  report("failed", { error: "Child workflow failed" });
3147
4082
  throw new WorkflowCallError(
@@ -3151,12 +4086,15 @@ ${tryStringify2(args.response)}`
3151
4086
  record.failure
3152
4087
  );
3153
4088
  }
3154
- (0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
4089
+ (0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
3155
4090
  recordUsage(outcome.usage);
3156
4091
  if (outcome.status === "completed") {
3157
4092
  record.data = outcome.data;
3158
4093
  workflowCallFingerprint(record.data);
3159
4094
  record.status = "completed";
4095
+ record.toolObservations = outcome.toolObservations ?? [];
4096
+ for (const observation of record.toolObservations)
4097
+ rememberToolObservation(observation);
3160
4098
  delete record.snapshot;
3161
4099
  report(
3162
4100
  "completed",
@@ -3179,8 +4117,12 @@ ${tryStringify2(args.response)}`
3179
4117
  });
3180
4118
  }
3181
4119
  }
3182
- if (cached)
4120
+ if (cached) {
3183
4121
  report("reused", workflowCallContent(telemetry, "output", record.data));
4122
+ if (record.restoredToolBranch !== telemetry?.correlation?.branchId)
4123
+ await replayToolObservations(record.toolObservations ?? [], true);
4124
+ delete record.restoredToolBranch;
4125
+ }
3184
4126
  const data = record.data ?? {};
3185
4127
  return {
3186
4128
  data: JSON.parse(JSON.stringify(data))
@@ -3417,6 +4359,7 @@ var ExecutionRequestError = class extends import_errors2.KortyxError {
3417
4359
  var import_node_stream = require("stream");
3418
4360
  var import_core2 = require("@kortyx/core");
3419
4361
  var import_errors4 = require("@kortyx/core/errors");
4362
+ var import_internal = __toESM(require_internal());
3420
4363
  var import_runtime = require("@kortyx/runtime");
3421
4364
  var import_langgraph = require("@langchain/langgraph");
3422
4365
 
@@ -3991,7 +4934,9 @@ async function orchestrateGraphStream({
3991
4934
  let structuredSeq = 0;
3992
4935
  const debugEnabled = Boolean(config?.features?.tracing);
3993
4936
  const traceAdapter = getTraceAdapter(config);
3994
- const contextMeta = isRecord3(config.context) ? config.context : {};
4937
+ const contextMeta = (0, import_internal.safeTelemetryMetadata)(
4938
+ isRecord3(config.context) ? { userId: config.context.userId, tenantId: config.context.tenantId } : {}
4939
+ );
3995
4940
  const telemetryConfig = isRecord3(config.telemetry) ? config.telemetry : {};
3996
4941
  const emitResumeFailure = (resumeError) => {
3997
4942
  const interruptId = typeof config.telemetryInterruptId === "string" ? config.telemetryInterruptId : void 0;
@@ -4027,8 +4972,13 @@ async function orchestrateGraphStream({
4027
4972
  ...isRecord3(telemetryConfig.metadata) ? telemetryConfig.metadata : {},
4028
4973
  ...contextMeta
4029
4974
  },
4030
- tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags : void 0,
4031
- captureContent: telemetryConfig.captureContent,
4975
+ tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags.filter(
4976
+ (tag) => typeof tag === "string"
4977
+ ) : void 0,
4978
+ captureContent: typeof telemetryConfig.captureContent === "boolean" ? telemetryConfig.captureContent : isRecord3(telemetryConfig.captureContent) ? {
4979
+ ...typeof telemetryConfig.captureContent.input === "boolean" ? { input: telemetryConfig.captureContent.input } : {},
4980
+ ...typeof telemetryConfig.captureContent.output === "boolean" ? { output: telemetryConfig.captureContent.output } : {}
4981
+ } : void 0,
4032
4982
  input: typeof state.input === "string" ? state.input : JSON.stringify(state.input)
4033
4983
  }
4034
4984
  };
@@ -4784,24 +5734,28 @@ async function orchestrateGraphStream({
4784
5734
  }
4785
5735
  };
4786
5736
  const emitTraceChunk = () => {
4787
- const traceContext = traceAdapter?.getActiveContext?.();
4788
- if (!traceContext) return;
4789
- write({
4790
- type: "trace",
4791
- traceId: traceContext.traceId,
4792
- spanId: traceContext.spanId,
4793
- runId,
4794
- rootSpanName: "kortyx.run"
4795
- });
5737
+ try {
5738
+ const traceContext = traceAdapter?.getActiveContext?.();
5739
+ if (!traceContext) return;
5740
+ write({
5741
+ type: "trace",
5742
+ traceId: traceContext.traceId,
5743
+ spanId: traceContext.spanId,
5744
+ runId,
5745
+ rootSpanName: "kortyx.run"
5746
+ });
5747
+ } catch {
5748
+ }
4796
5749
  };
4797
- const fallbackRunSpan = traceAdapter?.withSpan ? void 0 : traceAdapter?.startSpan?.(runSpanArgs);
5750
+ let fallbackRunSpan;
4798
5751
  if (!traceAdapter?.withSpan) {
5752
+ fallbackRunSpan = (0, import_internal.safeStartSpan)(traceAdapter, runSpanArgs);
4799
5753
  emitTraceChunk();
4800
5754
  }
4801
- const startRun = () => traceAdapter?.withSpan ? traceAdapter.withSpan(runSpanArgs, (runTraceSpan) => {
5755
+ const startRun = () => traceAdapter?.withSpan ? (0, import_internal.withSafeTraceSpan)(traceAdapter, runSpanArgs, async (runTraceSpan) => {
4802
5756
  emitTraceChunk();
4803
5757
  return runLoop(runTraceSpan);
4804
- }) : runLoop(fallbackRunSpan ?? {});
5758
+ }) : runLoop(fallbackRunSpan);
4805
5759
  const runPromise = frameworkAdapter?.acquireRunLease && !releaseLease ? frameworkAdapter.acquireRunLease({
4806
5760
  runId,
4807
5761
  ...sessionId ? { sessionId } : {},
@@ -5564,6 +6518,7 @@ var import_hooks = __toESM(require_dist());
5564
6518
  function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
5565
6519
  const branchId = (0, import_node_crypto3.randomUUID)();
5566
6520
  const emitted = /* @__PURE__ */ new Set();
6521
+ const toolEvidence = /* @__PURE__ */ new Set();
5567
6522
  for (const request of requests) {
5568
6523
  if (request.state)
5569
6524
  request.state.config = {
@@ -5601,6 +6556,52 @@ function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
5601
6556
  status: record.status
5602
6557
  }
5603
6558
  );
6559
+ if (record.status === "completed" && Array.isArray(record.toolObservations)) {
6560
+ if (telemetry?.reporter && telemetry.environment && telemetry.service)
6561
+ record.restoredToolBranch = branchId;
6562
+ for (const original of record.toolObservations) {
6563
+ const key = JSON.stringify([
6564
+ original.invocationId,
6565
+ original.toolCallId,
6566
+ original.attemptId
6567
+ ]);
6568
+ if (toolEvidence.has(key)) continue;
6569
+ toolEvidence.add(key);
6570
+ const payload = {
6571
+ ...original,
6572
+ runId: request.runId,
6573
+ branchId,
6574
+ attemptId: (0, import_node_crypto3.randomUUID)(),
6575
+ executed: false,
6576
+ durationMs: void 0,
6577
+ observationKind: "reused",
6578
+ source: original.source ?? {
6579
+ runId: original.runId ?? sourceRunId,
6580
+ invocationId: original.invocationId,
6581
+ branchId: original.branchId,
6582
+ toolCallId: original.toolCallId,
6583
+ attemptId: original.attemptId
6584
+ }
6585
+ };
6586
+ (0, import_hooks.emitWorkflowCall)(
6587
+ telemetry ? {
6588
+ ...telemetry,
6589
+ correlation: {
6590
+ runId: request.runId,
6591
+ sessionId: request.sessionId,
6592
+ workflowId: original.workflowId ?? request.workflow,
6593
+ nodeId: original.nodeId,
6594
+ workflowRevisionId: original.workflowRevisionId,
6595
+ invocationId: original.invocationId,
6596
+ parentInvocationId: original.parentInvocationId,
6597
+ branchId
6598
+ }
6599
+ } : void 0,
6600
+ "tool.reused",
6601
+ payload
6602
+ );
6603
+ }
6604
+ }
5604
6605
  }
5605
6606
  }
5606
6607
  }