@agent-native/toolkit 0.225.2-nightly-20261106152255 → 0.226.0

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 (174) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +124 -9
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +1 -1
  6. package/dist/app/agentkit/react/core-file-storage.d.ts +21 -0
  7. package/dist/app/agentkit/react/core-file-storage.d.ts.map +1 -0
  8. package/dist/app/agentkit/react/core-file-storage.js +3 -0
  9. package/dist/app/agentkit/react/core-file-storage.js.map +1 -0
  10. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  11. package/dist/app/auth/AuthPage.js +89 -10
  12. package/dist/app/auth/AuthPage.js.map +1 -1
  13. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  14. package/dist/app/chat/AgentKitAssistantChat.js +39 -58
  15. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  16. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  17. package/dist/app/chat/MultiTabAssistantChat.js +3 -1
  18. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  19. package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
  20. package/dist/app/chat/agentkit-chat/root.js +66 -2
  21. package/dist/app/chat/agentkit-chat/root.js.map +1 -1
  22. package/dist/app/chat/chat/surface-types.d.ts +1 -0
  23. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  24. package/dist/app/chat/chat/surface-types.js.map +1 -1
  25. package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
  26. package/dist/app/chat/composer/runtime-adapters.js +2 -0
  27. package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
  28. package/dist/app/chat/composer/use-agentkit-capabilities.d.ts +3 -1
  29. package/dist/app/chat/composer/use-agentkit-capabilities.d.ts.map +1 -1
  30. package/dist/app/chat/composer/use-agentkit-capabilities.js +11 -11
  31. package/dist/app/chat/composer/use-agentkit-capabilities.js.map +1 -1
  32. package/dist/app/chat/composer/use-workspace-composer-menu.d.ts +3 -0
  33. package/dist/app/chat/composer/use-workspace-composer-menu.d.ts.map +1 -0
  34. package/dist/app/chat/composer/use-workspace-composer-menu.js +80 -0
  35. package/dist/app/chat/composer/use-workspace-composer-menu.js.map +1 -0
  36. package/dist/app/chat/index.d.ts +1 -0
  37. package/dist/app/chat/index.d.ts.map +1 -1
  38. package/dist/app/chat/index.js +1 -0
  39. package/dist/app/chat/index.js.map +1 -1
  40. package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts +5 -0
  41. package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts.map +1 -0
  42. package/dist/app/chat/prepare-prompt-composer-agent-handoff.js +108 -0
  43. package/dist/app/chat/prepare-prompt-composer-agent-handoff.js.map +1 -0
  44. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  45. package/dist/app/i18n/catalogs/ar-SA.js +6 -1
  46. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  47. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  48. package/dist/app/i18n/catalogs/de-DE.js +6 -1
  49. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  50. package/dist/app/i18n/catalogs/en-US.d.ts +6 -1
  51. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/en-US.js +6 -1
  53. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  54. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/es-ES.js +6 -1
  56. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  57. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/fr-FR.js +6 -1
  59. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  60. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/hi-IN.js +6 -1
  62. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  63. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/ja-JP.js +6 -1
  65. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  66. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  67. package/dist/app/i18n/catalogs/ko-KR.js +6 -1
  68. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  69. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  70. package/dist/app/i18n/catalogs/pt-BR.js +6 -1
  71. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  72. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  73. package/dist/app/i18n/catalogs/zh-CN.js +6 -1
  74. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  75. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  76. package/dist/app/i18n/catalogs/zh-TW.js +6 -1
  77. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  78. package/dist/app/resources/McpIntegrationDialog.d.ts +3 -1
  79. package/dist/app/resources/McpIntegrationDialog.d.ts.map +1 -1
  80. package/dist/app/resources/McpIntegrationDialog.js +2 -2
  81. package/dist/app/resources/McpIntegrationDialog.js.map +1 -1
  82. package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
  83. package/dist/app/settings/useBuilderStatus.js +119 -40
  84. package/dist/app/settings/useBuilderStatus.js.map +1 -1
  85. package/dist/composer/ComposerContextMenu.d.ts +36 -2
  86. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  87. package/dist/composer/ComposerContextMenu.js +108 -40
  88. package/dist/composer/ComposerContextMenu.js.map +1 -1
  89. package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
  90. package/dist/composer/ComposerContextPickerDialog.js +63 -26
  91. package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
  92. package/dist/composer/ComposerPlusMenu.d.ts +5 -3
  93. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
  94. package/dist/composer/ComposerPlusMenu.js +166 -121
  95. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  96. package/dist/composer/MentionItemMedia.d.ts.map +1 -1
  97. package/dist/composer/MentionItemMedia.js +3 -1
  98. package/dist/composer/MentionItemMedia.js.map +1 -1
  99. package/dist/composer/PromptComposer.d.ts +1 -0
  100. package/dist/composer/PromptComposer.d.ts.map +1 -1
  101. package/dist/composer/PromptComposer.js +1 -0
  102. package/dist/composer/PromptComposer.js.map +1 -1
  103. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  104. package/dist/composer/TiptapComposer.js +117 -50
  105. package/dist/composer/TiptapComposer.js.map +1 -1
  106. package/dist/composer/index.d.ts +1 -0
  107. package/dist/composer/index.d.ts.map +1 -1
  108. package/dist/composer/runtime-adapters.d.ts +15 -0
  109. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  110. package/dist/composer/runtime-adapters.js.map +1 -1
  111. package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
  112. package/dist/composer/use-composer-panel-placement.js +2 -4
  113. package/dist/composer/use-composer-panel-placement.js.map +1 -1
  114. package/dist/composer/use-mention-search.d.ts +7 -0
  115. package/dist/composer/use-mention-search.d.ts.map +1 -1
  116. package/dist/composer/use-mention-search.js +86 -19
  117. package/dist/composer/use-mention-search.js.map +1 -1
  118. package/dist/composer/useComposerContextPicker.d.ts +5 -1
  119. package/dist/composer/useComposerContextPicker.d.ts.map +1 -1
  120. package/dist/composer/useComposerContextPicker.js +18 -4
  121. package/dist/composer/useComposerContextPicker.js.map +1 -1
  122. package/package.json +4 -4
  123. package/src/app/agentkit/react/components.tsx +158 -11
  124. package/src/app/agentkit/react/composer-context.spec.tsx +180 -0
  125. package/src/app/agentkit/react/core-file-storage.tsx +14 -0
  126. package/src/app/auth/AuthPage.interaction.spec.tsx +143 -0
  127. package/src/app/auth/AuthPage.tsx +159 -13
  128. package/src/app/chat/AgentKitAssistantChat.spec.tsx +65 -3
  129. package/src/app/chat/AgentKitAssistantChat.tsx +57 -65
  130. package/src/app/chat/MultiTabAssistantChat.spec.tsx +21 -0
  131. package/src/app/chat/MultiTabAssistantChat.tsx +4 -0
  132. package/src/app/chat/agentkit-chat/root.tsx +91 -8
  133. package/src/app/chat/chat/surface-types.ts +1 -0
  134. package/src/app/chat/composer/runtime-adapters.tsx +2 -0
  135. package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +26 -1
  136. package/src/app/chat/composer/use-agentkit-capabilities.ts +11 -11
  137. package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +2 -4
  138. package/src/app/chat/composer/use-workspace-composer-menu.spec.tsx +174 -0
  139. package/src/app/chat/composer/use-workspace-composer-menu.tsx +122 -0
  140. package/src/app/chat/index.ts +1 -0
  141. package/src/app/chat/prepare-prompt-composer-agent-handoff.spec.ts +162 -0
  142. package/src/app/chat/prepare-prompt-composer-agent-handoff.ts +134 -0
  143. package/src/app/i18n/catalogs/ar-SA.ts +6 -1
  144. package/src/app/i18n/catalogs/de-DE.ts +6 -1
  145. package/src/app/i18n/catalogs/en-US.ts +6 -1
  146. package/src/app/i18n/catalogs/es-ES.ts +6 -1
  147. package/src/app/i18n/catalogs/fr-FR.ts +6 -1
  148. package/src/app/i18n/catalogs/hi-IN.ts +6 -1
  149. package/src/app/i18n/catalogs/ja-JP.ts +6 -1
  150. package/src/app/i18n/catalogs/ko-KR.ts +6 -1
  151. package/src/app/i18n/catalogs/pt-BR.ts +6 -1
  152. package/src/app/i18n/catalogs/zh-CN.ts +6 -1
  153. package/src/app/i18n/catalogs/zh-TW.ts +6 -1
  154. package/src/app/resources/McpIntegrationDialog.tsx +5 -0
  155. package/src/app/settings/useBuilderStatus.spec.tsx +229 -13
  156. package/src/app/settings/useBuilderStatus.ts +145 -47
  157. package/src/composer/ComposerContextMenu.spec.tsx +95 -32
  158. package/src/composer/ComposerContextMenu.tsx +284 -73
  159. package/src/composer/ComposerContextPicker.spec.tsx +13 -14
  160. package/src/composer/ComposerContextPickerDialog.spec.tsx +40 -3
  161. package/src/composer/ComposerContextPickerDialog.tsx +158 -24
  162. package/src/composer/ComposerPlusMenu.parity.spec.tsx +442 -62
  163. package/src/composer/ComposerPlusMenu.tsx +322 -237
  164. package/src/composer/MentionItemMedia.tsx +3 -0
  165. package/src/composer/PromptComposer.context.spec.tsx +253 -82
  166. package/src/composer/PromptComposer.tsx +2 -0
  167. package/src/composer/TiptapComposer.spec.ts +6 -8
  168. package/src/composer/TiptapComposer.tsx +148 -55
  169. package/src/composer/index.ts +5 -0
  170. package/src/composer/runtime-adapters.tsx +18 -0
  171. package/src/composer/use-composer-panel-placement.ts +2 -4
  172. package/src/composer/use-mention-search.spec.tsx +99 -1
  173. package/src/composer/use-mention-search.ts +101 -24
  174. package/src/composer/useComposerContextPicker.ts +25 -5
@@ -7,6 +7,7 @@ import {
7
7
  IconMail,
8
8
  IconMessageChatbot,
9
9
  IconPresentation,
10
+ IconPlugConnected,
10
11
  IconStack2,
11
12
  IconUser,
12
13
  } from "@tabler/icons-react";
@@ -41,6 +42,8 @@ function LegacyMentionIcon({
41
42
  );
42
43
  }
43
44
  switch (icon) {
45
+ case "integration":
46
+ return <IconPlugConnected {...iconProps} />;
44
47
  case "folder":
45
48
  return <IconFolder {...iconProps} />;
46
49
  case "document":
@@ -105,7 +105,7 @@ describe("controlled composer context", () => {
105
105
  );
106
106
  const menu = document.querySelector('[role="menu"]')!;
107
107
  expect(menu.querySelector('[role="searchbox"]')).toBeNull();
108
- expect(menu.textContent).toContain("Upload File");
108
+ expect(menu.textContent).toContain("Files and images");
109
109
  expect(menu.textContent).toContain("Add context");
110
110
  await act(async () =>
111
111
  document.dispatchEvent(
@@ -522,22 +522,9 @@ describe("controlled composer context", () => {
522
522
  );
523
523
 
524
524
  const plusMenu = document.querySelector<HTMLElement>('[role="menu"]');
525
- expect(plusMenu?.textContent).toContain("Upload File");
525
+ expect(plusMenu?.textContent).toContain("Files and images");
526
526
  expect(plusMenu?.textContent).toContain("Add context");
527
- expect(plusMenu?.textContent).not.toContain("Choose source");
528
- const addContext = Array.from(
529
- document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
530
- ).find((item) => item.textContent === "Add context")!;
531
- await act(async () => {
532
- addContext.focus();
533
- addContext.dispatchEvent(
534
- new KeyboardEvent("keydown", {
535
- key: "ArrowRight",
536
- bubbles: true,
537
- cancelable: true,
538
- }),
539
- );
540
- });
527
+ expect(plusMenu?.textContent).toContain("Choose source");
541
528
  const sourceAction = Array.from(
542
529
  document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
543
530
  ).find((item) => item.textContent === "Choose source")!;
@@ -575,19 +562,6 @@ describe("controlled composer context", () => {
575
562
  }),
576
563
  );
577
564
  });
578
- const addContext = Array.from(
579
- document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
580
- ).find((item) => item.textContent === "Add context")!;
581
- await act(async () => {
582
- addContext.focus();
583
- addContext.dispatchEvent(
584
- new KeyboardEvent("keydown", {
585
- key: "ArrowRight",
586
- bubbles: true,
587
- cancelable: true,
588
- }),
589
- );
590
- });
591
565
  const integrations = Array.from(
592
566
  document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
593
567
  ).find((item) => item.textContent === "Integrations")!;
@@ -645,41 +619,17 @@ describe("controlled composer context", () => {
645
619
  }),
646
620
  );
647
621
  });
648
- const addContext = Array.from(
649
- document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
650
- ).find((item) => item.textContent === "Add context");
651
- if (addContext) {
652
- await act(async () => {
653
- addContext.focus();
654
- addContext.dispatchEvent(
655
- new KeyboardEvent("keydown", {
656
- key: "ArrowRight",
657
- bubbles: true,
658
- cancelable: true,
659
- }),
660
- );
661
- });
662
- }
663
- const section = Array.from(
664
- document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
665
- ).find((item) => item.textContent === "Connected Agents");
666
- if (section) {
667
- await act(async () => {
668
- section.focus();
669
- section.dispatchEvent(
670
- new KeyboardEvent("keydown", {
671
- key: "ArrowRight",
672
- bubbles: true,
673
- cancelable: true,
674
- }),
675
- );
676
- });
677
- }
678
- const row = Array.from(
622
+ const search = Array.from(
679
623
  document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
680
- ).find(
681
- (item) => item.textContent === "SlidesPresentations in this workspace",
682
- )!;
624
+ ).find((item) => item.textContent === "Search workspace…")!;
625
+ expect(search).toBeDefined();
626
+ await act(async () => {
627
+ search.click();
628
+ await new Promise((resolve) => setTimeout(resolve, 0));
629
+ });
630
+ const row = Array.from(
631
+ document.querySelectorAll<HTMLElement>('[role="option"]'),
632
+ ).find((item) => item.textContent === "Slides")!;
683
633
  expect(row).toBeDefined();
684
634
  await act(async () => row.click());
685
635
  await act(async () => {
@@ -829,7 +779,7 @@ describe("controlled composer context", () => {
829
779
  }
830
780
  bounds = { ...bounds, y: window.innerHeight + 500 };
831
781
  await act(async () => observation.resize());
832
- if (plusMenu) expect(panel.style.maxHeight).toBe("280px");
782
+ if (plusMenu) expect(panel.style.maxHeight).toBe("480px");
833
783
  bounds = { ...bounds, x: 120, y: 160 };
834
784
  await act(async () => window.dispatchEvent(new Event("scroll")));
835
785
  if (!plusMenu) {
@@ -840,7 +790,7 @@ describe("controlled composer context", () => {
840
790
  "--radix-popover-content-available-height",
841
791
  );
842
792
  } else {
843
- expect(panel.style.maxHeight).toBe("246px");
793
+ expect(panel.style.maxHeight).toBe("446px");
844
794
  }
845
795
  await act(async () =>
846
796
  editor.dispatchEvent(
@@ -858,7 +808,7 @@ describe("controlled composer context", () => {
858
808
  vi.restoreAllMocks();
859
809
  },
860
810
  );
861
- it("requests storage setup only after choosing Upload File", async () => {
811
+ it("requests storage setup only after choosing Files and images", async () => {
862
812
  const onAttachmentRequest = vi.fn();
863
813
  await mount({
864
814
  attachmentsEnabled: false,
@@ -882,7 +832,7 @@ describe("controlled composer context", () => {
882
832
  });
883
833
  const uploadFile = Array.from(
884
834
  document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
885
- ).find((button) => button.textContent?.includes("Upload File"));
835
+ ).find((button) => button.textContent?.includes("Files and images"));
886
836
  expect(uploadFile).toBeDefined();
887
837
  await act(async () => uploadFile!.click());
888
838
  await act(async () => new Promise((resolve) => setTimeout(resolve, 0)));
@@ -909,11 +859,243 @@ describe("controlled composer context", () => {
909
859
  );
910
860
  const uploadFile = document.querySelector<HTMLElement>('[role="menuitem"]');
911
861
  expect(uploadFile).toBeDefined();
912
- expect(uploadFile?.textContent).toContain("Upload File");
862
+ expect(uploadFile?.textContent).toContain("Files and images");
913
863
  await act(async () => uploadFile!.click());
914
864
  await act(async () => new Promise((resolve) => setTimeout(resolve, 0)));
915
865
  expect(onAttachmentRequest).toHaveBeenCalledOnce();
916
866
  });
867
+ it("shows localized source errors in the reference picker", async () => {
868
+ vi.stubGlobal(
869
+ "fetch",
870
+ vi.fn(
871
+ async () =>
872
+ new Response(
873
+ JSON.stringify({
874
+ error: {
875
+ code: "mention_source_unavailable",
876
+ message: "Impossible de charger les références.",
877
+ source: "dashboards",
878
+ },
879
+ }),
880
+ { status: 503 },
881
+ ),
882
+ ),
883
+ );
884
+ await mount({
885
+ contextMenuItems: [
886
+ {
887
+ id: "dashboards",
888
+ label: "Reference a dashboard…",
889
+ section: "context",
890
+ referenceType: "dashboard",
891
+ referencePicker: { source: "dashboards", referenceType: "dashboard" },
892
+ },
893
+ ],
894
+ });
895
+ await act(async () => {
896
+ container
897
+ .querySelector<HTMLElement>('button[aria-label="Add context"]')!
898
+ .dispatchEvent(
899
+ new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
900
+ );
901
+ });
902
+ await act(async () => {
903
+ Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]'))
904
+ .find((item) => item.textContent === "Reference a dashboard…")!
905
+ .click();
906
+ await new Promise((resolve) => setTimeout(resolve, 0));
907
+ });
908
+ expect(document.body.textContent).toContain(
909
+ "Impossible de charger les références.",
910
+ );
911
+ });
912
+ it("opens scoped native references in one dialog and excludes them from workspace search", async () => {
913
+ const fetch = vi.fn(
914
+ async () =>
915
+ new Response(
916
+ JSON.stringify({
917
+ items: [
918
+ {
919
+ id: "dashboard:one",
920
+ label: "Revenue overview",
921
+ source: "dashboards",
922
+ refType: "dashboard",
923
+ refId: "one",
924
+ section: "Dashboards",
925
+ },
926
+ {
927
+ id: "file:brief",
928
+ label: "Launch brief",
929
+ source: "resource:personal",
930
+ refType: "file",
931
+ refPath: "brief.md",
932
+ section: "Files",
933
+ },
934
+ ],
935
+ }),
936
+ ),
937
+ );
938
+ vi.stubGlobal("fetch", fetch);
939
+ const { composerRef, onSubmit } = await mount({
940
+ contextMenuItems: [
941
+ {
942
+ id: "dashboards",
943
+ label: "Reference a dashboard…",
944
+ section: "context",
945
+ referenceType: "dashboard",
946
+ referencePicker: { source: "dashboards", referenceType: "dashboard" },
947
+ },
948
+ ],
949
+ });
950
+ const open = async () => {
951
+ await act(async () => {
952
+ container
953
+ .querySelector<HTMLElement>('button[aria-label="Add context"]')!
954
+ .dispatchEvent(
955
+ new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
956
+ );
957
+ });
958
+ };
959
+ await open();
960
+ expect(fetch).not.toHaveBeenCalled();
961
+ expect(
962
+ document.querySelector('[role="menu"] .skeleton-shimmer'),
963
+ ).toBeNull();
964
+ const choose = async (name: string) => {
965
+ await act(async () => {
966
+ Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]'))
967
+ .find((item) => item.textContent === name)!
968
+ .click();
969
+ await new Promise((resolve) => setTimeout(resolve, 0));
970
+ });
971
+ };
972
+ await choose("Reference a dashboard…");
973
+ expect(fetch).toHaveBeenCalledWith(
974
+ expect.stringContaining("source=dashboards"),
975
+ { signal: expect.any(AbortSignal) },
976
+ );
977
+ expect(
978
+ document
979
+ .querySelector('[role="dialog"]')
980
+ ?.getAttribute("aria-labelledby"),
981
+ ).toBeTruthy();
982
+ expect(document.activeElement?.getAttribute("role")).toBe("combobox");
983
+ expect(
984
+ Array.from(document.querySelectorAll('[role="option"]')).map(
985
+ (item) => item.textContent,
986
+ ),
987
+ ).toEqual(["Revenue overview"]);
988
+ await act(async () =>
989
+ document.querySelector<HTMLElement>('[role="option"]')!.click(),
990
+ );
991
+ expect(document.querySelector('[role="dialog"]')).toBeNull();
992
+ expect(document.activeElement).toBe(
993
+ container.querySelector(".ProseMirror"),
994
+ );
995
+ await open();
996
+ await choose("Search workspace…");
997
+ expect(
998
+ Array.from(document.querySelectorAll('[role="option"]')).map(
999
+ (item) => item.textContent,
1000
+ ),
1001
+ ).toEqual(["Launch brief"]);
1002
+ await act(async () =>
1003
+ document.dispatchEvent(
1004
+ new KeyboardEvent("keydown", {
1005
+ key: "Escape",
1006
+ bubbles: true,
1007
+ cancelable: true,
1008
+ }),
1009
+ ),
1010
+ );
1011
+ await act(async () => composerRef.current!.submit());
1012
+ expect(onSubmit.mock.calls[0][2]).toEqual(
1013
+ expect.arrayContaining([
1014
+ expect.objectContaining({ refType: "dashboard", refId: "one" }),
1015
+ ]),
1016
+ );
1017
+ expect(onSubmit.mock.calls[0][3].references).toEqual(
1018
+ onSubmit.mock.calls[0][2],
1019
+ );
1020
+ });
1021
+
1022
+ it("aborts a workspace picker on identity change before stale results can be selected", async () => {
1023
+ let scopeKey = "org:one";
1024
+ let finish: (response: Response) => void = () => {
1025
+ throw new Error("No search started");
1026
+ };
1027
+ const fetch = vi.fn(
1028
+ () =>
1029
+ new Promise<Response>((resolve) => {
1030
+ finish = resolve;
1031
+ }),
1032
+ );
1033
+ vi.stubGlobal("fetch", fetch);
1034
+ const onReferencesChange = vi.fn();
1035
+ const render = async () => {
1036
+ await act(async () =>
1037
+ root.render(
1038
+ <ComposerRuntimeAdaptersProvider
1039
+ adapters={{
1040
+ resources: { useWorkspaceMenu: () => ({ scopeKey, items: [] }) },
1041
+ }}
1042
+ >
1043
+ <PromptComposer
1044
+ onSubmit={vi.fn()}
1045
+ onReferencesChange={onReferencesChange}
1046
+ showModelSelector={false}
1047
+ modelStatusChecksEnabled={false}
1048
+ includeDefaultSlashSkills={false}
1049
+ voiceEnabled={false}
1050
+ />
1051
+ </ComposerRuntimeAdaptersProvider>,
1052
+ ),
1053
+ );
1054
+ };
1055
+ await render();
1056
+ onReferencesChange.mockClear();
1057
+ const open = async () => {
1058
+ await act(async () => {
1059
+ const trigger = container.querySelector<HTMLElement>(
1060
+ 'button[aria-label="Add context"]',
1061
+ );
1062
+ if (!trigger) throw new Error("Missing context trigger");
1063
+ trigger.dispatchEvent(
1064
+ new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
1065
+ );
1066
+ });
1067
+ await act(async () => {
1068
+ const action = Array.from(
1069
+ document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
1070
+ ).find((item) => item.textContent === "Search workspace…");
1071
+ if (!action) throw new Error("Missing workspace search");
1072
+ action.click();
1073
+ await new Promise((resolve) => setTimeout(resolve, 0));
1074
+ });
1075
+ };
1076
+ await open();
1077
+ const signal = fetch.mock.calls[0][1].signal;
1078
+ expect(signal.aborted).toBe(false);
1079
+ expect(document.querySelector('[role="dialog"]')).not.toBeNull();
1080
+ scopeKey = "org:two";
1081
+ await render();
1082
+ expect(signal.aborted).toBe(true);
1083
+ expect(document.querySelector('[role="dialog"]')).toBeNull();
1084
+ await act(async () =>
1085
+ finish(
1086
+ new Response(
1087
+ JSON.stringify({
1088
+ items: [
1089
+ { id: "stale", label: "Private old reference", refType: "file" },
1090
+ ],
1091
+ }),
1092
+ ),
1093
+ ),
1094
+ );
1095
+ expect(document.body.textContent).not.toContain("Private old reference");
1096
+ expect(onReferencesChange).not.toHaveBeenCalled();
1097
+ });
1098
+
917
1099
  async function mount(props: Partial<PromptComposerProps> = {}) {
918
1100
  const composerRef = React.createRef<TiptapComposerHandle>();
919
1101
  const onSubmit = vi.fn();
@@ -1322,19 +1504,6 @@ describe("controlled composer context", () => {
1322
1504
  new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
1323
1505
  ),
1324
1506
  );
1325
- const addContext = Array.from(
1326
- document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
1327
- ).find((element) => element.textContent === "Add context")!;
1328
- await act(async () => {
1329
- addContext.focus();
1330
- addContext.dispatchEvent(
1331
- new KeyboardEvent("keydown", {
1332
- key: "ArrowRight",
1333
- bubbles: true,
1334
- cancelable: true,
1335
- }),
1336
- );
1337
- });
1338
1507
  const option = Array.from(
1339
1508
  document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
1340
1509
  ).find((element) => element.textContent?.includes("Choose brief"))!;
@@ -1683,7 +1852,9 @@ describe("controlled composer context", () => {
1683
1852
  expect(editor.textContent).toBe("Typed while readiness was checking");
1684
1853
  expect(sendButton.disabled).toBe(false);
1685
1854
  expect(sendButton.getAttribute("aria-busy")).toBeNull();
1686
- const statuses = container.querySelectorAll('[role="status"]');
1855
+ const statuses = Array.from(
1856
+ container.querySelectorAll('[role="status"]'),
1857
+ ).filter((status) => status.textContent?.trim());
1687
1858
  expect(statuses).toHaveLength(1);
1688
1859
  expect(statuses[0]?.textContent).toContain(
1689
1860
  "agentChat.setup.providerStatusUnavailable",
@@ -100,6 +100,7 @@ const MAX_INLINE_TEXT_FILE_CHARS = 60_000;
100
100
  export type PromptComposerFile = File;
101
101
 
102
102
  export interface PromptComposerSubmitOptions {
103
+ references?: Reference[];
103
104
  intent?: ComposerSubmitIntent;
104
105
  steer?: boolean;
105
106
  /** Clear the submitted draft once the host owns the message and its failure recovery. */
@@ -1119,6 +1120,7 @@ function PromptComposerInner({
1119
1120
  inlineTextAttachments,
1120
1121
  });
1121
1122
  await onSubmit(finalText, files, references, {
1123
+ references,
1122
1124
  intent: submitOptions?.intent ?? "immediate",
1123
1125
  ...(submitOptions?.steer ? { steer: true } : {}),
1124
1126
  onLocalSubmit: submitOptions?.onLocalSubmit,
@@ -457,7 +457,7 @@ describe("createTiptapComposerExtensions", () => {
457
457
  });
458
458
  act(() => {
459
459
  Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]'))
460
- .find((button) => button.textContent?.trim() === "Add context")
460
+ .find((button) => button.textContent?.trim() === "Commands")
461
461
  ?.dispatchEvent(
462
462
  new KeyboardEvent("keydown", {
463
463
  key: "ArrowRight",
@@ -820,10 +820,6 @@ describe("createTiptapComposerExtensions", () => {
820
820
  );
821
821
  await new Promise((resolve) => setTimeout(resolve, 0));
822
822
  });
823
- const addContextItem = Array.from(
824
- document.querySelectorAll<HTMLElement>('[role="menuitem"]'),
825
- ).find((item) => item.textContent?.trim() === "Add context");
826
- await act(async () => addContextItem?.click());
827
823
  const clickMenuItem = async (label: string) => {
828
824
  const item = Array.from(
829
825
  document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
@@ -3387,7 +3383,7 @@ describe("createTiptapComposerExtensions", () => {
3387
3383
  });
3388
3384
 
3389
3385
  describe("TiptapComposer storage gate", () => {
3390
- it("opens storage setup from Upload File without exposing a file picker", async () => {
3386
+ it("opens storage setup from Files and images without exposing a file picker", async () => {
3391
3387
  const onAttachmentRequest = vi.fn();
3392
3388
 
3393
3389
  function Harness() {
@@ -3432,9 +3428,11 @@ describe("TiptapComposer storage gate", () => {
3432
3428
  const uploadItem = Array.from(
3433
3429
  document.querySelectorAll<HTMLElement>("[role=menuitem]"),
3434
3430
  ).at(0);
3435
- expect(uploadItem).toBeDefined();
3431
+ expect(uploadItem?.textContent).toBe("Files and images");
3436
3432
  await act(async () => uploadItem?.click());
3437
- expect(onAttachmentRequest).toHaveBeenCalledOnce();
3433
+ await act(async () =>
3434
+ vi.waitFor(() => expect(onAttachmentRequest).toHaveBeenCalledOnce()),
3435
+ );
3438
3436
  expect(container.querySelector('input[type="file"]')).toBeNull();
3439
3437
  });
3440
3438
  });