@kortyx/hooks 0.4.1 → 0.5.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
@@ -20,11 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  runWithHookContext: () => runWithHookContext,
23
- useAiInterrupt: () => useAiInterrupt,
24
23
  useAiMemory: () => useAiMemory,
25
24
  useEmit: () => useEmit,
25
+ useInterrupt: () => useInterrupt,
26
26
  useNodeState: () => useNodeState,
27
- useReason: () => useReason,
27
+ useReason: () => useReason2,
28
28
  useStructuredData: () => useStructuredData,
29
29
  useWorkflowState: () => useWorkflowState
30
30
  });
@@ -65,6 +65,7 @@ var createInternalContext = (ctx) => {
65
65
  return {
66
66
  ...ctx,
67
67
  nodeStateIndex: 0,
68
+ reasonCallIndex: 0,
68
69
  currentNodeState,
69
70
  workflowState: cloneWorkflowState(hookMemory.workflowState),
70
71
  dirty: false
@@ -89,8 +90,15 @@ async function runWithHookContext(ctx, fn) {
89
90
  return { result, memoryUpdates: buildMemoryUpdates(internal) };
90
91
  } catch (err) {
91
92
  const memoryUpdates = buildMemoryUpdates(internal);
92
- if (memoryUpdates && err && typeof err === "object") {
93
- err.__kortyxMemoryUpdates = memoryUpdates;
93
+ if (memoryUpdates) {
94
+ const currentMemory = isRecord(internal.state.memory) ? internal.state.memory : {};
95
+ internal.state.memory = {
96
+ ...currentMemory,
97
+ ...memoryUpdates
98
+ };
99
+ if (err && typeof err === "object") {
100
+ err.__kortyxMemoryUpdates = memoryUpdates;
101
+ }
94
102
  }
95
103
  throw err;
96
104
  }
@@ -103,13 +111,111 @@ function getHookContext() {
103
111
  return ctx;
104
112
  }
105
113
 
114
+ // src/validation.ts
115
+ var parseWithSchema = (schema, value, label) => {
116
+ if (!schema) return value;
117
+ const parsed = schema.safeParse(value);
118
+ if (parsed.success) return parsed.data;
119
+ const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
120
+ const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
121
+ throw new Error(`${label} validation failed: ${reason}`);
122
+ };
123
+
124
+ // src/interrupt.ts
125
+ var awaitInterruptInternal = (args) => {
126
+ const ctx = getHookContext();
127
+ const request = parseWithSchema(
128
+ args.requestSchema,
129
+ args.request,
130
+ "useInterrupt request"
131
+ );
132
+ const enrichedRequest = {
133
+ ...request,
134
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
135
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
136
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
137
+ ...args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? { meta: args.meta } : {}
138
+ };
139
+ const raw = ctx.node.awaitInterrupt(enrichedRequest);
140
+ const response = parseWithSchema(
141
+ args.responseSchema,
142
+ raw,
143
+ "useInterrupt response"
144
+ );
145
+ return Promise.resolve(response);
146
+ };
147
+
148
+ // src/structured.ts
149
+ var emitStructuredData = (args) => {
150
+ const ctx = getHookContext();
151
+ const validated = parseWithSchema(
152
+ args.dataSchema,
153
+ args.data,
154
+ "useStructuredData data"
155
+ );
156
+ ctx.node.emit("structured_data", {
157
+ node: ctx.node.graph.node,
158
+ ...typeof args.dataType === "string" && args.dataType.length > 0 ? { dataType: args.dataType } : {},
159
+ ...typeof args.mode === "string" ? { mode: args.mode } : {},
160
+ ...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
161
+ ...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
162
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
163
+ ...typeof args.opId === "string" && args.opId.length > 0 ? { opId: args.opId } : {},
164
+ data: validated
165
+ });
166
+ };
167
+ var shouldEmitStructured = (cfg) => (cfg?.stream ?? "patch") !== "off";
168
+ var resolveStructuredMode = (cfg) => {
169
+ const mode = cfg?.stream ?? "patch";
170
+ if (mode === "snapshot") return "snapshot";
171
+ if (mode === "patch") return "patch";
172
+ return "final";
173
+ };
174
+
175
+ // src/reason/checkpoint.ts
176
+ var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
177
+ var readReasonCheckpoint = (raw) => {
178
+ if (!raw || typeof raw !== "object") return void 0;
179
+ const value = raw;
180
+ if (value.status !== "awaiting_interrupt") return void 0;
181
+ if (typeof value.firstText !== "string") return void 0;
182
+ if (!value.request || typeof value.request !== "object") return void 0;
183
+ return {
184
+ status: "awaiting_interrupt",
185
+ request: value.request,
186
+ firstText: value.firstText,
187
+ ...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
188
+ ...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
189
+ };
190
+ };
191
+ var resolveHookMemoryPatch = (args) => ({
192
+ __kortyx: {
193
+ nodeState: {
194
+ nodeId: args.nodeId,
195
+ state: args.currentNodeState
196
+ },
197
+ workflowState: args.workflowState
198
+ }
199
+ });
200
+
201
+ // src/reason/engine.ts
202
+ var import_node_crypto2 = require("crypto");
203
+
106
204
  // src/reason-engine.ts
205
+ var import_node_crypto = require("crypto");
107
206
  var chunkToText = (chunk) => {
108
207
  if (typeof chunk === "string") return chunk;
109
208
  if (typeof chunk.text === "string") return chunk.text;
110
209
  if (typeof chunk.content === "string") return chunk.content;
111
210
  return "";
112
211
  };
212
+ var createRuntimeId = () => {
213
+ try {
214
+ return (0, import_node_crypto.randomUUID)();
215
+ } catch {
216
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
217
+ }
218
+ };
113
219
  var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
114
220
  if (!emitEvent) return;
115
221
  emitEvent(event, {
@@ -130,11 +236,20 @@ async function runReasonEngine(args) {
130
236
  messages.push({ role: "system", content: args.system });
131
237
  }
132
238
  messages.push({ role: "user", content: String(args.input ?? "") });
239
+ const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
240
+ const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
241
+ const commonMeta = {
242
+ ...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
243
+ opId,
244
+ segmentId
245
+ };
133
246
  if (stream) {
134
247
  let final = "";
135
248
  let raw;
136
249
  if (emit) {
137
- emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {});
250
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
251
+ ...commonMeta
252
+ });
138
253
  }
139
254
  const response2 = await model.stream(messages);
140
255
  for await (const chunk of response2) {
@@ -146,12 +261,15 @@ async function runReasonEngine(args) {
146
261
  }
147
262
  if (emit) {
148
263
  emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
264
+ ...commonMeta,
149
265
  delta: text
150
266
  });
151
267
  }
152
268
  }
153
269
  if (emit) {
154
- emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {});
270
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
271
+ ...commonMeta
272
+ });
155
273
  }
156
274
  return {
157
275
  text: final,
@@ -160,13 +278,18 @@ async function runReasonEngine(args) {
160
278
  }
161
279
  const response = await model.invoke(messages);
162
280
  if (emit) {
163
- emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {});
281
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
282
+ ...commonMeta
283
+ });
164
284
  if (response.content) {
165
285
  emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
286
+ ...commonMeta,
166
287
  delta: response.content
167
288
  });
168
289
  }
169
- emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {});
290
+ emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
291
+ ...commonMeta
292
+ });
170
293
  }
171
294
  return {
172
295
  text: response.content,
@@ -174,20 +297,15 @@ async function runReasonEngine(args) {
174
297
  };
175
298
  }
176
299
 
177
- // src/hooks.ts
178
- function useEmit() {
179
- const ctx = getHookContext();
180
- return ctx.node.emit;
181
- }
182
- function useStructuredData(args) {
183
- const ctx = getHookContext();
184
- ctx.node.emit("structured_data", {
185
- node: ctx.node.graph.node,
186
- ...typeof args.dataType === "string" && args.dataType.length > 0 ? { dataType: args.dataType } : {},
187
- data: args.data
188
- });
189
- }
190
- async function reasonEngine(args) {
300
+ // src/reason/engine.ts
301
+ var createRuntimeId2 = () => {
302
+ try {
303
+ return (0, import_node_crypto2.randomUUID)();
304
+ } catch {
305
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
306
+ }
307
+ };
308
+ async function reasonEngine(args, meta, inputOverride) {
191
309
  const ctx = getHookContext();
192
310
  const getProvider = ctx.getProvider;
193
311
  if (!getProvider) {
@@ -196,18 +314,384 @@ async function reasonEngine(args) {
196
314
  return runReasonEngine({
197
315
  getProvider,
198
316
  model: args.model,
199
- input: args.input,
317
+ input: inputOverride ?? args.input,
200
318
  system: args.system,
201
319
  temperature: args.temperature,
202
320
  defaultTemperature: ctx.node.config?.model?.temperature,
203
321
  stream: args.stream,
204
322
  emit: args.emit,
205
323
  nodeId: ctx.node.graph.node,
206
- emitEvent: ctx.node.emit
324
+ emitEvent: ctx.node.emit,
325
+ id: meta.id,
326
+ opId: meta.opId
327
+ });
328
+ }
329
+
330
+ // src/reason/parsing.ts
331
+ var tryJsonParse = (value) => {
332
+ try {
333
+ return { ok: true, value: JSON.parse(value) };
334
+ } catch {
335
+ return { ok: false };
336
+ }
337
+ };
338
+ var extractJsonCodeBlock = (value) => {
339
+ const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
340
+ if (!match || typeof match[1] !== "string") return null;
341
+ const inner = match[1].trim();
342
+ return inner.length > 0 ? inner : null;
343
+ };
344
+ var tryStringify = (value) => {
345
+ if (typeof value === "string") return value;
346
+ try {
347
+ return JSON.stringify(value, null, 2);
348
+ } catch {
349
+ return String(value);
350
+ }
351
+ };
352
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
353
+ var resolveOutputCandidate = (text) => {
354
+ const trimmed = text.trim();
355
+ if (!trimmed) return text;
356
+ const direct = tryJsonParse(trimmed);
357
+ if (direct.ok) return direct.value;
358
+ const block = extractJsonCodeBlock(trimmed);
359
+ if (block) {
360
+ const parsedBlock = tryJsonParse(block);
361
+ if (parsedBlock.ok) return parsedBlock.value;
362
+ }
363
+ return text;
364
+ };
365
+ var parseInterruptFirstPassResult = (args) => {
366
+ const candidate = resolveOutputCandidate(args.text);
367
+ if (!isRecord2(candidate)) {
368
+ throw new Error(
369
+ "useReason first pass with interrupt must return a JSON object."
370
+ );
371
+ }
372
+ const requestCandidates = [
373
+ candidate.interruptRequest,
374
+ candidate.request,
375
+ candidate.interrupt,
376
+ candidate
377
+ ];
378
+ const requestPayload = requestCandidates.find(
379
+ (value) => value !== null && value !== void 0
380
+ );
381
+ const request = parseWithSchema(
382
+ args.requestSchema,
383
+ requestPayload,
384
+ "useReason interrupt.request"
385
+ );
386
+ let output;
387
+ if (args.outputSchema) {
388
+ const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
389
+ output = parseWithSchema(
390
+ args.outputSchema,
391
+ outputPayload,
392
+ "useReason output"
393
+ );
394
+ }
395
+ const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
396
+ if (!args.outputSchema && !explicitDraft) {
397
+ throw new Error(
398
+ "useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
399
+ );
400
+ }
401
+ const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
402
+ return {
403
+ draftText,
404
+ request,
405
+ ...output !== void 0 ? { output } : {}
406
+ };
407
+ };
408
+
409
+ // src/reason/prompting.ts
410
+ var import_zod = require("zod");
411
+ var tryStringify2 = (value) => {
412
+ if (typeof value === "string") return value;
413
+ try {
414
+ return JSON.stringify(value, null, 2);
415
+ } catch {
416
+ return String(value);
417
+ }
418
+ };
419
+ var tryGetSchemaHint = (schema) => {
420
+ try {
421
+ const hint = (0, import_zod.toJSONSchema)(schema);
422
+ return JSON.stringify(hint, null, 2);
423
+ } catch {
424
+ return void 0;
425
+ }
426
+ };
427
+ var withSchemaHint = (input, label, schema) => {
428
+ const hint = tryGetSchemaHint(schema);
429
+ if (!hint) return input;
430
+ return `${input}
431
+
432
+ ${label}:
433
+ ${hint}`;
434
+ };
435
+ var defaultInterruptFirstPassInput = (args) => {
436
+ const base = args.outputSchema ? [
437
+ args.input,
438
+ "Output rules:",
439
+ "- Return JSON only. No markdown fences.",
440
+ "- Return an object with keys: output, interruptRequest, draftText.",
441
+ "- output must match the expected output JSON schema.",
442
+ "- interruptRequest must match the expected interrupt request JSON schema.",
443
+ "- interruptRequest must be a non-null object.",
444
+ "- draftText should be a concise plain-language summary of output.",
445
+ "- If uncertain, still return a valid interruptRequest with safe default options."
446
+ ].join("\n\n") : [
447
+ args.input,
448
+ "Output rules:",
449
+ "- Return JSON only. No markdown fences.",
450
+ "- Return an object with keys: draftText, interruptRequest.",
451
+ "- draftText should be the current draft answer text.",
452
+ "- interruptRequest must match the expected interrupt request JSON schema.",
453
+ "- interruptRequest must be a non-null object.",
454
+ "- If uncertain, still return a valid interruptRequest with safe default options."
455
+ ].join("\n\n");
456
+ const withInterruptSchema = withSchemaHint(
457
+ base,
458
+ "Expected interrupt request JSON schema (for `interruptRequest`)",
459
+ args.requestSchema
460
+ );
461
+ if (!args.outputSchema) return withInterruptSchema;
462
+ const withOutputSchema = withSchemaHint(
463
+ withInterruptSchema,
464
+ "Expected output JSON schema (for `output`)",
465
+ args.outputSchema
466
+ );
467
+ return [
468
+ withOutputSchema,
469
+ "JSON template:",
470
+ '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
471
+ ].join("\n\n");
472
+ };
473
+ var defaultContinuationInput = (args) => [
474
+ "You are continuing a paused reasoning task after user input.",
475
+ "Update the previous result using the user response and return the final answer only.",
476
+ "If the previous output was JSON, return JSON in the same schema.",
477
+ "",
478
+ `Original request:
479
+ ${args.input}`,
480
+ `Draft answer text:
481
+ ${args.draftText}`,
482
+ `Draft output object:
483
+ ${tryStringify2(args.draftOutput ?? args.draftText)}`,
484
+ `Interrupt request shown to user:
485
+ ${tryStringify2(args.request)}`,
486
+ `User response:
487
+ ${tryStringify2(args.response)}`
488
+ ].join("\n\n");
489
+ var withOutputGuardrails = (input, schema) => {
490
+ const base = [
491
+ input,
492
+ "Output rules:",
493
+ "- Return only JSON, no markdown fences.",
494
+ "- Ensure the response conforms to the expected output schema.",
495
+ "- Do not include explanatory text outside the JSON object."
496
+ ].join("\n\n");
497
+ return withSchemaHint(base, "Expected output JSON schema", schema);
498
+ };
499
+
500
+ // src/reason/use-reason.ts
501
+ var emitReasonStructuredOutput = (args) => {
502
+ if (!shouldEmitStructured(args.structured)) return;
503
+ emitStructuredData({
504
+ dataType: args.structured?.dataType ?? "reason-output",
505
+ data: args.output,
506
+ mode: resolveStructuredMode(args.structured),
507
+ ...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
508
+ ...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
509
+ ...args.id ? { id: args.id } : {},
510
+ opId: args.opId
207
511
  });
512
+ };
513
+ async function useReason(args) {
514
+ const ctx = getHookContext();
515
+ const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
516
+ const opId = createRuntimeId2();
517
+ const checkpointKey = resolveReasonCheckpointKey({
518
+ ...id ? { id } : {},
519
+ autoIndex: ctx.reasonCallIndex++
520
+ });
521
+ const existingCheckpoint = readReasonCheckpoint(
522
+ ctx.currentNodeState.byKey[checkpointKey]
523
+ );
524
+ const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
525
+ const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
526
+ let firstText = "";
527
+ let firstRaw;
528
+ let firstOutput;
529
+ let firstInterruptRequest;
530
+ let finalText = "";
531
+ let finalRaw;
532
+ let finalOutput;
533
+ if (existingCheckpoint) {
534
+ firstText = existingCheckpoint.firstText;
535
+ firstRaw = existingCheckpoint.firstRaw;
536
+ firstOutput = existingCheckpoint.firstOutput;
537
+ finalText = firstText;
538
+ finalRaw = firstRaw;
539
+ finalOutput = firstOutput;
540
+ } else {
541
+ const first = await reasonEngine(
542
+ {
543
+ ...args,
544
+ emit: suppressTextStream ? false : args.emit,
545
+ stream: suppressTextStream ? false : args.stream
546
+ },
547
+ reasonMeta,
548
+ args.interrupt ? defaultInterruptFirstPassInput({
549
+ input: args.input,
550
+ requestSchema: args.interrupt.requestSchema,
551
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
552
+ }) : args.outputSchema ? withOutputGuardrails(
553
+ args.input,
554
+ args.outputSchema
555
+ ) : void 0
556
+ );
557
+ firstRaw = first.raw;
558
+ finalRaw = first.raw;
559
+ if (args.interrupt) {
560
+ const firstPass = parseInterruptFirstPassResult({
561
+ text: first.text,
562
+ requestSchema: args.interrupt.requestSchema,
563
+ ...args.outputSchema ? { outputSchema: args.outputSchema } : {}
564
+ });
565
+ firstText = firstPass.draftText;
566
+ finalText = firstPass.draftText;
567
+ firstInterruptRequest = firstPass.request;
568
+ firstOutput = firstPass.output;
569
+ finalOutput = firstPass.output;
570
+ if (firstOutput !== void 0) {
571
+ emitReasonStructuredOutput({
572
+ ...id ? { id } : {},
573
+ opId,
574
+ output: firstOutput,
575
+ structured: args.structured
576
+ });
577
+ }
578
+ } else {
579
+ firstText = first.text;
580
+ finalText = first.text;
581
+ if (args.outputSchema) {
582
+ firstOutput = parseWithSchema(
583
+ args.outputSchema,
584
+ resolveOutputCandidate(first.text),
585
+ "useReason output"
586
+ );
587
+ emitReasonStructuredOutput({
588
+ ...id ? { id } : {},
589
+ opId,
590
+ output: firstOutput,
591
+ structured: args.structured
592
+ });
593
+ }
594
+ finalOutput = firstOutput;
595
+ }
596
+ }
597
+ let interruptResponse;
598
+ if (args.interrupt) {
599
+ const requestSchema = args.interrupt.requestSchema;
600
+ const interruptRequest = existingCheckpoint ? parseWithSchema(
601
+ requestSchema,
602
+ existingCheckpoint.request,
603
+ "useReason interrupt.request"
604
+ ) : firstInterruptRequest;
605
+ if (!interruptRequest) {
606
+ throw new Error(
607
+ "useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
608
+ );
609
+ }
610
+ if (!existingCheckpoint) {
611
+ ctx.currentNodeState.byKey[checkpointKey] = {
612
+ status: "awaiting_interrupt",
613
+ request: interruptRequest,
614
+ firstText,
615
+ ...firstRaw !== void 0 ? { firstRaw } : {},
616
+ ...firstOutput !== void 0 ? { firstOutput } : {}
617
+ };
618
+ ctx.dirty = true;
619
+ }
620
+ const resumeMemoryPatch = resolveHookMemoryPatch({
621
+ nodeId: ctx.node.graph.node,
622
+ currentNodeState: ctx.currentNodeState,
623
+ workflowState: ctx.workflowState
624
+ });
625
+ interruptResponse = await awaitInterruptInternal({
626
+ request: interruptRequest,
627
+ requestSchema,
628
+ responseSchema: args.interrupt.responseSchema,
629
+ ...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
630
+ ...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
631
+ ...id ? { id } : {},
632
+ meta: {
633
+ __kortyxResumeMemory: resumeMemoryPatch
634
+ }
635
+ });
636
+ if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
637
+ delete ctx.currentNodeState.byKey[checkpointKey];
638
+ ctx.dirty = true;
639
+ }
640
+ const continuationInput = defaultContinuationInput({
641
+ input: args.input,
642
+ draftText: firstText,
643
+ ...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
644
+ request: interruptRequest,
645
+ response: interruptResponse
646
+ });
647
+ const second = await reasonEngine(
648
+ {
649
+ ...args,
650
+ emit: suppressTextStream ? false : args.emit,
651
+ stream: suppressTextStream ? false : args.stream
652
+ },
653
+ reasonMeta,
654
+ args.outputSchema ? withOutputGuardrails(
655
+ continuationInput,
656
+ args.outputSchema
657
+ ) : continuationInput
658
+ );
659
+ finalText = second.text;
660
+ finalRaw = second.raw;
661
+ if (args.outputSchema) {
662
+ finalOutput = parseWithSchema(
663
+ args.outputSchema,
664
+ resolveOutputCandidate(finalText),
665
+ "useReason output"
666
+ );
667
+ emitReasonStructuredOutput({
668
+ ...id ? { id } : {},
669
+ opId,
670
+ output: finalOutput,
671
+ structured: args.structured
672
+ });
673
+ }
674
+ }
675
+ return {
676
+ ...id ? { id } : {},
677
+ opId,
678
+ text: finalText,
679
+ ...finalRaw !== void 0 ? { raw: finalRaw } : {},
680
+ ...finalOutput !== void 0 ? { output: finalOutput } : {},
681
+ ...interruptResponse !== void 0 ? { interruptResponse } : {}
682
+ };
208
683
  }
209
- function useReason(args) {
210
- return reasonEngine(args);
684
+
685
+ // src/hooks.ts
686
+ function useEmit() {
687
+ const ctx = getHookContext();
688
+ return ctx.node.emit;
689
+ }
690
+ function useStructuredData(args) {
691
+ emitStructuredData(args);
692
+ }
693
+ function useReason2(args) {
694
+ return useReason(args);
211
695
  }
212
696
  function useAiMemory() {
213
697
  const ctx = getHookContext();
@@ -216,9 +700,8 @@ function useAiMemory() {
216
700
  }
217
701
  return ctx.memoryAdapter;
218
702
  }
219
- function useAiInterrupt(input) {
220
- const ctx = getHookContext();
221
- return Promise.resolve(ctx.node.awaitInterrupt(input));
703
+ function useInterrupt(args) {
704
+ return awaitInterruptInternal(args);
222
705
  }
223
706
  function useNodeState(keyOrInitial, initialValue) {
224
707
  const ctx = getHookContext();
@@ -273,9 +756,9 @@ function useWorkflowState(key, initialValue) {
273
756
  // Annotate the CommonJS export names for ESM import in node:
274
757
  0 && (module.exports = {
275
758
  runWithHookContext,
276
- useAiInterrupt,
277
759
  useAiMemory,
278
760
  useEmit,
761
+ useInterrupt,
279
762
  useNodeState,
280
763
  useReason,
281
764
  useStructuredData,