@dudousxd/nestjs-agent-core 0.44.1 → 0.45.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.cjs CHANGED
@@ -68,6 +68,7 @@ __export(src_exports, {
68
68
  DEFAULT_MAX_FACT_CHARS: () => DEFAULT_MAX_FACT_CHARS,
69
69
  DEFAULT_MAX_MEMORIES: () => DEFAULT_MAX_MEMORIES,
70
70
  DEFAULT_MAX_SKILLS: () => DEFAULT_MAX_SKILLS,
71
+ DEFAULT_PREVIEW_THROTTLE_MS: () => DEFAULT_PREVIEW_THROTTLE_MS,
71
72
  DEFAULT_REFUSAL_REASON: () => DEFAULT_REFUSAL_REASON,
72
73
  DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION: () => DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION,
73
74
  DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES: () => DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES,
@@ -186,9 +187,12 @@ __export(src_exports, {
186
187
  observeTurnFrames: () => observeTurnFrames,
187
188
  offerMemories: () => offerMemories,
188
189
  offerSkills: () => offerSkills,
190
+ parsePartialJson: () => parsePartialJson,
189
191
  parseTextActionProposalCommand: () => parseTextActionProposalCommand,
190
192
  personaCatalogEntry: () => personaCatalogEntry,
191
193
  prepareActionProposal: () => prepareActionProposal,
194
+ previewToolInputs: () => previewToolInputs,
195
+ previewUiId: () => previewUiId,
192
196
  ptBrActionProposalText: () => ptBrActionProposalText,
193
197
  publishAgentDelegated: () => publishAgentDelegated,
194
198
  publishAgentMemoryResolved: () => publishAgentMemoryResolved,
@@ -209,6 +213,7 @@ __export(src_exports, {
209
213
  quotaWarning: () => quotaWarning,
210
214
  readElicitationInput: () => readElicitationInput,
211
215
  readElicitationQuestions: () => readElicitationQuestions,
216
+ registryInputPreviews: () => registryInputPreviews,
212
217
  releaseGatedFrames: () => releaseGatedFrames,
213
218
  releaseThreadRun: () => releaseThreadRun,
214
219
  rememberInputSchema: () => rememberInputSchema,
@@ -251,6 +256,8 @@ __export(src_exports, {
251
256
  toActionProposalView: () => toActionProposalView,
252
257
  toolCallApprovalFromRow: () => toolCallApprovalFromRow,
253
258
  toolCallContext: () => toolCallContext,
259
+ toolCallUpdateForProposal: () => toolCallUpdateForProposal,
260
+ toolCallUpdateForTransition: () => toolCallUpdateForTransition,
254
261
  traceLlmTurn: () => traceLlmTurn,
255
262
  traceToolExecution: () => traceToolExecution,
256
263
  transitionActionProposalClaim: () => transitionActionProposalClaim,
@@ -277,6 +284,7 @@ __export(src_exports, {
277
284
  withSkillTool: () => withSkillTool,
278
285
  withToolTimeout: () => withToolTimeout,
279
286
  withTurnFrames: () => withTurnFrames,
287
+ withdrawPreviewEvent: () => withdrawPreviewEvent,
280
288
  wrapToolPreflightDenied: () => wrapToolPreflightDenied,
281
289
  wrapToolStepOutput: () => wrapToolStepOutput,
282
290
  writeMemory: () => writeMemory
@@ -398,7 +406,7 @@ function observeTurnFrames(writer, now = Date.now) {
398
406
  return;
399
407
  }
400
408
  closeBurst();
401
- if (event.kind === "ui") {
409
+ if (event.kind === "ui" && event.partial !== true) {
402
410
  ui.set(event.id, {
403
411
  id: event.id,
404
412
  component: event.component,
@@ -468,6 +476,423 @@ function withTurnFrames(result2, frames) {
468
476
  }
469
477
  __name(withTurnFrames, "withTurnFrames");
470
478
 
479
+ // src/partial-json.ts
480
+ var Cut = class Cut2 extends Error {
481
+ static {
482
+ __name(this, "Cut");
483
+ }
484
+ };
485
+ function parsePartialJson(text) {
486
+ const open = /* @__PURE__ */ new WeakSet();
487
+ const pending = /* @__PURE__ */ new WeakMap();
488
+ let at = 0;
489
+ const skipSpace = /* @__PURE__ */ __name(() => {
490
+ while (at < text.length) {
491
+ const char = text.charCodeAt(at);
492
+ if (char === 32 || char === 9 || char === 10 || char === 13) at += 1;
493
+ else break;
494
+ }
495
+ }, "skipSpace");
496
+ const fail = /* @__PURE__ */ __name(() => {
497
+ throw new SyntaxError(`unexpected character at ${at}`);
498
+ }, "fail");
499
+ const readString = /* @__PURE__ */ __name(() => {
500
+ at += 1;
501
+ let out = "";
502
+ while (at < text.length) {
503
+ const char = text[at];
504
+ if (char === '"') {
505
+ at += 1;
506
+ return {
507
+ value: out,
508
+ cut: false
509
+ };
510
+ }
511
+ if (char === "\\") {
512
+ const next = text[at + 1];
513
+ if (next === void 0) {
514
+ at = text.length;
515
+ return {
516
+ value: out,
517
+ cut: true
518
+ };
519
+ }
520
+ if (next === "u") {
521
+ const hex = text.slice(at + 2, at + 6);
522
+ if (hex.length < 4) {
523
+ at = text.length;
524
+ return {
525
+ value: out,
526
+ cut: true
527
+ };
528
+ }
529
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) fail();
530
+ out += String.fromCharCode(Number.parseInt(hex, 16));
531
+ at += 6;
532
+ continue;
533
+ }
534
+ const escapes = {
535
+ '"': '"',
536
+ "\\": "\\",
537
+ "/": "/",
538
+ b: "\b",
539
+ f: "\f",
540
+ n: "\n",
541
+ r: "\r",
542
+ t: " "
543
+ };
544
+ const escaped = escapes[next];
545
+ if (escaped === void 0) fail();
546
+ out += escaped;
547
+ at += 2;
548
+ continue;
549
+ }
550
+ out += char;
551
+ at += 1;
552
+ }
553
+ return {
554
+ value: out,
555
+ cut: true
556
+ };
557
+ }, "readString");
558
+ const readValue = /* @__PURE__ */ __name(() => {
559
+ skipSpace();
560
+ if (at >= text.length) throw new Cut();
561
+ const char = text[at];
562
+ if (char === "{") return readObject();
563
+ if (char === "[") return readArray();
564
+ if (char === '"') return readString();
565
+ const rest = text.slice(at);
566
+ const number = /^-?\d*(?:\.\d*)?(?:[eE][+-]?\d*)?/.exec(rest)?.[0] ?? "";
567
+ if (number.length > 0) {
568
+ if (at + number.length >= text.length) throw new Cut();
569
+ if (!/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(number)) fail();
570
+ at += number.length;
571
+ return {
572
+ value: Number(number),
573
+ cut: false
574
+ };
575
+ }
576
+ for (const [word, value] of [
577
+ [
578
+ "true",
579
+ true
580
+ ],
581
+ [
582
+ "false",
583
+ false
584
+ ],
585
+ [
586
+ "null",
587
+ null
588
+ ]
589
+ ]) {
590
+ if (rest.startsWith(word)) {
591
+ at += word.length;
592
+ return {
593
+ value,
594
+ cut: false
595
+ };
596
+ }
597
+ if (word.startsWith(rest)) throw new Cut();
598
+ }
599
+ return fail();
600
+ }, "readValue");
601
+ const readObject = /* @__PURE__ */ __name(() => {
602
+ const object = {};
603
+ open.add(object);
604
+ at += 1;
605
+ skipSpace();
606
+ if (text[at] === "}") {
607
+ at += 1;
608
+ open.delete(object);
609
+ return {
610
+ value: object,
611
+ cut: false
612
+ };
613
+ }
614
+ for (; ; ) {
615
+ skipSpace();
616
+ if (at >= text.length) return {
617
+ value: object,
618
+ cut: true
619
+ };
620
+ if (text[at] !== '"') fail();
621
+ const key = readString();
622
+ if (key.cut) return {
623
+ value: object,
624
+ cut: true
625
+ };
626
+ skipSpace();
627
+ if (at >= text.length) {
628
+ pending.set(object, key.value);
629
+ return {
630
+ value: object,
631
+ cut: true
632
+ };
633
+ }
634
+ if (text[at] !== ":") fail();
635
+ at += 1;
636
+ let member;
637
+ try {
638
+ member = readValue();
639
+ } catch (error) {
640
+ if (!(error instanceof Cut)) throw error;
641
+ pending.set(object, key.value);
642
+ return {
643
+ value: object,
644
+ cut: true
645
+ };
646
+ }
647
+ object[key.value] = member.value;
648
+ if (member.cut) {
649
+ pending.set(object, key.value);
650
+ return {
651
+ value: object,
652
+ cut: true
653
+ };
654
+ }
655
+ skipSpace();
656
+ if (at >= text.length) return {
657
+ value: object,
658
+ cut: true
659
+ };
660
+ if (text[at] === ",") {
661
+ at += 1;
662
+ continue;
663
+ }
664
+ if (text[at] === "}") {
665
+ at += 1;
666
+ open.delete(object);
667
+ return {
668
+ value: object,
669
+ cut: false
670
+ };
671
+ }
672
+ fail();
673
+ }
674
+ }, "readObject");
675
+ const readArray = /* @__PURE__ */ __name(() => {
676
+ const array = [];
677
+ open.add(array);
678
+ at += 1;
679
+ skipSpace();
680
+ if (text[at] === "]") {
681
+ at += 1;
682
+ open.delete(array);
683
+ return {
684
+ value: array,
685
+ cut: false
686
+ };
687
+ }
688
+ for (; ; ) {
689
+ let item;
690
+ try {
691
+ item = readValue();
692
+ } catch (error) {
693
+ if (!(error instanceof Cut)) throw error;
694
+ pending.set(array, array.length);
695
+ return {
696
+ value: array,
697
+ cut: true
698
+ };
699
+ }
700
+ array.push(item.value);
701
+ if (item.cut) {
702
+ pending.set(array, array.length - 1);
703
+ return {
704
+ value: array,
705
+ cut: true
706
+ };
707
+ }
708
+ skipSpace();
709
+ if (at >= text.length) return {
710
+ value: array,
711
+ cut: true
712
+ };
713
+ if (text[at] === ",") {
714
+ at += 1;
715
+ continue;
716
+ }
717
+ if (text[at] === "]") {
718
+ at += 1;
719
+ open.delete(array);
720
+ return {
721
+ value: array,
722
+ cut: false
723
+ };
724
+ }
725
+ fail();
726
+ }
727
+ }, "readArray");
728
+ let root;
729
+ try {
730
+ root = readValue();
731
+ } catch (error) {
732
+ if (error instanceof Cut) root = {
733
+ value: void 0,
734
+ cut: true
735
+ };
736
+ else return void 0;
737
+ }
738
+ skipSpace();
739
+ return {
740
+ value: root.value,
741
+ complete: !root.cut && at >= text.length,
742
+ isOpen: /* @__PURE__ */ __name((container) => open.has(container), "isOpen"),
743
+ pendingMember: /* @__PURE__ */ __name((container) => pending.get(container), "pendingMember")
744
+ };
745
+ }
746
+ __name(parsePartialJson, "parsePartialJson");
747
+
748
+ // src/tool-input-preview.ts
749
+ var DEFAULT_PREVIEW_THROTTLE_MS = 100;
750
+ function previewUiId(toolCallId) {
751
+ return `${toolCallId}:ui:0`;
752
+ }
753
+ __name(previewUiId, "previewUiId");
754
+ function withdrawPreviewEvent(shown) {
755
+ return {
756
+ kind: "ui",
757
+ id: shown.id,
758
+ component: shown.component,
759
+ props: {},
760
+ toolCallId: shown.toolCallId,
761
+ partial: true
762
+ };
763
+ }
764
+ __name(withdrawPreviewEvent, "withdrawPreviewEvent");
765
+ function previewToolInputs(inner, resolve, now = Date.now) {
766
+ const calls = /* @__PURE__ */ new Map();
767
+ const shown = /* @__PURE__ */ new Map();
768
+ const decoder3 = new TextDecoder();
769
+ let pending = "";
770
+ async function emit2(id, state, input, done) {
771
+ let rendered;
772
+ try {
773
+ const parsed = done && input !== void 0 ? {
774
+ value: input,
775
+ complete: true,
776
+ isOpen: /* @__PURE__ */ __name(() => false, "isOpen"),
777
+ pendingMember: /* @__PURE__ */ __name(() => void 0, "pendingMember")
778
+ } : parsePartialJson(state.text);
779
+ if (parsed === void 0) return;
780
+ rendered = state.preview.render({
781
+ value: parsed.value,
782
+ done: done || parsed.complete,
783
+ isOpen: parsed.isOpen,
784
+ pendingMember: parsed.pendingMember
785
+ });
786
+ } catch {
787
+ rendered = null;
788
+ }
789
+ if (rendered === void 0) return;
790
+ const uiId = previewUiId(id);
791
+ if (rendered === null) {
792
+ state.stopped = true;
793
+ const standing = shown.get(uiId);
794
+ if (standing !== void 0) {
795
+ shown.delete(uiId);
796
+ await inner.write(encodeStreamEvent(withdrawPreviewEvent(standing)));
797
+ }
798
+ return;
799
+ }
800
+ const props = JSON.stringify(rendered.props);
801
+ if (props === state.lastProps && rendered.component === state.component) return;
802
+ state.lastProps = props;
803
+ state.component = rendered.component;
804
+ state.lastAt = now();
805
+ shown.set(uiId, {
806
+ id: uiId,
807
+ component: rendered.component,
808
+ toolCallId: id
809
+ });
810
+ await inner.write(encodeStreamEvent({
811
+ kind: "ui",
812
+ id: uiId,
813
+ component: rendered.component,
814
+ props: JSON.parse(props),
815
+ ...rendered.version !== void 0 ? {
816
+ version: rendered.version
817
+ } : {},
818
+ toolCallId: id,
819
+ partial: true
820
+ }));
821
+ }
822
+ __name(emit2, "emit");
823
+ async function observe(event) {
824
+ if (event.kind === "tool-input-start") {
825
+ if (calls.has(event.id)) return;
826
+ let preview;
827
+ try {
828
+ preview = await resolve(event.name, event.id);
829
+ } catch {
830
+ preview = void 0;
831
+ }
832
+ if (preview !== void 0) {
833
+ calls.set(event.id, {
834
+ preview,
835
+ text: "",
836
+ lastAt: void 0,
837
+ lastProps: void 0,
838
+ component: void 0,
839
+ stopped: false
840
+ });
841
+ }
842
+ return;
843
+ }
844
+ if (event.kind === "tool-input-delta") {
845
+ const state = calls.get(event.id);
846
+ if (state === void 0 || state.stopped) return;
847
+ state.text += event.delta;
848
+ const throttle = state.preview.throttleMs ?? DEFAULT_PREVIEW_THROTTLE_MS;
849
+ if (state.lastAt !== void 0 && now() - state.lastAt < throttle) return;
850
+ await emit2(event.id, state, void 0, false);
851
+ return;
852
+ }
853
+ if (event.kind === "tool-input-available") {
854
+ const state = calls.get(event.id);
855
+ if (state === void 0) return;
856
+ calls.delete(event.id);
857
+ if (state.stopped) return;
858
+ await emit2(event.id, state, event.input, true);
859
+ }
860
+ }
861
+ __name(observe, "observe");
862
+ return {
863
+ writer: {
864
+ async write(chunk) {
865
+ await inner.write(chunk);
866
+ pending += decoder3.decode(chunk, {
867
+ stream: true
868
+ });
869
+ let newline = pending.indexOf("\n");
870
+ while (newline !== -1) {
871
+ const line = pending.slice(0, newline);
872
+ pending = pending.slice(newline + 1);
873
+ const event = line.trim().length > 0 ? decodeStreamEvent(line) : null;
874
+ if (event !== null) await observe(event);
875
+ newline = pending.indexOf("\n");
876
+ }
877
+ },
878
+ end: /* @__PURE__ */ __name(() => inner.end(), "end"),
879
+ fail: /* @__PURE__ */ __name((error) => inner.fail(error), "fail")
880
+ },
881
+ shown: /* @__PURE__ */ __name(() => [
882
+ ...shown.values()
883
+ ], "shown")
884
+ };
885
+ }
886
+ __name(previewToolInputs, "previewToolInputs");
887
+ function registryInputPreviews(registry, offered, scope) {
888
+ const names = new Set(offered.map((tool) => tool.name));
889
+ return async (toolName, toolCallId) => names.has(toolName) ? registry.previewInput(toolName, {
890
+ ...scope,
891
+ toolCallId
892
+ }) : void 0;
893
+ }
894
+ __name(registryInputPreviews, "registryInputPreviews");
895
+
471
896
  // src/tool-ui.ts
472
897
  var TOOL_STEP_UI = "@@nestjs-agent/tool-step-ui";
473
898
  function wrapToolStepOutput(output, ui, text) {
@@ -3354,6 +3779,7 @@ function defineComponent(definition) {
3354
3779
  }
3355
3780
  if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
3356
3781
  if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
3782
+ if (definition.streaming !== void 0 && definition.streaming !== "partial" && definition.streaming !== "complete") throw new TypeError("genui: streaming must be 'partial' or 'complete'");
3357
3783
  return definition;
3358
3784
  }
3359
3785
  __name(defineComponent, "defineComponent");
@@ -4712,6 +5138,19 @@ var ToolRegistry = class {
4712
5138
  unregister(name) {
4713
5139
  return this.entries.delete(name);
4714
5140
  }
5141
+ /**
5142
+ * How a streamed call of `name` is previewed while its input arrives (`ToolHandler.previewInput`),
5143
+ * or `undefined`. A preview that fails to build is no preview: it never fails the turn.
5144
+ */
5145
+ async previewInput(name, scope) {
5146
+ const handler = this.entries.get(name)?.handler;
5147
+ if (handler?.previewInput === void 0) return void 0;
5148
+ try {
5149
+ return await handler.previewInput(scope) ?? void 0;
5150
+ } catch {
5151
+ return void 0;
5152
+ }
5153
+ }
4715
5154
  spec(name) {
4716
5155
  return this.entries.get(name)?.spec;
4717
5156
  }
@@ -4770,6 +5209,9 @@ var ToolRegistry = class {
4770
5209
  * Run a tool. Re-checks that the tool is enabled and that the role allows it (defense-in-depth —
4771
5210
  * a call can reach here from a replayed durable step or an approval granted before the flag
4772
5211
  * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
5212
+ * A tool whose `describe()` answers `available: false` for the call's scope (actor, thread, agent,
5213
+ * `uiCapabilities`) is refused with {@link ToolNotFoundError}, as the unknown tool it was to the
5214
+ * model.
4773
5215
  */
4774
5216
  async invoke(name, input, ctx, policy, options = {}) {
4775
5217
  const { entry, value, ctx: withEmit } = await this.validated(name, input, ctx, policy, options);
@@ -4823,6 +5265,10 @@ var ToolRegistry = class {
4823
5265
  if (options.allowedTools !== void 0 && !options.allowedTools.includes(name)) {
4824
5266
  throw new ToolForbiddenError(name);
4825
5267
  }
5268
+ if (entry.handler.describe !== void 0) {
5269
+ const described = await entry.handler.describe(describeScopeOf(ctx));
5270
+ if (described?.available === false) throw new ToolNotFoundError(name);
5271
+ }
4826
5272
  const validation = await entry.spec.inputSchema["~standard"].validate(input);
4827
5273
  if (validation.issues !== void 0) {
4828
5274
  throw new ToolInputInvalidError(name, validation.issues);
@@ -4838,6 +5284,21 @@ var ToolRegistry = class {
4838
5284
  };
4839
5285
  }
4840
5286
  };
5287
+ function describeScopeOf(ctx) {
5288
+ return {
5289
+ actor: ctx.actor,
5290
+ ...ctx.threadId ? {
5291
+ threadId: ctx.threadId
5292
+ } : {},
5293
+ ...ctx.agentName !== void 0 ? {
5294
+ agentName: ctx.agentName
5295
+ } : {},
5296
+ ...ctx.uiCapabilities !== void 0 ? {
5297
+ uiCapabilities: ctx.uiCapabilities
5298
+ } : {}
5299
+ };
5300
+ }
5301
+ __name(describeScopeOf, "describeScopeOf");
4841
5302
  var DefaultRolesPolicy = class {
4842
5303
  static {
4843
5304
  __name(this, "DefaultRolesPolicy");
@@ -5221,6 +5682,21 @@ var RunCancelledError = class extends Error {
5221
5682
  this.name = "RunCancelledError";
5222
5683
  }
5223
5684
  };
5685
+ async function abortableModelCall(hooks, call) {
5686
+ const signal = hooks.abortSignal;
5687
+ let result2;
5688
+ try {
5689
+ result2 = await call(signal !== void 0 ? {
5690
+ abortSignal: signal
5691
+ } : {});
5692
+ } catch (error) {
5693
+ if (signal?.aborted === true) throw new RunCancelledError();
5694
+ throw error;
5695
+ }
5696
+ if (signal?.aborted === true) throw new RunCancelledError();
5697
+ return result2;
5698
+ }
5699
+ __name(abortableModelCall, "abortableModelCall");
5224
5700
  function agentFailureCode(error) {
5225
5701
  if (error instanceof RunCancelledError) {
5226
5702
  return "cancelled";
@@ -5678,13 +6154,14 @@ async function structureAnswer(schema, deps, input, hooks, messages, step) {
5678
6154
  fail: /* @__PURE__ */ __name(() => {
5679
6155
  }, "fail")
5680
6156
  };
5681
- const turn = await deps.model.runTurn({
6157
+ const turn = await abortableModelCall(hooks, (signal) => deps.model.runTurn({
5682
6158
  system,
5683
6159
  messages,
5684
6160
  tools: [],
5685
6161
  sink: discard,
5686
- outputSchema: schema
5687
- });
6162
+ outputSchema: schema,
6163
+ ...signal
6164
+ }));
5688
6165
  return {
5689
6166
  text: turn.text,
5690
6167
  usage: turn.usage,
@@ -5806,6 +6283,9 @@ function toolContext(deps, input, hooks) {
5806
6283
  ...deps.host !== void 0 ? {
5807
6284
  host: deps.host
5808
6285
  } : {},
6286
+ ...hooks.abortSignal !== void 0 ? {
6287
+ abortSignal: hooks.abortSignal
6288
+ } : {},
5809
6289
  // Replaced per tool call by the call's own collector (see the tool step); this one only serves
5810
6290
  // what runs outside a call — the elicitation wait.
5811
6291
  emitUi: createNoopEmitUi(hooks.runId)
@@ -7173,26 +7653,46 @@ ${block}`;
7173
7653
  writer
7174
7654
  }) : void 0;
7175
7655
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
7176
- const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
7177
- const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
7656
+ const previews = previewToolInputs(incremental?.writer ?? buffer?.writer ?? writer, registryInputPreviews(deps.registry, tools, {
7657
+ actor: input.actor,
7658
+ threadId: input.threadId,
7659
+ ...input.agentName !== void 0 ? {
7660
+ agentName: input.agentName
7661
+ } : {},
7662
+ ...input.uiCapabilities !== void 0 ? {
7663
+ uiCapabilities: input.uiCapabilities
7664
+ } : {}
7665
+ }));
7666
+ const frames = observeTurnFrames(previews.writer);
7667
+ const result2 = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => abortableModelCall(hooks, (signal) => deps.model.runTurn({
7178
7668
  system: prompt.system,
7179
7669
  messages: prompt.messages,
7180
7670
  tools,
7181
- sink: frames.writer
7182
- })), frames.summary()), deps);
7671
+ sink: frames.writer,
7672
+ ...signal
7673
+ }))), frames.summary()), deps);
7674
+ const shownPreviews = previews.shown();
7675
+ const previewed = shownPreviews.length > 0 ? {
7676
+ previews: shownPreviews
7677
+ } : {};
7183
7678
  if (incremental !== void 0) {
7184
7679
  await incremental.settled();
7185
7680
  const refusal = incremental.rejection();
7186
7681
  return {
7187
7682
  ...result2,
7683
+ ...previewed,
7188
7684
  releasedText: incremental.released(),
7189
7685
  ...refusal !== void 0 ? {
7190
7686
  gateRejection: refusal
7191
7687
  } : {}
7192
7688
  };
7193
7689
  }
7194
- return buffer === void 0 ? result2 : {
7690
+ return buffer === void 0 ? {
7691
+ ...result2,
7692
+ ...previewed
7693
+ } : {
7195
7694
  ...result2,
7695
+ ...previewed,
7196
7696
  bufferedFrames: buffer.frames()
7197
7697
  };
7198
7698
  });
@@ -7511,6 +8011,12 @@ ${block}`;
7511
8011
  if (toolUi.length > 0) {
7512
8012
  await deps.store.setMessageUi?.(assistant.id, mergeUi(turn.ui, toolUi));
7513
8013
  }
8014
+ for (const preview of turn.previews ?? []) {
8015
+ const pushed = turnCalls.toolUi.get(preview.toolCallId) ?? [];
8016
+ if (!pushed.some((component) => component.id === preview.id)) {
8017
+ await writer.write(encodeStreamEvent(withdrawPreviewEvent(preview)));
8018
+ }
8019
+ }
7514
8020
  for (const result2 of results) {
7515
8021
  await writer.write(encodeStreamEvent(outputFrame(result2)));
7516
8022
  }
@@ -7592,6 +8098,98 @@ ${toolText}`;
7592
8098
  }
7593
8099
  __name(runAgentLoop, "runAgentLoop");
7594
8100
 
8101
+ // src/action-proposal-tool-call.ts
8102
+ function toolCallUpdateForProposal(proposal) {
8103
+ const audit = proposal.decisionAudit;
8104
+ const toolCallId = proposal.originToolCallId;
8105
+ const decidedBy = {
8106
+ ...audit?.actorRef !== void 0 ? {
8107
+ executedByRef: audit.actorRef
8108
+ } : {},
8109
+ ...audit?.via !== void 0 ? {
8110
+ decidedVia: audit.via
8111
+ } : {}
8112
+ };
8113
+ switch (proposal.decision) {
8114
+ case "rejected":
8115
+ return {
8116
+ toolCallId,
8117
+ status: "rejected",
8118
+ ...decidedBy,
8119
+ ...audit?.reason !== void 0 ? {
8120
+ error: storableText2(audit.reason)
8121
+ } : {}
8122
+ };
8123
+ case "expired":
8124
+ return {
8125
+ toolCallId,
8126
+ status: "expired",
8127
+ ...audit?.via !== void 0 ? {
8128
+ decidedVia: audit.via
8129
+ } : {}
8130
+ };
8131
+ case "superseded":
8132
+ return {
8133
+ toolCallId,
8134
+ status: "expired",
8135
+ decidedVia: audit?.via ?? "supersession",
8136
+ error: proposal.supersededBy !== void 0 ? `superseded by proposal ${proposal.supersededBy}` : "superseded by a newer proposal"
8137
+ };
8138
+ case "approved": {
8139
+ const execution = proposal.execution;
8140
+ const remember = audit?.remember !== void 0 ? {
8141
+ remember: audit.remember
8142
+ } : {};
8143
+ if (execution?.status === "succeeded") {
8144
+ return {
8145
+ toolCallId,
8146
+ status: "executed",
8147
+ ...storableOutput(execution.result) ? {
8148
+ output: execution.result
8149
+ } : {},
8150
+ ...decidedBy,
8151
+ ...remember
8152
+ };
8153
+ }
8154
+ if (execution?.status === "failed") {
8155
+ return {
8156
+ toolCallId,
8157
+ status: "failed",
8158
+ error: storableText2(execution.error ?? "the approved action failed"),
8159
+ ...decidedBy,
8160
+ ...remember
8161
+ };
8162
+ }
8163
+ return null;
8164
+ }
8165
+ default:
8166
+ return null;
8167
+ }
8168
+ }
8169
+ __name(toolCallUpdateForProposal, "toolCallUpdateForProposal");
8170
+ function toolCallUpdateForTransition(previous, next) {
8171
+ const update = toolCallUpdateForProposal(next);
8172
+ if (update === null) return null;
8173
+ const before = previous === void 0 ? null : toolCallUpdateForProposal(previous);
8174
+ return before?.status === update.status ? null : update;
8175
+ }
8176
+ __name(toolCallUpdateForTransition, "toolCallUpdateForTransition");
8177
+ function storableOutput(result2) {
8178
+ if (result2 === void 0) return false;
8179
+ let json;
8180
+ try {
8181
+ json = JSON.stringify(result2);
8182
+ } catch {
8183
+ return false;
8184
+ }
8185
+ return json !== void 0 && !/\\u0000|\\ud[89ab][0-9a-f]{2}(?!\\ud[c-f])|(?<!\\ud[89ab][0-9a-f]{2})\\ud[c-f][0-9a-f]{2}/i.test(json);
8186
+ }
8187
+ __name(storableOutput, "storableOutput");
8188
+ function storableText2(text) {
8189
+ return text.replaceAll("\0", "");
8190
+ }
8191
+ __name(storableText2, "storableText");
8192
+
7595
8193
  // src/in-memory-action-proposal-store.ts
7596
8194
  var import_node_crypto2 = require("crypto");
7597
8195
  var InMemoryActionProposalStore = class {
@@ -7638,9 +8236,19 @@ var InMemoryActionProposalStore = class {
7638
8236
  status: "not_found"
7639
8237
  };
7640
8238
  const result2 = transition(row2, this.clock());
7641
- if (result2.proposal) this.rows.set(id, snapshotActionProposal(result2.proposal));
8239
+ if (result2.proposal) {
8240
+ this.rows.set(id, snapshotActionProposal(result2.proposal));
8241
+ this.proposalChanged(row2, result2.proposal);
8242
+ }
7642
8243
  return result2;
7643
8244
  }
8245
+ /**
8246
+ * Called after every proposal transition, with the row before and after — synchronously, so no
8247
+ * reader sees one without the other. {@link InMemoryAgentStore} settles the proposal's tool-call
8248
+ * record here (see `toolCallUpdateForTransition`).
8249
+ */
8250
+ proposalChanged(_previous, _next) {
8251
+ }
7644
8252
  async decideActionProposal(scope, id, command) {
7645
8253
  return this.mutate(scope, id, (row2, now) => transitionActionProposalDecision(row2, command, now));
7646
8254
  }
@@ -7721,7 +8329,11 @@ var InMemoryActionProposalStore = class {
7721
8329
  if (result2.proposal) replacements.push(result2.proposal);
7722
8330
  }
7723
8331
  this.rows.set(proposal.id, proposal);
7724
- for (const row2 of replacements) this.rows.set(row2.id, row2);
8332
+ for (const row2 of replacements) {
8333
+ const previous = this.rows.get(row2.id);
8334
+ this.rows.set(row2.id, row2);
8335
+ if (previous) this.proposalChanged(previous, row2);
8336
+ }
7725
8337
  return {
7726
8338
  status: "created",
7727
8339
  proposal: snapshotActionProposal(proposal)
@@ -8442,6 +9054,15 @@ var InMemoryAgentStore = class extends InMemoryActionProposalStore {
8442
9054
  return settled;
8443
9055
  }
8444
9056
  async updateToolCall(input) {
9057
+ this.applyToolCallUpdate(input);
9058
+ }
9059
+ /** An independent proposal moved: its `proposed` tool-call record follows it. */
9060
+ proposalChanged(previous, next) {
9061
+ const update = toolCallUpdateForTransition(previous, next);
9062
+ if (update === null || this.toolCalls.get(update.toolCallId)?.proposalId !== next.id) return;
9063
+ this.applyToolCallUpdate(update);
9064
+ }
9065
+ applyToolCallUpdate(input) {
8445
9066
  const row2 = this.toolCalls.get(input.toolCallId);
8446
9067
  if (row2 === void 0) {
8447
9068
  return;
@@ -9796,6 +10417,7 @@ __name(createFunctionalTool, "createFunctionalTool");
9796
10417
  DEFAULT_MAX_FACT_CHARS,
9797
10418
  DEFAULT_MAX_MEMORIES,
9798
10419
  DEFAULT_MAX_SKILLS,
10420
+ DEFAULT_PREVIEW_THROTTLE_MS,
9799
10421
  DEFAULT_REFUSAL_REASON,
9800
10422
  DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION,
9801
10423
  DEFAULT_TEXT_ACTION_PROPOSAL_REPLIES,
@@ -9914,9 +10536,12 @@ __name(createFunctionalTool, "createFunctionalTool");
9914
10536
  observeTurnFrames,
9915
10537
  offerMemories,
9916
10538
  offerSkills,
10539
+ parsePartialJson,
9917
10540
  parseTextActionProposalCommand,
9918
10541
  personaCatalogEntry,
9919
10542
  prepareActionProposal,
10543
+ previewToolInputs,
10544
+ previewUiId,
9920
10545
  ptBrActionProposalText,
9921
10546
  publishAgentDelegated,
9922
10547
  publishAgentMemoryResolved,
@@ -9937,6 +10562,7 @@ __name(createFunctionalTool, "createFunctionalTool");
9937
10562
  quotaWarning,
9938
10563
  readElicitationInput,
9939
10564
  readElicitationQuestions,
10565
+ registryInputPreviews,
9940
10566
  releaseGatedFrames,
9941
10567
  releaseThreadRun,
9942
10568
  rememberInputSchema,
@@ -9979,6 +10605,8 @@ __name(createFunctionalTool, "createFunctionalTool");
9979
10605
  toActionProposalView,
9980
10606
  toolCallApprovalFromRow,
9981
10607
  toolCallContext,
10608
+ toolCallUpdateForProposal,
10609
+ toolCallUpdateForTransition,
9982
10610
  traceLlmTurn,
9983
10611
  traceToolExecution,
9984
10612
  transitionActionProposalClaim,
@@ -10005,6 +10633,7 @@ __name(createFunctionalTool, "createFunctionalTool");
10005
10633
  withSkillTool,
10006
10634
  withToolTimeout,
10007
10635
  withTurnFrames,
10636
+ withdrawPreviewEvent,
10008
10637
  wrapToolPreflightDenied,
10009
10638
  wrapToolStepOutput,
10010
10639
  writeMemory