@agent-native/toolkit 0.198.6 → 0.198.7

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 (50) hide show
  1. package/dist/app/chat/AgentPanel.d.ts +4 -3
  2. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  3. package/dist/app/chat/AgentPanel.js +14 -24
  4. package/dist/app/chat/AgentPanel.js.map +1 -1
  5. package/dist/app/chat/ConnectBuilderCard.d.ts.map +1 -1
  6. package/dist/app/chat/ConnectBuilderCard.js +2 -1
  7. package/dist/app/chat/ConnectBuilderCard.js.map +1 -1
  8. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  9. package/dist/app/chat/MultiTabAssistantChat.js +21 -2
  10. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  11. package/dist/composer/ComposerContextMenu.d.ts +2 -2
  12. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  13. package/dist/composer/ComposerContextMenu.js +54 -66
  14. package/dist/composer/ComposerContextMenu.js.map +1 -1
  15. package/dist/composer/ComposerPlusMenu.d.ts +2 -3
  16. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
  17. package/dist/composer/ComposerPlusMenu.js +13 -278
  18. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  19. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  20. package/dist/composer/TiptapComposer.js +3 -12
  21. package/dist/composer/TiptapComposer.js.map +1 -1
  22. package/dist/composer/use-composer-panel-placement.d.ts +0 -1
  23. package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
  24. package/dist/composer/use-composer-panel-placement.js +4 -5
  25. package/dist/composer/use-composer-panel-placement.js.map +1 -1
  26. package/package.json +3 -3
  27. package/src/app/chat/AgentPanel.header.spec.ts +25 -27
  28. package/src/app/chat/AgentPanel.overlay.spec.tsx +14 -51
  29. package/src/app/chat/AgentPanel.tsx +15 -26
  30. package/src/app/chat/ConnectBuilderCard.spec.tsx +45 -0
  31. package/src/app/chat/ConnectBuilderCard.tsx +3 -2
  32. package/src/app/chat/MultiTabAssistantChat.spec.tsx +12 -0
  33. package/src/app/chat/MultiTabAssistantChat.tsx +26 -2
  34. package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +4 -2
  35. package/src/composer/ComposerContextMenu.spec.tsx +68 -60
  36. package/src/composer/ComposerContextMenu.tsx +93 -110
  37. package/src/composer/ComposerContextPicker.spec.tsx +16 -14
  38. package/src/composer/ComposerContextPickerDialog.spec.tsx +8 -6
  39. package/src/composer/ComposerPlusMenu.parity.spec.tsx +37 -75
  40. package/src/composer/ComposerPlusMenu.tsx +26 -450
  41. package/src/composer/PromptComposer.context.spec.tsx +112 -18
  42. package/src/composer/TiptapComposer.spec.ts +15 -0
  43. package/src/composer/TiptapComposer.tsx +2 -12
  44. package/src/composer/use-composer-panel-placement.ts +7 -5
  45. package/dist/composer/asset-picker-url.d.ts +0 -10
  46. package/dist/composer/asset-picker-url.d.ts.map +0 -1
  47. package/dist/composer/asset-picker-url.js +0 -56
  48. package/dist/composer/asset-picker-url.js.map +0 -1
  49. package/src/composer/asset-picker-url.spec.ts +0 -56
  50. package/src/composer/asset-picker-url.ts +0 -72
@@ -71,6 +71,8 @@ async function open() {
71
71
  container.querySelector('button[aria-label="Add context"]')!,
72
72
  "ArrowDown",
73
73
  );
74
+ await click("Add context");
75
+ await click("Category");
74
76
  await click("Source");
75
77
  }
76
78
  async function search(value: string, placeholder = "Search source") {
@@ -167,7 +169,7 @@ describe("declarative context picker", () => {
167
169
  expect(select).not.toHaveBeenCalled();
168
170
  await click("Clear selection");
169
171
  expect(clear).toHaveBeenCalledOnce();
170
- expect(menus()).toHaveLength(2);
172
+ expect(menus()).toHaveLength(4);
171
173
  });
172
174
  it("retries a failed selection and closes only on success", async () => {
173
175
  const onSelect = vi
@@ -192,7 +194,7 @@ describe("declarative context picker", () => {
192
194
  await render({ ...config, scopeKey: "account-b" });
193
195
  expect(onSelect.mock.calls[0][1].signal.aborted).toBe(true);
194
196
  await act(async () => pending.resolve());
195
- expect(menus()).toHaveLength(2);
197
+ expect(menus()).toHaveLength(4);
196
198
  });
197
199
  it("keeps link, search and page on sync refresh, and aborts a dismissed load", async () => {
198
200
  const pending = deferred<{ items: { id: string; title: string }[] }>();
@@ -231,16 +233,16 @@ describe("declarative context picker", () => {
231
233
  await act(async () =>
232
234
  pending.resolve({ items: [{ id: "late", title: "Late" }] }),
233
235
  );
234
- expect(menus()).toHaveLength(1);
236
+ expect(menus()).toHaveLength(3);
235
237
  expect(document.body.textContent).not.toContain("Late");
236
238
  });
237
239
  it("keeps connected panels, integrated headers and source search autofocus", async () => {
238
240
  await render(local());
239
241
  await open();
240
- expect(menus()).toHaveLength(2);
242
+ expect(menus()).toHaveLength(4);
241
243
  expect(menus().at(-1)?.classList.contains("w-64")).toBe(true);
242
244
  const searches = document.querySelectorAll('input[role="searchbox"]');
243
- expect(searches).toHaveLength(2);
245
+ expect(searches).toHaveLength(1);
244
246
  expect(document.querySelector('[role="combobox"]')).toBeNull();
245
247
  for (const input of searches)
246
248
  expect(input.hasAttribute("aria-controls")).toBe(false);
@@ -270,7 +272,7 @@ describe("declarative context picker", () => {
270
272
  signal: expect.any(AbortSignal),
271
273
  }),
272
274
  );
273
- expect(menus()).toHaveLength(2);
275
+ expect(menus()).toHaveLength(4);
274
276
  });
275
277
  it("routes typing and arrows through the root item and picker submenu", async () => {
276
278
  await render(local());
@@ -285,10 +287,10 @@ describe("declarative context picker", () => {
285
287
  await key(row("One"), "ArrowDown");
286
288
  expect(document.activeElement).toBe(row("Two"));
287
289
  await key(row("Two"), "ArrowLeft");
288
- expect(menus()).toHaveLength(1);
290
+ expect(menus()).toHaveLength(3);
289
291
  expect(document.activeElement).toBe(row("Source"));
290
292
  await key(row("Source"), "Escape");
291
- expect(menus()).toHaveLength(0);
293
+ expect(menus()).toHaveLength(2);
292
294
  });
293
295
  it("loads pages with cursor history and preserves location on refresh without descriptor loops", async () => {
294
296
  const load = vi
@@ -388,7 +390,7 @@ describe("declarative context picker", () => {
388
390
  expect(signal.aborted).toBe(true);
389
391
  await click("Source");
390
392
  await act(async () => pending.resolve());
391
- expect(menus()).toHaveLength(2);
393
+ expect(menus()).toHaveLength(4);
392
394
  });
393
395
  it("validates links before reading, searches results, and returns to the populated link stage", async () => {
394
396
  const load = vi
@@ -433,19 +435,19 @@ describe("declarative context picker", () => {
433
435
  'input[placeholder="Source URL"]',
434
436
  )?.value,
435
437
  ).toBe("https://example.com/source");
436
- expect(menus()).toHaveLength(2);
438
+ expect(menus()).toHaveLength(4);
437
439
  await key(
438
440
  document.querySelector('input[placeholder="Source URL"]')!,
439
441
  "ArrowLeft",
440
442
  );
441
- expect(menus()).toHaveLength(2);
443
+ expect(menus()).toHaveLength(4);
442
444
  await key(
443
445
  document.querySelector('input[placeholder="Source URL"]')!,
444
446
  "ArrowRight",
445
447
  );
446
- expect(menus()).toHaveLength(2);
448
+ expect(menus()).toHaveLength(4);
447
449
  await key(row("Read link"), "ArrowLeft");
448
- expect(menus()).toHaveLength(1);
450
+ expect(menus()).toHaveLength(3);
449
451
  });
450
452
  it("keeps a genuine link footer present in empty and populated states", async () => {
451
453
  const config = {
@@ -512,6 +514,6 @@ describe("declarative context picker", () => {
512
514
  await click("Retry");
513
515
  expect(footer).toHaveBeenCalledTimes(2);
514
516
  expect(load).not.toHaveBeenCalled();
515
- expect(menus()).toHaveLength(2);
517
+ expect(menus()).toHaveLength(4);
516
518
  });
517
519
  });
@@ -118,6 +118,8 @@ async function open() {
118
118
  new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
119
119
  ),
120
120
  );
121
+ await act(async () => row("Add context").click());
122
+ await act(async () => row("Design").click());
121
123
  expect(row("Attach source").hasAttribute("aria-haspopup")).toBe(false);
122
124
  await act(async () => row("Attach source").click());
123
125
  await tick();
@@ -205,7 +207,7 @@ describe("shared context URL dialog", () => {
205
207
  onAttach.mock.calls[0][0].map((item: { id: string }) => item.id),
206
208
  ).toEqual(["two"]);
207
209
  });
208
- it("flattens categories and opens a single-select picker from the root", async () => {
210
+ it("keeps a searchable single-select picker under the Design category", async () => {
209
211
  const onSelect = vi.fn().mockResolvedValue(undefined);
210
212
  const config = {
211
213
  presentation: { type: "dialog" as const, mode: "single" as const },
@@ -223,11 +225,11 @@ describe("shared context URL dialog", () => {
223
225
  new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true }),
224
226
  ),
225
227
  );
226
- expect(
227
- Array.from(document.querySelectorAll('[role="menuitem"]')).some(
228
- (item) => item.textContent === "Design",
229
- ),
230
- ).toBe(false);
228
+ expect(document.querySelector('[role="searchbox"]')).toBeNull();
229
+ expect(row("Add context")).toBeDefined();
230
+ await act(async () => row("Add context").click());
231
+ expect(row("Design")).toBeDefined();
232
+ await act(async () => row("Design").click());
231
233
  expect(row("Attach source").querySelector("svg")).toBeNull();
232
234
  await act(async () => row("Attach source").click());
233
235
  await tick();
@@ -6,7 +6,6 @@ import { createRoot, type Root } from "react-dom/client";
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7
7
 
8
8
  import { TooltipProvider } from "../ui/tooltip.js";
9
- import { ASSET_PICKER_HANDOFF_PARAM } from "./asset-picker-url.js";
10
9
  import { mergeComposerMenuItems } from "./ComposerPlusMenu.js";
11
10
  import { PromptComposer, type PromptComposerProps } from "./PromptComposer.js";
12
11
  import type { PromptComposerSubmitOptions } from "./PromptComposer.js";
@@ -97,7 +96,7 @@ async function mount(props: Partial<PromptComposerProps> = {}) {
97
96
  });
98
97
  return { composerRef, onSubmit };
99
98
  }
100
- async function open(trigger: "+" | "@") {
99
+ async function open(trigger: "+" | "@", enterAddContext = false) {
101
100
  const target =
102
101
  trigger === "+"
103
102
  ? container.querySelector<HTMLElement>('button[aria-label="Add context"]')
@@ -114,6 +113,8 @@ async function open(trigger: "+" | "@") {
114
113
  );
115
114
  await settle();
116
115
  });
116
+ if (enterAddContext && labels().includes("Add context"))
117
+ await choose("Add context");
117
118
  }
118
119
  function item(label: string) {
119
120
  const row = Array.from(
@@ -210,12 +211,11 @@ describe("shared default action preservation", () => {
210
211
  expect(document.querySelector('[role="menu"]')).toBeNull();
211
212
  }
212
213
  } else {
213
- await open(trigger);
214
+ await open(trigger, false);
214
215
  const expected =
215
216
  mode === "full"
216
217
  ? [
217
218
  "Upload File",
218
- "Generate Image",
219
219
  "Schedule Task",
220
220
  "Create Automation",
221
221
  "Integrations",
@@ -255,8 +255,8 @@ describe("shared default action preservation", () => {
255
255
  ],
256
256
  extensionTools: true,
257
257
  });
258
- await open(trigger);
259
- expect(labels()).toContain("Host source");
258
+ await open(trigger, false);
259
+ expect(labels()).not.toContain("Host source");
260
260
  if (action === "Create new skill") await choose("Create Skill");
261
261
  await choose(action);
262
262
  expect(
@@ -283,7 +283,7 @@ describe("shared default action preservation", () => {
283
283
 
284
284
  it(`${trigger} clears automation mode through its accessible Cancel button`, async () => {
285
285
  const { composerRef, onSubmit } = await mount();
286
- await open(trigger);
286
+ await open(trigger, false);
287
287
  await choose("Create Automation");
288
288
  const cancel = container.querySelector<HTMLButtonElement>(
289
289
  '[data-agent-composer-slot="mode-row"] button[aria-label="Cancel"]',
@@ -325,12 +325,11 @@ describe("shared default action preservation", () => {
325
325
  },
326
326
  ],
327
327
  });
328
- await open(trigger);
329
- expect(
330
- document
331
- .querySelector('[role="menu"]')
332
- ?.textContent?.match(/Integrations/g),
333
- ).toHaveLength(1);
328
+ await open(trigger, false);
329
+ expect(labels().filter((label) => label === "Integrations")).toHaveLength(
330
+ 1,
331
+ );
332
+ await choose("Integrations");
334
333
  await choose("Connect / Manage");
335
334
  expect(configure).toHaveBeenCalledOnce();
336
335
  expect(document.querySelector('[role="dialog"]')).toBeNull();
@@ -339,7 +338,7 @@ describe("shared default action preservation", () => {
339
338
  it(`${trigger} preserves native upload and storage-setup handoff`, async () => {
340
339
  const onAttachmentRequest = vi.fn();
341
340
  await mount({ attachmentsEnabled: false, onAttachmentRequest });
342
- await open(trigger);
341
+ await open(trigger, false);
343
342
  expect(onAttachmentRequest).not.toHaveBeenCalled();
344
343
  await choose("Upload File");
345
344
  await act(async () => {
@@ -347,7 +346,7 @@ describe("shared default action preservation", () => {
347
346
  });
348
347
  expect(onAttachmentRequest).toHaveBeenCalledOnce();
349
348
  await mount({ attachmentsEnabled: false });
350
- await open(trigger);
349
+ await open(trigger, false);
351
350
  expect(labels()).not.toContain("Upload File");
352
351
  await close();
353
352
  await mount();
@@ -355,7 +354,7 @@ describe("shared default action preservation", () => {
355
354
  'input[type="file"][multiple]',
356
355
  )!;
357
356
  const click = vi.spyOn(input, "click").mockImplementation(() => {});
358
- await open(trigger);
357
+ await open(trigger, false);
359
358
  await choose("Upload File");
360
359
  expect(click).toHaveBeenCalledOnce();
361
360
  });
@@ -367,7 +366,7 @@ describe("shared default action preservation", () => {
367
366
  plusMenuMode: "terminal",
368
367
  terminalModeControl: { enabled: false, onChange, onNewTerminal },
369
368
  });
370
- await open(trigger);
369
+ await open(trigger, false);
371
370
  await choose("New terminal");
372
371
  expect(onChange).toHaveBeenCalledExactlyOnceWith(true);
373
372
  expect(onNewTerminal).not.toHaveBeenCalled();
@@ -375,13 +374,13 @@ describe("shared default action preservation", () => {
375
374
  plusMenuMode: "terminal",
376
375
  terminalModeControl: { enabled: true, onChange, onNewTerminal },
377
376
  });
378
- await open(trigger);
377
+ await open(trigger, false);
379
378
  expect(item("CLI terminal mode").getAttribute("aria-checked")).toBe(
380
379
  "true",
381
380
  );
382
381
  await choose("New terminal");
383
382
  expect(onNewTerminal).toHaveBeenCalledOnce();
384
- await open(trigger);
383
+ await open(trigger, false);
385
384
  await choose("CLI terminal mode");
386
385
  expect(onChange).toHaveBeenLastCalledWith(false);
387
386
  expect(useOrg).not.toHaveBeenCalled();
@@ -397,7 +396,7 @@ describe("shared default action preservation", () => {
397
396
  );
398
397
  vi.stubGlobal("fetch", fetch);
399
398
  await mount();
400
- await open(trigger);
399
+ await open(trigger, false);
401
400
  const form = await uploadSkill();
402
401
  expect(form).not.toBeNull();
403
402
  expect(form.textContent).toContain(
@@ -445,8 +444,8 @@ describe("shared default action preservation", () => {
445
444
  plusMenuMode: "hidden",
446
445
  contextMenuItems: [{ id: "host", label: "Host source", onSelect }],
447
446
  });
448
- await open("@");
449
- expect(labels()).toEqual(["Upload File", "Host source"]);
447
+ await open("@", true);
448
+ expect(labels()).toEqual(["Upload File", "Add context", "Host source"]);
450
449
  await choose("Host source");
451
450
  expect(onSelect).toHaveBeenCalledOnce();
452
451
  expect(useOrg).not.toHaveBeenCalled();
@@ -573,7 +572,7 @@ describe("shared default action preservation", () => {
573
572
  root.render(<Host />);
574
573
  await settle();
575
574
  });
576
- await open("@");
575
+ await open("@", true);
577
576
  await choose("Attach integration");
578
577
  await open("+");
579
578
  await choose("Schedule Task");
@@ -671,58 +670,21 @@ describe("shared default action preservation", () => {
671
670
  );
672
671
  });
673
672
 
674
- it("preserves the Assets window handoff and rejects mismatched messages", async () => {
675
- const pickerWindow = {} as Window;
676
- const windowOpen = vi.spyOn(window, "open").mockReturnValue(pickerWindow);
677
- await mount();
678
- await open("@");
679
- await choose("Generate Image");
680
- const link =
681
- document.querySelector<HTMLAnchorElement>('[role="dialog"] a')!;
682
- expect(link).not.toBeNull();
683
- const url = new URL(link.href);
684
- const handoffId = url.searchParams.get(ASSET_PICKER_HANDOFF_PARAM);
685
- expect(handoffId).toBeTruthy();
686
- await act(async () => {
687
- link.click();
688
- await settle();
689
- });
690
- expect(windowOpen).toHaveBeenCalledExactlyOnceWith(
691
- url.toString(),
692
- "_blank",
693
- );
694
- const message = (id: string | null) =>
695
- new MessageEvent("message", {
696
- origin: url.origin,
697
- source: pickerWindow,
698
- data: {
699
- protocol: "agent-native.embed",
700
- version: 1,
701
- type: "message",
702
- name: "chooseAsset",
703
- payload: {
704
- handoffId: id,
705
- assetId: "test-image",
706
- url: "https://assets.example.test/image.png",
707
- title: "Test image",
708
- },
709
- },
710
- });
711
- await act(async () => {
712
- window.dispatchEvent(message("wrong-handoff"));
713
- await settle();
714
- });
715
- expect(setContextItem).not.toHaveBeenCalled();
716
- await act(async () => {
717
- window.dispatchEvent(message(handoffId));
718
- await settle();
719
- });
720
- expect(setContextItem).toHaveBeenCalledExactlyOnceWith({
721
- key: "asset-image:test-image",
722
- title: "Image: Test image",
723
- context:
724
- "Image URL: https://assets.example.test/image.png\nAsset ID: test-image",
673
+ it("keeps chat actions at the root, nests context sources, and omits Generate Image", async () => {
674
+ await mount({
675
+ contextMenuItems: [
676
+ { id: "host-source", label: "Host source", onSelect: vi.fn() },
677
+ ],
725
678
  });
679
+ await open("+");
680
+ expect(labels()).toContain("Schedule Task");
681
+ expect(labels()).toContain("Create Automation");
682
+ expect(labels()).toContain("Create Skill");
683
+ expect(labels()).not.toContain("Generate Image");
684
+ expect(labels()).not.toContain("Host source");
685
+ await choose("Add context");
686
+ expect(labels()).toContain("Host source");
687
+ expect(labels()).toContain("Schedule Task");
726
688
  });
727
689
 
728
690
  it("cancels a skill save when its submenu closes and retains retry after errors", async () => {
@@ -738,7 +700,7 @@ describe("shared default action preservation", () => {
738
700
  );
739
701
  vi.stubGlobal("fetch", fetch);
740
702
  await mount();
741
- await open("+");
703
+ await open("+", false);
742
704
  const form = await uploadSkill();
743
705
  await act(async () => {
744
706
  form.dispatchEvent(