@assistant-ui/react-ag-ui 0.0.71 → 0.1.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.
Files changed (70) hide show
  1. package/README.md +1 -1
  2. package/dist/agUiExtras.js +1 -2
  3. package/dist/hooks.js +1 -2
  4. package/dist/runtime/{AgUiThreadRuntimeCore.d.ts → AgUiThreadController.d.ts} +6 -12
  5. package/dist/runtime/AgUiThreadController.d.ts.map +1 -0
  6. package/dist/runtime/{AgUiThreadRuntimeCore.js → AgUiThreadController.js} +567 -194
  7. package/dist/runtime/AgUiThreadController.js.map +1 -0
  8. package/dist/runtime/adapter/conversions.d.ts +4 -4
  9. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  10. package/dist/runtime/adapter/conversions.js +120 -35
  11. package/dist/runtime/adapter/conversions.js.map +1 -1
  12. package/dist/runtime/adapter/replay-content.d.ts +9 -0
  13. package/dist/runtime/adapter/replay-content.d.ts.map +1 -0
  14. package/dist/runtime/adapter/replay-content.js +185 -0
  15. package/dist/runtime/adapter/replay-content.js.map +1 -0
  16. package/dist/runtime/adapter/run-aggregator.d.ts +3 -5
  17. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  18. package/dist/runtime/adapter/run-aggregator.js +48 -63
  19. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  20. package/dist/runtime/adapter/subscriber.d.ts +0 -24
  21. package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
  22. package/dist/runtime/adapter/subscriber.js +2 -24
  23. package/dist/runtime/adapter/subscriber.js.map +1 -1
  24. package/dist/runtime/adapter/tool-approval.d.ts +2 -0
  25. package/dist/runtime/adapter/tool-approval.d.ts.map +1 -1
  26. package/dist/runtime/adapter/tool-approval.js +23 -6
  27. package/dist/runtime/adapter/tool-approval.js.map +1 -1
  28. package/dist/runtime/agUiThreadState.d.ts +60 -0
  29. package/dist/runtime/agUiThreadState.d.ts.map +1 -0
  30. package/dist/runtime/agUiThreadState.js +94 -0
  31. package/dist/runtime/agUiThreadState.js.map +1 -0
  32. package/dist/runtime/event-parser.d.ts.map +1 -1
  33. package/dist/runtime/event-parser.js +46 -50
  34. package/dist/runtime/event-parser.js.map +1 -1
  35. package/dist/runtime/interrupt-internals.js +1 -2
  36. package/dist/runtime/logger.js +1 -2
  37. package/dist/runtime/mcp-tool-result.d.ts +3 -1
  38. package/dist/runtime/mcp-tool-result.d.ts.map +1 -1
  39. package/dist/runtime/mcp-tool-result.js +7 -5
  40. package/dist/runtime/mcp-tool-result.js.map +1 -1
  41. package/dist/runtime/types.d.ts +11 -35
  42. package/dist/runtime/types.d.ts.map +1 -1
  43. package/dist/runtime/types.js +1 -2
  44. package/dist/runtime/types.js.map +1 -1
  45. package/dist/useAgUiRuntime.js +3 -4
  46. package/dist/useAgUiRuntime.js.map +1 -1
  47. package/package.json +12 -11
  48. package/src/runtime/AgUiThreadController.deferred-results.test.ts +548 -0
  49. package/src/runtime/{AgUiThreadRuntimeCore.interrupt-cancel.test.ts → AgUiThreadController.interrupt-cancel.test.ts} +4 -4
  50. package/src/runtime/AgUiThreadController.replay.test.ts +3932 -0
  51. package/src/runtime/{AgUiThreadRuntimeCore.test.ts → AgUiThreadController.test.ts} +114 -6
  52. package/src/runtime/{AgUiThreadRuntimeCore.ts → AgUiThreadController.ts} +1037 -236
  53. package/src/runtime/adapter/conversions.content.test.ts +149 -5
  54. package/src/runtime/adapter/conversions.tools.test.ts +2 -1
  55. package/src/runtime/adapter/conversions.ts +249 -62
  56. package/src/runtime/adapter/replay-content.test.ts +235 -0
  57. package/src/runtime/adapter/replay-content.ts +306 -0
  58. package/src/runtime/adapter/run-aggregator.ts +79 -114
  59. package/src/runtime/adapter/subscriber.ts +1 -44
  60. package/src/runtime/adapter/tool-approval.ts +41 -3
  61. package/src/runtime/agUiThreadState.test.ts +224 -0
  62. package/src/runtime/agUiThreadState.ts +139 -0
  63. package/src/runtime/event-parser.ts +40 -58
  64. package/src/runtime/mcp-tool-result.ts +16 -2
  65. package/src/runtime/types.ts +16 -28
  66. package/src/useAgUiRuntime.fast-refresh.test.tsx +3 -3
  67. package/src/useAgUiRuntime.thread-switch.test.tsx +4 -4
  68. package/src/useAgUiRuntime.ts +3 -3
  69. package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +0 -1
  70. package/dist/runtime/AgUiThreadRuntimeCore.js.map +0 -1
@@ -6,7 +6,7 @@ import type {
6
6
  ThreadHistoryAdapter,
7
7
  ThreadMessage,
8
8
  } from "@assistant-ui/core";
9
- import { AgUiThreadRuntimeCore } from "./AgUiThreadRuntimeCore";
9
+ import { AgUiThreadController } from "./AgUiThreadController";
10
10
  import { makeLogger } from "./logger";
11
11
 
12
12
  const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
@@ -15,7 +15,7 @@ function createCore(
15
15
  history?: ThreadHistoryAdapter,
16
16
  agent = {} as AbstractAgent,
17
17
  ) {
18
- const core = new AgUiThreadRuntimeCore({
18
+ const core = new AgUiThreadController({
19
19
  agent,
20
20
  logger: makeLogger(),
21
21
  showThinking: true,
@@ -141,7 +141,7 @@ function deferredHistory(messages: ThreadMessage[] = [storedUser]) {
141
141
  };
142
142
  }
143
143
 
144
- describe("AgUiThreadRuntimeCore late history loading", () => {
144
+ describe("AgUiThreadController late history loading", () => {
145
145
  it("drops a queued send when loading is cancelled", async () => {
146
146
  const { history, finish } = deferredHistory();
147
147
  const runAgent = vi.fn(async () => {});
@@ -479,7 +479,7 @@ describe("AgUiThreadRuntimeCore late history loading", () => {
479
479
  });
480
480
  });
481
481
 
482
- describe("AgUiThreadRuntimeCore steerAway parent selection", () => {
482
+ describe("AgUiThreadController steerAway parent selection", () => {
483
483
  it("starts a root branch for an explicit null parent", async () => {
484
484
  const { core } = createCore();
485
485
  await core.append(userMessage("old"));
@@ -497,7 +497,7 @@ describe("AgUiThreadRuntimeCore steerAway parent selection", () => {
497
497
  });
498
498
  });
499
499
 
500
- describe("AgUiThreadRuntimeCore activity deltas", () => {
500
+ describe("AgUiThreadController activity deltas", () => {
501
501
  it("applies a delta to the snapshot the run streamed", async () => {
502
502
  type Subscriber = Record<string, ((payload?: unknown) => void) | undefined>;
503
503
  const runAgent = vi.fn(async (_input: unknown, subscriber: Subscriber) => {
@@ -521,7 +521,7 @@ describe("AgUiThreadRuntimeCore activity deltas", () => {
521
521
  subscriber.onRunFinalized?.();
522
522
  });
523
523
  const agent = { runAgent, abortRun: vi.fn() } as unknown as AbstractAgent;
524
- const core = new AgUiThreadRuntimeCore({
524
+ const core = new AgUiThreadController({
525
525
  agent,
526
526
  logger: makeLogger(),
527
527
  showThinking: true,
@@ -538,3 +538,111 @@ describe("AgUiThreadRuntimeCore activity deltas", () => {
538
538
  });
539
539
  });
540
540
  });
541
+
542
+ describe("AgUiThreadController setState", () => {
543
+ it("keeps a cancel the updater makes while a send waits for history", async () => {
544
+ const { history, finish } = deferredHistory();
545
+ const runAgent = vi.fn(async () => {});
546
+ const { core } = createCore(history, {
547
+ runAgent,
548
+ abortRun: vi.fn(),
549
+ } as unknown as AbstractAgent);
550
+ const loading = core.__internal_load();
551
+ const sending = core.append({
552
+ ...userMessage("cancelled"),
553
+ startRun: true,
554
+ });
555
+
556
+ core.setState(() => {
557
+ void core.cancel();
558
+ return { count: 1 };
559
+ });
560
+ finish();
561
+ await Promise.all([loading, sending]);
562
+
563
+ expect(core.getState()).toEqual({ count: 1 });
564
+ expect(core.getMessages().map((message) => message.id)).toEqual([
565
+ "restored",
566
+ ]);
567
+ expect(runAgent).not.toHaveBeenCalled();
568
+ });
569
+ });
570
+
571
+ describe("AgUiThreadController resuming a disconnected run", () => {
572
+ type Subscriber = {
573
+ onTextMessageStartEvent?: (payload: { event: unknown }) => void;
574
+ onTextMessageContentEvent?: (payload: { event: unknown }) => void;
575
+ onTextMessageEndEvent?: (payload: { event: unknown }) => void;
576
+ onRunFinishedEvent?: (payload: { event: unknown }) => void;
577
+ };
578
+ const sendText = (
579
+ subscriber: Subscriber,
580
+ messageId: string,
581
+ text: string,
582
+ end = true,
583
+ ) => {
584
+ subscriber.onTextMessageStartEvent?.({
585
+ event: { type: "TEXT_MESSAGE_START", messageId },
586
+ });
587
+ subscriber.onTextMessageContentEvent?.({
588
+ event: { type: "TEXT_MESSAGE_CONTENT", messageId, delta: text },
589
+ });
590
+ if (end) {
591
+ subscriber.onTextMessageEndEvent?.({
592
+ event: { type: "TEXT_MESSAGE_END", messageId },
593
+ });
594
+ }
595
+ };
596
+
597
+ it.each(["user", "assistant"] as const)(
598
+ "preserves replayed IDs and content when resume uses the %s parent",
599
+ async (parent) => {
600
+ const runAgent = vi.fn(
601
+ async (input: { runId: string }, subscriber: Subscriber) => {
602
+ if (runAgent.mock.calls.length === 1) {
603
+ sendText(subscriber, "assistant-a", "first", false);
604
+ throw new Error("stream disconnected");
605
+ }
606
+ sendText(subscriber, "assistant-a", "first and more");
607
+ sendText(subscriber, "assistant-b", "second answer");
608
+ subscriber.onRunFinishedEvent?.({
609
+ event: { type: "RUN_FINISHED", runId: input.runId },
610
+ });
611
+ },
612
+ );
613
+ const { core } = createCore(undefined, {
614
+ runAgent,
615
+ abortRun: vi.fn(),
616
+ } as unknown as AbstractAgent);
617
+
618
+ await expect(
619
+ core.append({ ...userMessage("question"), startRun: true }),
620
+ ).rejects.toThrow("stream disconnected");
621
+ const userId = core.getMessages()[0]!.id;
622
+ expect(core.getMessages()[1]).toMatchObject({
623
+ id: "assistant-a",
624
+ content: [{ type: "text", text: "first" }],
625
+ });
626
+
627
+ await core.resume({
628
+ parentId: parent === "user" ? userId : "assistant-a",
629
+ sourceId: null,
630
+ runConfig: {},
631
+ });
632
+
633
+ expect(runAgent).toHaveBeenCalledTimes(2);
634
+ expect(core.getMessages().map((message) => message.id)).toEqual([
635
+ userId,
636
+ "assistant-a",
637
+ "assistant-b",
638
+ ]);
639
+ expect(core.getMessages().slice(1)).toMatchObject([
640
+ { content: [{ type: "text", text: "first and more" }] },
641
+ { content: [{ type: "text", text: "second answer" }] },
642
+ ]);
643
+ expect(
644
+ core.getMessageRepository().messages.map(({ parentId }) => parentId),
645
+ ).toEqual([null, userId, "assistant-a"]);
646
+ },
647
+ );
648
+ });