@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.
- package/agent-native.eject.json +1 -0
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +124 -9
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +1 -1
- package/dist/app/agentkit/react/core-file-storage.d.ts +21 -0
- package/dist/app/agentkit/react/core-file-storage.d.ts.map +1 -0
- package/dist/app/agentkit/react/core-file-storage.js +3 -0
- package/dist/app/agentkit/react/core-file-storage.js.map +1 -0
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +89 -10
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +39 -58
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +3 -1
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/root.js +66 -2
- package/dist/app/chat/agentkit-chat/root.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +1 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.js +2 -0
- package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.d.ts +3 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.d.ts.map +1 -1
- package/dist/app/chat/composer/use-agentkit-capabilities.js +11 -11
- package/dist/app/chat/composer/use-agentkit-capabilities.js.map +1 -1
- package/dist/app/chat/composer/use-workspace-composer-menu.d.ts +3 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.d.ts.map +1 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.js +80 -0
- package/dist/app/chat/composer/use-workspace-composer-menu.js.map +1 -0
- package/dist/app/chat/index.d.ts +1 -0
- package/dist/app/chat/index.d.ts.map +1 -1
- package/dist/app/chat/index.js +1 -0
- package/dist/app/chat/index.js.map +1 -1
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts +5 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts.map +1 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.js +108 -0
- package/dist/app/chat/prepare-prompt-composer-agent-handoff.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +6 -1
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +6 -1
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +6 -1
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +6 -1
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +6 -1
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +6 -1
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +6 -1
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +6 -1
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +6 -1
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +6 -1
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +6 -1
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +6 -1
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/resources/McpIntegrationDialog.d.ts +3 -1
- package/dist/app/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/app/resources/McpIntegrationDialog.js +2 -2
- package/dist/app/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +119 -40
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/composer/ComposerContextMenu.d.ts +36 -2
- package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
- package/dist/composer/ComposerContextMenu.js +108 -40
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.js +63 -26
- package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.d.ts +5 -3
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +166 -121
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/MentionItemMedia.d.ts.map +1 -1
- package/dist/composer/MentionItemMedia.js +3 -1
- package/dist/composer/MentionItemMedia.js.map +1 -1
- package/dist/composer/PromptComposer.d.ts +1 -0
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +1 -0
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +117 -50
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/index.d.ts +1 -0
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.d.ts +15 -0
- package/dist/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.js.map +1 -1
- package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
- package/dist/composer/use-composer-panel-placement.js +2 -4
- package/dist/composer/use-composer-panel-placement.js.map +1 -1
- package/dist/composer/use-mention-search.d.ts +7 -0
- package/dist/composer/use-mention-search.d.ts.map +1 -1
- package/dist/composer/use-mention-search.js +86 -19
- package/dist/composer/use-mention-search.js.map +1 -1
- package/dist/composer/useComposerContextPicker.d.ts +5 -1
- package/dist/composer/useComposerContextPicker.d.ts.map +1 -1
- package/dist/composer/useComposerContextPicker.js +18 -4
- package/dist/composer/useComposerContextPicker.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.tsx +158 -11
- package/src/app/agentkit/react/composer-context.spec.tsx +180 -0
- package/src/app/agentkit/react/core-file-storage.tsx +14 -0
- package/src/app/auth/AuthPage.interaction.spec.tsx +143 -0
- package/src/app/auth/AuthPage.tsx +159 -13
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +65 -3
- package/src/app/chat/AgentKitAssistantChat.tsx +57 -65
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +21 -0
- package/src/app/chat/MultiTabAssistantChat.tsx +4 -0
- package/src/app/chat/agentkit-chat/root.tsx +91 -8
- package/src/app/chat/chat/surface-types.ts +1 -0
- package/src/app/chat/composer/runtime-adapters.tsx +2 -0
- package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +26 -1
- package/src/app/chat/composer/use-agentkit-capabilities.ts +11 -11
- package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +2 -4
- package/src/app/chat/composer/use-workspace-composer-menu.spec.tsx +174 -0
- package/src/app/chat/composer/use-workspace-composer-menu.tsx +122 -0
- package/src/app/chat/index.ts +1 -0
- package/src/app/chat/prepare-prompt-composer-agent-handoff.spec.ts +162 -0
- package/src/app/chat/prepare-prompt-composer-agent-handoff.ts +134 -0
- package/src/app/i18n/catalogs/ar-SA.ts +6 -1
- package/src/app/i18n/catalogs/de-DE.ts +6 -1
- package/src/app/i18n/catalogs/en-US.ts +6 -1
- package/src/app/i18n/catalogs/es-ES.ts +6 -1
- package/src/app/i18n/catalogs/fr-FR.ts +6 -1
- package/src/app/i18n/catalogs/hi-IN.ts +6 -1
- package/src/app/i18n/catalogs/ja-JP.ts +6 -1
- package/src/app/i18n/catalogs/ko-KR.ts +6 -1
- package/src/app/i18n/catalogs/pt-BR.ts +6 -1
- package/src/app/i18n/catalogs/zh-CN.ts +6 -1
- package/src/app/i18n/catalogs/zh-TW.ts +6 -1
- package/src/app/resources/McpIntegrationDialog.tsx +5 -0
- package/src/app/settings/useBuilderStatus.spec.tsx +229 -13
- package/src/app/settings/useBuilderStatus.ts +145 -47
- package/src/composer/ComposerContextMenu.spec.tsx +95 -32
- package/src/composer/ComposerContextMenu.tsx +284 -73
- package/src/composer/ComposerContextPicker.spec.tsx +13 -14
- package/src/composer/ComposerContextPickerDialog.spec.tsx +40 -3
- package/src/composer/ComposerContextPickerDialog.tsx +158 -24
- package/src/composer/ComposerPlusMenu.parity.spec.tsx +442 -62
- package/src/composer/ComposerPlusMenu.tsx +322 -237
- package/src/composer/MentionItemMedia.tsx +3 -0
- package/src/composer/PromptComposer.context.spec.tsx +253 -82
- package/src/composer/PromptComposer.tsx +2 -0
- package/src/composer/TiptapComposer.spec.ts +6 -8
- package/src/composer/TiptapComposer.tsx +148 -55
- package/src/composer/index.ts +5 -0
- package/src/composer/runtime-adapters.tsx +18 -0
- package/src/composer/use-composer-panel-placement.ts +2 -4
- package/src/composer/use-mention-search.spec.tsx +99 -1
- package/src/composer/use-mention-search.ts +101 -24
- package/src/composer/useComposerContextPicker.ts +25 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
3
|
import { AssistantRuntimeProvider, useLocalRuntime } from "@assistant-ui/react";
|
|
4
|
+
import { waitFor } from "@testing-library/react";
|
|
4
5
|
import React, { act } from "react";
|
|
5
6
|
import { createRoot, type Root } from "react-dom/client";
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
8
|
|
|
9
|
+
import { Dialog, DialogContent, DialogTitle } from "../ui/dialog.js";
|
|
8
10
|
import { TooltipProvider } from "../ui/tooltip.js";
|
|
9
11
|
import { mergeComposerMenuItems } from "./ComposerPlusMenu.js";
|
|
10
12
|
import { PromptComposer, type PromptComposerProps } from "./PromptComposer.js";
|
|
@@ -99,7 +101,7 @@ async function mount(
|
|
|
99
101
|
});
|
|
100
102
|
return { composerRef, onSubmit };
|
|
101
103
|
}
|
|
102
|
-
async function open(
|
|
104
|
+
async function open(_enterAddContext = false) {
|
|
103
105
|
const target = container.querySelector<HTMLElement>(
|
|
104
106
|
'button[aria-label="Add context"]',
|
|
105
107
|
);
|
|
@@ -115,13 +117,13 @@ async function open(enterAddContext = false) {
|
|
|
115
117
|
);
|
|
116
118
|
await settle();
|
|
117
119
|
});
|
|
118
|
-
if (enterAddContext && labels().includes("Add context"))
|
|
119
|
-
await choose("Add context");
|
|
120
120
|
}
|
|
121
121
|
function item(label: string) {
|
|
122
122
|
const row = Array.from(
|
|
123
123
|
document.querySelectorAll<HTMLElement>('[role^="menuitem"]'),
|
|
124
|
-
)
|
|
124
|
+
)
|
|
125
|
+
.reverse()
|
|
126
|
+
.find((row) => row.textContent?.trim() === label);
|
|
125
127
|
expect(row, label).toBeDefined();
|
|
126
128
|
return row!;
|
|
127
129
|
}
|
|
@@ -159,7 +161,6 @@ function labels() {
|
|
|
159
161
|
);
|
|
160
162
|
}
|
|
161
163
|
async function uploadSkill() {
|
|
162
|
-
await choose("Create Skill");
|
|
163
164
|
await choose("Upload skill file");
|
|
164
165
|
const input = container.querySelector<HTMLInputElement>(
|
|
165
166
|
'input[accept=".md,text/markdown"]',
|
|
@@ -172,11 +173,11 @@ async function uploadSkill() {
|
|
|
172
173
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
173
174
|
await settle();
|
|
174
175
|
});
|
|
175
|
-
return document.querySelector<HTMLFormElement>('[role="
|
|
176
|
+
return document.querySelector<HTMLFormElement>('[role="dialog"] form')!;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
describe("shared default action preservation", () => {
|
|
179
|
-
it("preserves
|
|
180
|
+
it("preserves overridden actions at the shared command slot without empty host groups", () => {
|
|
180
181
|
const defaults = [
|
|
181
182
|
{ id: "integrations", label: "Setup", onSelect: vi.fn() },
|
|
182
183
|
{ id: "create-skill", label: "Skill", onSelect: vi.fn() },
|
|
@@ -191,8 +192,203 @@ describe("shared default action preservation", () => {
|
|
|
191
192
|
},
|
|
192
193
|
];
|
|
193
194
|
const merged = mergeComposerMenuItems(defaults, provided);
|
|
194
|
-
expect(merged).toEqual([provided[0], defaults[1]]);
|
|
195
|
-
|
|
195
|
+
expect(merged).toEqual([provided[0].children[0], defaults[1]]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("keeps host context at the root and keeps nested integration overrides in their labeled section", async () => {
|
|
199
|
+
const selectContext = vi.fn();
|
|
200
|
+
const selectIntegration = vi.fn();
|
|
201
|
+
await mount({
|
|
202
|
+
contextMenuItems: [
|
|
203
|
+
{
|
|
204
|
+
id: "host-group",
|
|
205
|
+
label: "Workspace",
|
|
206
|
+
children: [
|
|
207
|
+
{
|
|
208
|
+
id: "reference",
|
|
209
|
+
label: "Reference",
|
|
210
|
+
icon: <svg data-testid="reference-icon" />,
|
|
211
|
+
onSelect: selectContext,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "integrations",
|
|
215
|
+
label: "Use integration",
|
|
216
|
+
order: -100,
|
|
217
|
+
children: [
|
|
218
|
+
{
|
|
219
|
+
id: "invoke",
|
|
220
|
+
label: "Invoke service",
|
|
221
|
+
intent: "invoke-integration",
|
|
222
|
+
onSelect: selectIntegration,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
await open();
|
|
231
|
+
expect(labels()).toEqual([
|
|
232
|
+
"Files and images",
|
|
233
|
+
"Workspace",
|
|
234
|
+
"Use integration",
|
|
235
|
+
"Schedule Task",
|
|
236
|
+
"Create Automation",
|
|
237
|
+
"Create Skill",
|
|
238
|
+
"Upload skill file",
|
|
239
|
+
]);
|
|
240
|
+
await choose("Workspace");
|
|
241
|
+
expect(
|
|
242
|
+
item("Reference").querySelector('[data-testid="reference-icon"]'),
|
|
243
|
+
).not.toBeNull();
|
|
244
|
+
expect(
|
|
245
|
+
Array.from(document.querySelectorAll('[role="menu"]')).at(-1)
|
|
246
|
+
?.textContent,
|
|
247
|
+
).not.toContain("Use integration");
|
|
248
|
+
await choose("Reference");
|
|
249
|
+
expect(selectContext).toHaveBeenCalledOnce();
|
|
250
|
+
await open();
|
|
251
|
+
expect(
|
|
252
|
+
document.querySelector('[role="group"][aria-labelledby]'),
|
|
253
|
+
).not.toBeNull();
|
|
254
|
+
expect(labels()).toContain("Use integration");
|
|
255
|
+
expect(
|
|
256
|
+
labels().filter((label) => label === "Use integration"),
|
|
257
|
+
).toHaveLength(1);
|
|
258
|
+
expect(labels()).not.toContain("Integrations");
|
|
259
|
+
await choose("Use integration");
|
|
260
|
+
await choose("Invoke service");
|
|
261
|
+
expect(selectIntegration).toHaveBeenCalledOnce();
|
|
262
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("preserves disabled ancestry when host commands replace defaults", async () => {
|
|
266
|
+
await mount({
|
|
267
|
+
contextMenuItems: [
|
|
268
|
+
{
|
|
269
|
+
id: "disabled",
|
|
270
|
+
label: "Unavailable",
|
|
271
|
+
disabled: true,
|
|
272
|
+
children: [
|
|
273
|
+
{ id: "integrations", label: "Use integration", onSelect: vi.fn() },
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
await open();
|
|
279
|
+
expect(labels()).toEqual([
|
|
280
|
+
"Files and images",
|
|
281
|
+
"Use integration",
|
|
282
|
+
"Schedule Task",
|
|
283
|
+
"Create Automation",
|
|
284
|
+
"Create Skill",
|
|
285
|
+
"Upload skill file",
|
|
286
|
+
]);
|
|
287
|
+
expect(item("Use integration").getAttribute("aria-disabled")).toBe("true");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("routes nested integration commands separately from context siblings", async () => {
|
|
291
|
+
const invoke = vi.fn();
|
|
292
|
+
await mount({
|
|
293
|
+
contextMenuItems: [
|
|
294
|
+
{
|
|
295
|
+
id: "workspace",
|
|
296
|
+
label: "Workspace",
|
|
297
|
+
children: [
|
|
298
|
+
{ id: "reference", label: "Reference", onSelect: vi.fn() },
|
|
299
|
+
{
|
|
300
|
+
id: "invoke",
|
|
301
|
+
label: "Use service",
|
|
302
|
+
intent: "invoke-integration",
|
|
303
|
+
onSelect: invoke,
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
});
|
|
309
|
+
await open();
|
|
310
|
+
await choose("Workspace");
|
|
311
|
+
expect(labels()).toContain("Reference");
|
|
312
|
+
expect(
|
|
313
|
+
Array.from(document.querySelectorAll('[role="menu"]')).at(-1)
|
|
314
|
+
?.textContent,
|
|
315
|
+
).not.toContain("Use service");
|
|
316
|
+
await act(async () =>
|
|
317
|
+
item("Reference").dispatchEvent(
|
|
318
|
+
new KeyboardEvent("keydown", {
|
|
319
|
+
key: "Escape",
|
|
320
|
+
bubbles: true,
|
|
321
|
+
cancelable: true,
|
|
322
|
+
}),
|
|
323
|
+
),
|
|
324
|
+
);
|
|
325
|
+
await choose("Use service");
|
|
326
|
+
expect(invoke).toHaveBeenCalledOnce();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("distinguishes file uploads from saved file references", async () => {
|
|
330
|
+
const { onSubmit, composerRef } = await mount({
|
|
331
|
+
mentionItems: [
|
|
332
|
+
{
|
|
333
|
+
id: "other",
|
|
334
|
+
label: "Other reference",
|
|
335
|
+
source: "resource",
|
|
336
|
+
refType: "custom",
|
|
337
|
+
section: "Other",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
id: "connected",
|
|
341
|
+
label: "Connected agent",
|
|
342
|
+
source: "resource",
|
|
343
|
+
refType: "agent",
|
|
344
|
+
section: "Connected Agents",
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: "agent",
|
|
348
|
+
label: "Local agent",
|
|
349
|
+
source: "resource",
|
|
350
|
+
refType: "agent",
|
|
351
|
+
section: "Agents",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
id: "brief",
|
|
355
|
+
label: "Launch brief",
|
|
356
|
+
source: "resource",
|
|
357
|
+
refType: "file",
|
|
358
|
+
refPath: "brief.md",
|
|
359
|
+
section: "Files",
|
|
360
|
+
icon: "file",
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
await open();
|
|
365
|
+
expect(labels()).toEqual([
|
|
366
|
+
"Files and images",
|
|
367
|
+
"Search workspace…",
|
|
368
|
+
"Integrations",
|
|
369
|
+
"Schedule Task",
|
|
370
|
+
"Create Automation",
|
|
371
|
+
"Create Skill",
|
|
372
|
+
"Upload skill file",
|
|
373
|
+
]);
|
|
374
|
+
await choose("Search workspace…");
|
|
375
|
+
expect(document.querySelector('[role="dialog"]')).not.toBeNull();
|
|
376
|
+
const option = Array.from(
|
|
377
|
+
document.querySelectorAll<HTMLElement>('[role="option"]'),
|
|
378
|
+
).find((element) => element.textContent === "Launch brief")!;
|
|
379
|
+
expect(option).toBeDefined();
|
|
380
|
+
await act(async () => option.click());
|
|
381
|
+
await act(async () => {
|
|
382
|
+
await composerRef.current?.submit?.();
|
|
383
|
+
});
|
|
384
|
+
expect(onSubmit).toHaveBeenCalledWith(
|
|
385
|
+
expect.any(String),
|
|
386
|
+
expect.any(Array),
|
|
387
|
+
expect.arrayContaining([
|
|
388
|
+
expect.objectContaining({ name: "Launch brief", path: "brief.md" }),
|
|
389
|
+
]),
|
|
390
|
+
expect.anything(),
|
|
391
|
+
);
|
|
196
392
|
});
|
|
197
393
|
|
|
198
394
|
it.each(["full", "upload-only", "terminal", "hidden"] as const)(
|
|
@@ -212,15 +408,16 @@ describe("shared default action preservation", () => {
|
|
|
212
408
|
const expected =
|
|
213
409
|
mode === "full"
|
|
214
410
|
? [
|
|
215
|
-
"
|
|
411
|
+
"Files and images",
|
|
412
|
+
"Integrations",
|
|
216
413
|
"Schedule Task",
|
|
217
414
|
"Create Automation",
|
|
218
|
-
"Integrations",
|
|
219
415
|
"Create Skill",
|
|
416
|
+
"Upload skill file",
|
|
220
417
|
]
|
|
221
418
|
: mode === "terminal"
|
|
222
419
|
? ["New terminal", "CLI terminal mode"]
|
|
223
|
-
: ["
|
|
420
|
+
: ["Files and images"];
|
|
224
421
|
expect(labels()).toEqual(expected);
|
|
225
422
|
}
|
|
226
423
|
if (mode === "full") {
|
|
@@ -234,11 +431,202 @@ describe("shared default action preservation", () => {
|
|
|
234
431
|
},
|
|
235
432
|
);
|
|
236
433
|
|
|
434
|
+
it("hands focus to a contributed dialog and restores the draft only after it closes", async () => {
|
|
435
|
+
adapters.resources!.useWorkspaceMenu = function useWorkspaceMenu({
|
|
436
|
+
onRestoreFocus,
|
|
437
|
+
}) {
|
|
438
|
+
const [visible, setVisible] = React.useState(false);
|
|
439
|
+
return {
|
|
440
|
+
scopeKey: "one",
|
|
441
|
+
items: [
|
|
442
|
+
{
|
|
443
|
+
id: "connect",
|
|
444
|
+
label: "Connect workspace",
|
|
445
|
+
section: "integrations",
|
|
446
|
+
opensDialog: true,
|
|
447
|
+
onSelect: () => setVisible(true),
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
overlays: (
|
|
451
|
+
<Dialog open={visible} onOpenChange={setVisible}>
|
|
452
|
+
<DialogContent
|
|
453
|
+
aria-describedby={undefined}
|
|
454
|
+
onCloseAutoFocus={(event) => {
|
|
455
|
+
event.preventDefault();
|
|
456
|
+
onRestoreFocus?.();
|
|
457
|
+
}}
|
|
458
|
+
>
|
|
459
|
+
<DialogTitle>Connect workspace</DialogTitle>
|
|
460
|
+
<button onClick={() => setVisible(false)}>
|
|
461
|
+
Finish connection
|
|
462
|
+
</button>
|
|
463
|
+
</DialogContent>
|
|
464
|
+
</Dialog>
|
|
465
|
+
),
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
await mount();
|
|
469
|
+
await open();
|
|
470
|
+
await choose("Connect workspace");
|
|
471
|
+
const dialog = document.querySelector('[role="dialog"]');
|
|
472
|
+
expect(dialog).not.toBeNull();
|
|
473
|
+
expect(dialog?.contains(document.activeElement)).toBe(true);
|
|
474
|
+
await act(async () => {
|
|
475
|
+
const done = Array.from(
|
|
476
|
+
document.querySelectorAll<HTMLButtonElement>('[role="dialog"] button'),
|
|
477
|
+
).find((button) => button.textContent === "Finish connection");
|
|
478
|
+
if (!done) throw new Error("Missing connection control");
|
|
479
|
+
done.click();
|
|
480
|
+
await settle();
|
|
481
|
+
});
|
|
482
|
+
await act(async () => {
|
|
483
|
+
await new Promise(requestAnimationFrame);
|
|
484
|
+
});
|
|
485
|
+
await waitFor(() =>
|
|
486
|
+
expect(document.activeElement).toBe(
|
|
487
|
+
container.querySelector(".ProseMirror"),
|
|
488
|
+
),
|
|
489
|
+
);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it.each(["full", "upload-only"] as const)(
|
|
493
|
+
"merges workspace contributions into labeled groups in %s and inserts their references",
|
|
494
|
+
async (mode) => {
|
|
495
|
+
const useWorkspaceMenu = vi.fn(
|
|
496
|
+
({
|
|
497
|
+
onSelectIntegration,
|
|
498
|
+
}: {
|
|
499
|
+
enabled: boolean;
|
|
500
|
+
onSelectIntegration: (reference: {
|
|
501
|
+
label: string;
|
|
502
|
+
source: string;
|
|
503
|
+
refType: string;
|
|
504
|
+
refId: string;
|
|
505
|
+
}) => void;
|
|
506
|
+
}) => ({
|
|
507
|
+
scopeKey: "org:one",
|
|
508
|
+
items: [
|
|
509
|
+
{
|
|
510
|
+
id: "calendar",
|
|
511
|
+
label: "Use calendar",
|
|
512
|
+
section: "integrations" as const,
|
|
513
|
+
onSelect: () =>
|
|
514
|
+
onSelectIntegration({
|
|
515
|
+
label: "Calendar",
|
|
516
|
+
source: "workspace-integration",
|
|
517
|
+
refType: "integration",
|
|
518
|
+
refId: "calendar",
|
|
519
|
+
}),
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
}),
|
|
523
|
+
);
|
|
524
|
+
adapters.resources!.useWorkspaceMenu = useWorkspaceMenu;
|
|
525
|
+
const { composerRef, onSubmit } = await mount({
|
|
526
|
+
plusMenuMode: mode,
|
|
527
|
+
contextMenuItems: [
|
|
528
|
+
{
|
|
529
|
+
id: "brief",
|
|
530
|
+
label: "Reference brief",
|
|
531
|
+
section: "context",
|
|
532
|
+
onSelect: vi.fn(),
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
});
|
|
536
|
+
expect(useWorkspaceMenu).toHaveBeenLastCalledWith(
|
|
537
|
+
expect.objectContaining({ enabled: false }),
|
|
538
|
+
);
|
|
539
|
+
await open();
|
|
540
|
+
expect(useWorkspaceMenu).toHaveBeenLastCalledWith(
|
|
541
|
+
expect.objectContaining({ enabled: true }),
|
|
542
|
+
);
|
|
543
|
+
const groups = Array.from(
|
|
544
|
+
document.querySelectorAll<HTMLElement>(
|
|
545
|
+
'[role="group"][aria-labelledby]',
|
|
546
|
+
),
|
|
547
|
+
);
|
|
548
|
+
expect(
|
|
549
|
+
groups.map(
|
|
550
|
+
(group) =>
|
|
551
|
+
document.getElementById(group.getAttribute("aria-labelledby")!)
|
|
552
|
+
?.textContent,
|
|
553
|
+
),
|
|
554
|
+
).toEqual(
|
|
555
|
+
mode === "full"
|
|
556
|
+
? ["Add context", "Integrations", "Commands"]
|
|
557
|
+
: ["Add context", "Integrations"],
|
|
558
|
+
);
|
|
559
|
+
expect(groups[1].textContent).toContain("Use calendar");
|
|
560
|
+
expect(groups[1].textContent).not.toContain("Reference brief");
|
|
561
|
+
expect(labels()).not.toContain("Integrations");
|
|
562
|
+
await choose("Use calendar");
|
|
563
|
+
await act(async () => composerRef.current!.submit());
|
|
564
|
+
expect(onSubmit.mock.calls[0][2]).toEqual(
|
|
565
|
+
expect.arrayContaining([
|
|
566
|
+
expect.objectContaining({
|
|
567
|
+
refType: "integration",
|
|
568
|
+
refId: "calendar",
|
|
569
|
+
}),
|
|
570
|
+
]),
|
|
571
|
+
);
|
|
572
|
+
},
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
it.each(["upload-only", "hidden"] as const)(
|
|
576
|
+
"keeps contributed integration commands separate in %s without adding default modes",
|
|
577
|
+
async (mode) => {
|
|
578
|
+
const attach = vi.fn();
|
|
579
|
+
const invoke = vi.fn();
|
|
580
|
+
await mount({
|
|
581
|
+
plusMenuMode: mode,
|
|
582
|
+
contextMenuItems: [
|
|
583
|
+
{
|
|
584
|
+
id: "source",
|
|
585
|
+
label: "Reference brief",
|
|
586
|
+
order: 20,
|
|
587
|
+
onSelect: attach,
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
id: "workspace-tools",
|
|
591
|
+
label: "Workspace tools",
|
|
592
|
+
intent: "invoke-integration",
|
|
593
|
+
children: [
|
|
594
|
+
{ id: "provider", label: "Connected provider", onSelect: invoke },
|
|
595
|
+
],
|
|
596
|
+
},
|
|
597
|
+
],
|
|
598
|
+
});
|
|
599
|
+
await open();
|
|
600
|
+
expect(labels()).toEqual([
|
|
601
|
+
"Files and images",
|
|
602
|
+
"Reference brief",
|
|
603
|
+
"Workspace tools",
|
|
604
|
+
]);
|
|
605
|
+
expect(labels()).toContain("Workspace tools");
|
|
606
|
+
for (const label of [
|
|
607
|
+
"Schedule Task",
|
|
608
|
+
"Create Automation",
|
|
609
|
+
"Create Skill",
|
|
610
|
+
"Upload skill file",
|
|
611
|
+
"Integrations",
|
|
612
|
+
])
|
|
613
|
+
expect(labels()).not.toContain(label);
|
|
614
|
+
await choose("Workspace tools");
|
|
615
|
+
await choose("Connected provider");
|
|
616
|
+
expect(invoke).toHaveBeenCalledOnce();
|
|
617
|
+
expect(attach).not.toHaveBeenCalled();
|
|
618
|
+
expect(useOrg).not.toHaveBeenCalled();
|
|
619
|
+
await open();
|
|
620
|
+
await choose("Reference brief");
|
|
621
|
+
expect(attach).toHaveBeenCalledOnce();
|
|
622
|
+
},
|
|
623
|
+
);
|
|
624
|
+
|
|
237
625
|
it.each([
|
|
238
626
|
["Schedule Task", "Create a recurring job: Draft", "manage-jobs"],
|
|
239
627
|
["Create Automation", "Create an automation: Draft", "manage-automations"],
|
|
240
628
|
["Create Extension", "Create an extension: Draft", "create-extension"],
|
|
241
|
-
["Create
|
|
629
|
+
["Create Skill", "Create a skill: Draft", "resources"],
|
|
242
630
|
])(
|
|
243
631
|
"+ submits %s instructions through the accepted host path",
|
|
244
632
|
async (action, message, context) => {
|
|
@@ -249,8 +637,7 @@ describe("shared default action preservation", () => {
|
|
|
249
637
|
extensionTools: true,
|
|
250
638
|
});
|
|
251
639
|
await open();
|
|
252
|
-
expect(labels()).
|
|
253
|
-
if (action === "Create new skill") await choose("Create Skill");
|
|
640
|
+
expect(labels()).toContain("Host source");
|
|
254
641
|
await choose(action);
|
|
255
642
|
expect(
|
|
256
643
|
container.querySelector('[data-agent-composer-slot="mode-row"]'),
|
|
@@ -331,14 +718,14 @@ describe("shared default action preservation", () => {
|
|
|
331
718
|
await mount({ attachmentsEnabled: false, onAttachmentRequest });
|
|
332
719
|
await open();
|
|
333
720
|
expect(onAttachmentRequest).not.toHaveBeenCalled();
|
|
334
|
-
await choose("
|
|
721
|
+
await choose("Files and images");
|
|
335
722
|
await act(async () => {
|
|
336
723
|
await new Promise(requestAnimationFrame);
|
|
337
724
|
});
|
|
338
725
|
expect(onAttachmentRequest).toHaveBeenCalledOnce();
|
|
339
726
|
await mount({ attachmentsEnabled: false });
|
|
340
727
|
await open();
|
|
341
|
-
expect(labels()).not.toContain("
|
|
728
|
+
expect(labels()).not.toContain("Files and images");
|
|
342
729
|
await close();
|
|
343
730
|
await mount();
|
|
344
731
|
const input = container.querySelector<HTMLInputElement>(
|
|
@@ -346,7 +733,7 @@ describe("shared default action preservation", () => {
|
|
|
346
733
|
)!;
|
|
347
734
|
const click = vi.spyOn(input, "click").mockImplementation(() => {});
|
|
348
735
|
await open();
|
|
349
|
-
await choose("
|
|
736
|
+
await choose("Files and images");
|
|
350
737
|
expect(click).toHaveBeenCalledOnce();
|
|
351
738
|
});
|
|
352
739
|
|
|
@@ -434,7 +821,7 @@ describe("shared default action preservation", () => {
|
|
|
434
821
|
contextMenuItems: [{ id: "host", label: "Host source", onSelect }],
|
|
435
822
|
});
|
|
436
823
|
await open(true);
|
|
437
|
-
expect(labels()).toEqual(["
|
|
824
|
+
expect(labels()).toEqual(["Files and images", "Host source"]);
|
|
438
825
|
await choose("Host source");
|
|
439
826
|
expect(onSelect).toHaveBeenCalledOnce();
|
|
440
827
|
expect(useOrg).not.toHaveBeenCalled();
|
|
@@ -659,24 +1046,32 @@ describe("shared default action preservation", () => {
|
|
|
659
1046
|
);
|
|
660
1047
|
});
|
|
661
1048
|
|
|
662
|
-
it("
|
|
1049
|
+
it("labels chat actions as Commands and keeps context at the root", async () => {
|
|
663
1050
|
await mount({
|
|
664
1051
|
contextMenuItems: [
|
|
665
1052
|
{ id: "host-source", label: "Host source", onSelect: vi.fn() },
|
|
666
1053
|
],
|
|
667
1054
|
});
|
|
668
1055
|
await open();
|
|
1056
|
+
expect(labels()).toEqual([
|
|
1057
|
+
"Files and images",
|
|
1058
|
+
"Host source",
|
|
1059
|
+
"Integrations",
|
|
1060
|
+
"Schedule Task",
|
|
1061
|
+
"Create Automation",
|
|
1062
|
+
"Create Skill",
|
|
1063
|
+
"Upload skill file",
|
|
1064
|
+
]);
|
|
669
1065
|
expect(labels()).toContain("Schedule Task");
|
|
670
1066
|
expect(labels()).toContain("Create Automation");
|
|
671
1067
|
expect(labels()).toContain("Create Skill");
|
|
672
1068
|
expect(labels()).not.toContain("Generate Image");
|
|
673
|
-
expect(labels()).
|
|
674
|
-
await choose("Add context");
|
|
1069
|
+
expect(labels()).toContain("Host source");
|
|
675
1070
|
expect(labels()).toContain("Host source");
|
|
676
1071
|
expect(labels()).toContain("Schedule Task");
|
|
677
1072
|
});
|
|
678
1073
|
|
|
679
|
-
it("cancels a skill save when its
|
|
1074
|
+
it("cancels a skill save when its dialog closes and retains retry after errors", async () => {
|
|
680
1075
|
let resolve!: (response: Response) => void;
|
|
681
1076
|
const fetch = vi
|
|
682
1077
|
.fn()
|
|
@@ -717,19 +1112,21 @@ describe("shared default action preservation", () => {
|
|
|
717
1112
|
await settle();
|
|
718
1113
|
});
|
|
719
1114
|
expect(request.signal?.aborted).toBe(true);
|
|
720
|
-
expect(document.querySelector('[role="
|
|
1115
|
+
expect(document.querySelector('[role="dialog"] form')).toBeNull();
|
|
721
1116
|
await act(async () => {
|
|
722
1117
|
resolve(new Response("{}"));
|
|
723
1118
|
await settle();
|
|
724
1119
|
});
|
|
725
|
-
expect(
|
|
1120
|
+
expect(
|
|
1121
|
+
document.querySelector('[role="status"]')?.textContent,
|
|
1122
|
+
).not.toContain("added");
|
|
1123
|
+
await open();
|
|
726
1124
|
expect(item("Upload skill file")).toBeDefined();
|
|
727
1125
|
});
|
|
728
1126
|
|
|
729
|
-
it("
|
|
1127
|
+
it("opens skill-file review in a dialog after the native picker returns", async () => {
|
|
730
1128
|
await mount();
|
|
731
1129
|
await open();
|
|
732
|
-
await choose("Create Skill");
|
|
733
1130
|
await choose("Upload skill file");
|
|
734
1131
|
|
|
735
1132
|
await act(async () => {
|
|
@@ -751,7 +1148,7 @@ describe("shared default action preservation", () => {
|
|
|
751
1148
|
await settle();
|
|
752
1149
|
});
|
|
753
1150
|
|
|
754
|
-
const form = document.querySelector<HTMLFormElement>("[role=
|
|
1151
|
+
const form = document.querySelector<HTMLFormElement>("[role=dialog] form");
|
|
755
1152
|
expect(form).not.toBeNull();
|
|
756
1153
|
expect(form?.textContent).toContain(
|
|
757
1154
|
"Review the content from Test Skill.md before saving.",
|
|
@@ -761,10 +1158,9 @@ describe("shared default action preservation", () => {
|
|
|
761
1158
|
);
|
|
762
1159
|
});
|
|
763
1160
|
|
|
764
|
-
it("
|
|
1161
|
+
it("leaves the menu closed when the native picker is canceled", async () => {
|
|
765
1162
|
await mount();
|
|
766
1163
|
await open();
|
|
767
|
-
await choose("Create Skill");
|
|
768
1164
|
await choose("Upload skill file");
|
|
769
1165
|
|
|
770
1166
|
await act(async () => {
|
|
@@ -779,41 +1175,25 @@ describe("shared default action preservation", () => {
|
|
|
779
1175
|
await settle();
|
|
780
1176
|
});
|
|
781
1177
|
|
|
782
|
-
expect(
|
|
1178
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
1179
|
+
expect(document.querySelector('[role="menu"]')).toBeNull();
|
|
783
1180
|
});
|
|
784
1181
|
|
|
785
|
-
it("
|
|
1182
|
+
it("closes skill review and aborts its pending save when the composer is disabled", async () => {
|
|
1183
|
+
const fetch = vi.fn(() => new Promise<Response>(() => {}));
|
|
1184
|
+
vi.stubGlobal("fetch", fetch);
|
|
786
1185
|
await mount();
|
|
787
1186
|
await open();
|
|
788
|
-
await
|
|
789
|
-
await
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
const timeoutCallIndex = scheduledTimeouts.mock.calls.length;
|
|
800
|
-
act(() => input.dispatchEvent(new Event("cancel")));
|
|
801
|
-
expect(vi.getTimerCount()).toBe(timersBeforeCancel + 1);
|
|
802
|
-
const resumeTimer =
|
|
803
|
-
scheduledTimeouts.mock.results[timeoutCallIndex]?.value;
|
|
804
|
-
expect(resumeTimer).toBeDefined();
|
|
805
|
-
|
|
806
|
-
await mount({ disabled: true }, false);
|
|
807
|
-
expect(clearedTimeouts).toHaveBeenCalledWith(resumeTimer);
|
|
808
|
-
|
|
809
|
-
await act(async () => {
|
|
810
|
-
await vi.advanceTimersByTimeAsync(0);
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
expect(document.querySelector("[role=menu] form")).toBeNull();
|
|
814
|
-
expect(labels()).not.toContain("Upload skill file");
|
|
815
|
-
} finally {
|
|
816
|
-
vi.useRealTimers();
|
|
817
|
-
}
|
|
1187
|
+
const form = await uploadSkill();
|
|
1188
|
+
await act(async () => {
|
|
1189
|
+
form.dispatchEvent(
|
|
1190
|
+
new Event("submit", { bubbles: true, cancelable: true }),
|
|
1191
|
+
);
|
|
1192
|
+
await settle();
|
|
1193
|
+
});
|
|
1194
|
+
const request = fetch.mock.calls[0][1] as RequestInit;
|
|
1195
|
+
await mount({ disabled: true });
|
|
1196
|
+
expect(request.signal?.aborted).toBe(true);
|
|
1197
|
+
expect(document.querySelector('[role="dialog"]')).toBeNull();
|
|
818
1198
|
});
|
|
819
1199
|
});
|