@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
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, expectTypeOf, it } from "vitest";
2
+ import type { Message as AgUiWireMessage } from "@ag-ui/client";
3
+ import { MessageSchema, UserMessageSchema } from "@ag-ui/core/schemas";
2
4
  import {
3
- MessageSchema,
4
- UserMessageSchema,
5
- type Message as AgUiWireMessage,
6
- } from "@ag-ui/client";
7
- import type { AppendMessage } from "@assistant-ui/core";
5
+ SimpleImageAttachmentAdapter,
6
+ type AppendMessage,
7
+ } from "@assistant-ui/core";
8
8
  import {
9
9
  fromAgUiMessages,
10
10
  toAgUiMessages,
@@ -82,6 +82,62 @@ describe("toAgUiMessages data URLs", () => {
82
82
  });
83
83
 
84
84
  describe("toAgUiMessages content metadata", () => {
85
+ it("preserves an uploaded image's filename through a snapshot round trip", async () => {
86
+ const adapter = new SimpleImageAttachmentAdapter();
87
+ const pending = await adapter.add({
88
+ file: new File([new Uint8Array([137, 80, 78, 71])], "cat.png", {
89
+ type: "image/png",
90
+ }),
91
+ });
92
+ const attachment = await adapter.send(pending);
93
+ const sent = toAgUiMessages([userMessage([], [attachment])]);
94
+ const restored = fromAgUiMessages(sent);
95
+
96
+ expect(restored[0]?.attachments?.[0]?.name).toBe("cat.png");
97
+ expect(sent[0]?.content).toEqual([
98
+ {
99
+ type: "image",
100
+ source: { type: "data", value: "iVBORw==", mimeType: "image/png" },
101
+ metadata: { filename: "cat.png" },
102
+ },
103
+ ]);
104
+ });
105
+
106
+ it.each(["image", "document"] as const)(
107
+ "preserves a %s file source and its metadata through a snapshot round trip",
108
+ (type) => {
109
+ const part = {
110
+ type,
111
+ source: {
112
+ type: "file",
113
+ value: `${type}-handle`,
114
+ provider: "openai",
115
+ mimeType: type === "image" ? "image/png" : "application/pdf",
116
+ },
117
+ metadata: { filename: `${type}.bin`, file_id: `${type}-1` },
118
+ };
119
+ const original = {
120
+ id: "u-file",
121
+ role: "user",
122
+ content: [
123
+ { type: "text", text: "label" },
124
+ {
125
+ type: "image",
126
+ source: { type: "url", value: "https://example.com/other.png" },
127
+ },
128
+ part,
129
+ ],
130
+ metadata: { trace: "kept" },
131
+ };
132
+
133
+ const imported = fromAgUiMessages([original]);
134
+ const exported = toAgUiMessages(imported);
135
+
136
+ expect(MessageSchema.parse(exported[0])).toEqual(original);
137
+ expect(exported).toEqual([original]);
138
+ },
139
+ );
140
+
85
141
  it("emits AgUiMessage values assignable to AG-UI Message", () => {
86
142
  expectTypeOf<AgUiMessage>().toExtend<AgUiWireMessage>();
87
143
  });
@@ -373,3 +429,91 @@ describe("toAgUiMessages percent-encoded data URLs", () => {
373
429
  expect(toAgUiMessages(rebuilt as never)).toEqual(sent);
374
430
  });
375
431
  });
432
+
433
+ describe("toAgUiMessages tool model content", () => {
434
+ const withModelContent = (modelContent: unknown): Message => ({
435
+ id: "a1",
436
+ role: "assistant",
437
+ content: [
438
+ {
439
+ type: "tool-call",
440
+ toolCallId: "call-1",
441
+ toolName: "download",
442
+ args: {},
443
+ argsText: "{}",
444
+ result: { private: "not model-visible" },
445
+ modelContent,
446
+ },
447
+ ],
448
+ });
449
+
450
+ it.each([
451
+ ["image", "image/png"],
452
+ ["document", "application/pdf"],
453
+ ["audio", "audio/wav"],
454
+ ["video", "video/mp4"],
455
+ ])(
456
+ "keeps %s model content through a snapshot round trip",
457
+ (type, mediaType) => {
458
+ const exported = toAgUiMessages([
459
+ withModelContent([
460
+ { type: "text", text: "Before" },
461
+ { type: "file", data: "YWJj", mediaType, filename: "result.bin" },
462
+ { type: "text", text: "After" },
463
+ ]),
464
+ ]);
465
+ const expected = {
466
+ id: "call-1:tool",
467
+ role: "tool",
468
+ toolCallId: "call-1",
469
+ content: [
470
+ { type: "text", text: "Before" },
471
+ {
472
+ type,
473
+ source: { type: "data", value: "YWJj", mimeType: mediaType },
474
+ metadata: { filename: "result.bin" },
475
+ },
476
+ { type: "text", text: "After" },
477
+ ],
478
+ };
479
+
480
+ expect(MessageSchema.parse(exported[1])).toEqual(expected);
481
+ expect(toAgUiMessages(fromAgUiMessages(exported))[1]).toEqual(expected);
482
+ },
483
+ );
484
+
485
+ it("sends a file-only model result instead of an empty string", () => {
486
+ const exported = toAgUiMessages([
487
+ withModelContent([
488
+ {
489
+ type: "file",
490
+ data: "https://example.com/report.pdf",
491
+ mediaType: "application/pdf",
492
+ },
493
+ ]),
494
+ ]);
495
+
496
+ expect(exported[1]?.content).toEqual([
497
+ {
498
+ type: "document",
499
+ source: {
500
+ type: "url",
501
+ value: "https://example.com/report.pdf",
502
+ mimeType: "application/pdf",
503
+ },
504
+ },
505
+ ]);
506
+ });
507
+
508
+ it.each([
509
+ { modelContent: [], content: "" },
510
+ { modelContent: [{ type: "text", text: "public" }], content: "public" },
511
+ ])(
512
+ "keeps text-only model content as $content",
513
+ ({ modelContent, content }) => {
514
+ expect(toAgUiMessages([withModelContent(modelContent)])[1]?.content).toBe(
515
+ content,
516
+ );
517
+ },
518
+ );
519
+ });
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, expectTypeOf, it } from "vitest";
2
- import { ToolSchema, type RunAgentParameters } from "@ag-ui/client";
2
+ import type { RunAgentParameters } from "@ag-ui/client";
3
+ import { ToolSchema } from "@ag-ui/core/schemas";
3
4
  import { toAgUiTools } from "./conversions";
4
5
 
5
6
  describe("toAgUiTools", () => {