@agent-native/toolkit 0.204.0 → 0.204.1-nightly-20261008034100

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 (144) hide show
  1. package/dist/app/agent-page/AgentJobsTab.d.ts.map +1 -1
  2. package/dist/app/agent-page/AgentJobsTab.js +4 -2
  3. package/dist/app/agent-page/AgentJobsTab.js.map +1 -1
  4. package/dist/app/agent-page/AutomationDetailsDialog.d.ts +2 -1
  5. package/dist/app/agent-page/AutomationDetailsDialog.d.ts.map +1 -1
  6. package/dist/app/agent-page/AutomationDetailsDialog.js +8 -4
  7. package/dist/app/agent-page/AutomationDetailsDialog.js.map +1 -1
  8. package/dist/app/agent-page/ScheduledTriggerNotice.d.ts.map +1 -1
  9. package/dist/app/agent-page/ScheduledTriggerNotice.js +41 -19
  10. package/dist/app/agent-page/ScheduledTriggerNotice.js.map +1 -1
  11. package/dist/app/agent-page/use-jobs.d.ts +1 -1
  12. package/dist/app/agent-page/use-jobs.d.ts.map +1 -1
  13. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  14. package/dist/app/agentkit/react/components.js +124 -45
  15. package/dist/app/agentkit/react/components.js.map +1 -1
  16. package/dist/app/agentkit/react/context.d.ts +22 -1
  17. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  18. package/dist/app/agentkit/react/context.js +14 -1
  19. package/dist/app/agentkit/react/context.js.map +1 -1
  20. package/dist/app/agentkit/react/root.d.ts +3 -2
  21. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  22. package/dist/app/agentkit/react/root.js +2 -2
  23. package/dist/app/agentkit/react/root.js.map +1 -1
  24. package/dist/app/agentkit/react/styles.css +32 -2
  25. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  26. package/dist/app/chat/AgentKitAssistantChat.js +28 -3
  27. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  28. package/dist/app/chat/AgentPanel.js +1 -1
  29. package/dist/app/chat/AgentPanel.js.map +1 -1
  30. package/dist/app/chat/RunStuckBanner.d.ts +8 -1
  31. package/dist/app/chat/RunStuckBanner.d.ts.map +1 -1
  32. package/dist/app/chat/RunStuckBanner.js +68 -6
  33. package/dist/app/chat/RunStuckBanner.js.map +1 -1
  34. package/dist/app/chat/chat/run-recovery.d.ts +4 -2
  35. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  36. package/dist/app/chat/chat/run-recovery.js +3 -3
  37. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  38. package/dist/app/chat/composer/use-mention-search.d.ts +1 -0
  39. package/dist/app/chat/composer/use-mention-search.d.ts.map +1 -1
  40. package/dist/app/chat/feedback-report.d.ts +12 -0
  41. package/dist/app/chat/feedback-report.d.ts.map +1 -0
  42. package/dist/app/chat/feedback-report.js +21 -0
  43. package/dist/app/chat/feedback-report.js.map +1 -0
  44. package/dist/app/chat/reconcile-settled-run.d.ts +15 -0
  45. package/dist/app/chat/reconcile-settled-run.d.ts.map +1 -0
  46. package/dist/app/chat/reconcile-settled-run.js +41 -0
  47. package/dist/app/chat/reconcile-settled-run.js.map +1 -0
  48. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/ar-SA.js +9 -0
  50. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  51. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/de-DE.js +9 -0
  53. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  54. package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
  55. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  56. package/dist/app/i18n/catalogs/en-US.js +9 -0
  57. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  58. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  59. package/dist/app/i18n/catalogs/es-ES.js +9 -0
  60. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  61. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  62. package/dist/app/i18n/catalogs/fr-FR.js +9 -0
  63. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  64. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  65. package/dist/app/i18n/catalogs/hi-IN.js +9 -0
  66. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  67. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  68. package/dist/app/i18n/catalogs/ja-JP.js +9 -0
  69. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  70. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  71. package/dist/app/i18n/catalogs/ko-KR.js +9 -0
  72. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  73. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  74. package/dist/app/i18n/catalogs/pt-BR.js +9 -0
  75. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  76. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  77. package/dist/app/i18n/catalogs/zh-CN.js +9 -0
  78. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  79. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  80. package/dist/app/i18n/catalogs/zh-TW.js +9 -0
  81. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  82. package/dist/app/settings/shell/pages/audit.d.ts.map +1 -1
  83. package/dist/app/settings/shell/pages/audit.js +11 -2
  84. package/dist/app/settings/shell/pages/audit.js.map +1 -1
  85. package/dist/composer/ComposerContextMenu.d.ts +14 -1
  86. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  87. package/dist/composer/ComposerContextMenu.js +65 -19
  88. package/dist/composer/ComposerContextMenu.js.map +1 -1
  89. package/dist/composer/MentionPopover.d.ts.map +1 -1
  90. package/dist/composer/MentionPopover.js +3 -3
  91. package/dist/composer/MentionPopover.js.map +1 -1
  92. package/dist/composer/PromptComposer.d.ts +1 -1
  93. package/dist/composer/PromptComposer.d.ts.map +1 -1
  94. package/dist/composer/TiptapComposer.d.ts +2 -2
  95. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  96. package/dist/composer/TiptapComposer.js +92 -36
  97. package/dist/composer/TiptapComposer.js.map +1 -1
  98. package/dist/composer/use-mention-search.d.ts +1 -0
  99. package/dist/composer/use-mention-search.d.ts.map +1 -1
  100. package/dist/composer/use-mention-search.js +8 -1
  101. package/dist/composer/use-mention-search.js.map +1 -1
  102. package/package.json +4 -4
  103. package/src/app/agent-page/AgentJobsTab.blocked.spec.tsx +43 -0
  104. package/src/app/agent-page/AgentJobsTab.tsx +14 -5
  105. package/src/app/agent-page/AutomationDetailsDialog.tsx +22 -8
  106. package/src/app/agent-page/ScheduledTriggerNotice.spec.tsx +45 -3
  107. package/src/app/agent-page/ScheduledTriggerNotice.tsx +52 -31
  108. package/src/app/agent-page/use-jobs.ts +1 -1
  109. package/src/app/agentkit/react/components.interaction.spec.tsx +311 -1
  110. package/src/app/agentkit/react/components.spec.tsx +106 -2
  111. package/src/app/agentkit/react/components.tsx +217 -80
  112. package/src/app/agentkit/react/context.tsx +36 -0
  113. package/src/app/agentkit/react/root.tsx +4 -0
  114. package/src/app/agentkit/react/styles.css +32 -2
  115. package/src/app/chat/AgentKitAssistantChat.tsx +35 -2
  116. package/src/app/chat/AgentPanel.header.spec.ts +43 -7
  117. package/src/app/chat/AgentPanel.tsx +1 -1
  118. package/src/app/chat/RunStuckBanner.spec.tsx +258 -17
  119. package/src/app/chat/RunStuckBanner.tsx +107 -5
  120. package/src/app/chat/chat/run-recovery.tsx +10 -2
  121. package/src/app/chat/feedback-report.spec.ts +45 -0
  122. package/src/app/chat/feedback-report.ts +27 -0
  123. package/src/app/chat/reconcile-settled-run.spec.ts +112 -0
  124. package/src/app/chat/reconcile-settled-run.ts +57 -0
  125. package/src/app/i18n/catalogs/ar-SA.ts +12 -0
  126. package/src/app/i18n/catalogs/de-DE.ts +12 -0
  127. package/src/app/i18n/catalogs/en-US.ts +12 -0
  128. package/src/app/i18n/catalogs/es-ES.ts +12 -0
  129. package/src/app/i18n/catalogs/fr-FR.ts +12 -0
  130. package/src/app/i18n/catalogs/hi-IN.ts +12 -0
  131. package/src/app/i18n/catalogs/ja-JP.ts +12 -0
  132. package/src/app/i18n/catalogs/ko-KR.ts +12 -0
  133. package/src/app/i18n/catalogs/pt-BR.ts +12 -0
  134. package/src/app/i18n/catalogs/zh-CN.ts +10 -0
  135. package/src/app/i18n/catalogs/zh-TW.ts +10 -0
  136. package/src/app/settings/shell/pages/audit.spec.tsx +30 -0
  137. package/src/app/settings/shell/pages/audit.tsx +12 -1
  138. package/src/composer/ComposerContextMenu.tsx +94 -22
  139. package/src/composer/ComposerPlusMenu.parity.spec.tsx +218 -233
  140. package/src/composer/MentionPopover.tsx +12 -3
  141. package/src/composer/PromptComposer.context.spec.tsx +526 -195
  142. package/src/composer/PromptComposer.tsx +1 -1
  143. package/src/composer/TiptapComposer.tsx +105 -37
  144. package/src/composer/use-mention-search.ts +8 -1
@@ -96,17 +96,16 @@ async function mount(props: Partial<PromptComposerProps> = {}) {
96
96
  });
97
97
  return { composerRef, onSubmit };
98
98
  }
99
- async function open(trigger: "+" | "@", enterAddContext = false) {
100
- const target =
101
- trigger === "+"
102
- ? container.querySelector<HTMLElement>('button[aria-label="Add context"]')
103
- : container.querySelector<HTMLElement>(".ProseMirror");
99
+ async function open(enterAddContext = false) {
100
+ const target = container.querySelector<HTMLElement>(
101
+ 'button[aria-label="Add context"]',
102
+ );
104
103
  expect(target).not.toBeNull();
105
104
  await act(async () => {
106
105
  target!.focus();
107
106
  target!.dispatchEvent(
108
107
  new KeyboardEvent("keydown", {
109
- key: trigger === "+" ? "ArrowDown" : "@",
108
+ key: "ArrowDown",
110
109
  bubbles: true,
111
110
  cancelable: true,
112
111
  }),
@@ -193,250 +192,236 @@ describe("shared default action preservation", () => {
193
192
  expect(merged[0]).toBe(provided[0]);
194
193
  });
195
194
 
196
- for (const trigger of ["+", "@"] as const) {
197
- it.each(["full", "upload-only", "terminal", "hidden"] as const)(
198
- `${trigger} respects %s action and resource gates`,
199
- async (mode) => {
200
- const onChange = vi.fn();
201
- await mount({
202
- plusMenuMode: mode,
203
- terminalModeControl: { enabled: false, onChange },
204
- });
205
- if (mode === "hidden") {
206
- expect(
207
- container.querySelector('button[aria-label="Add context"]'),
208
- ).toBeNull();
209
- if (trigger === "@") {
210
- await open(trigger);
211
- expect(document.querySelector('[role="menu"]')).toBeNull();
212
- }
213
- } else {
214
- await open(trigger, false);
215
- const expected =
216
- mode === "full"
217
- ? [
218
- "Upload File",
219
- "Schedule Task",
220
- "Create Automation",
221
- "Integrations",
222
- "Create Skill",
223
- ]
224
- : mode === "terminal"
225
- ? ["New terminal", "CLI terminal mode"]
226
- : ["Upload File"];
227
- expect(labels()).toEqual(expected);
228
- }
229
- if (mode === "full") {
230
- expect(useOrg).toHaveBeenCalled();
231
- expect(useCreateMcpServer).toHaveBeenCalled();
232
- } else {
233
- expect(useOrg).not.toHaveBeenCalled();
234
- expect(useCreateMcpServer).not.toHaveBeenCalled();
235
- expect(isMcpIntegrationAvailable).not.toHaveBeenCalled();
236
- }
237
- },
238
- );
239
-
240
- it.each([
241
- ["Schedule Task", "Create a recurring job: Draft", "manage-jobs"],
242
- [
243
- "Create Automation",
244
- "Create an automation: Draft",
245
- "manage-automations",
246
- ],
247
- ["Create Extension", "Create an extension: Draft", "create-extension"],
248
- ["Create new skill", "Create a skill: Draft", "resources"],
249
- ])(
250
- `${trigger} submits %s instructions through the accepted host path`,
251
- async (action, message, context) => {
252
- const { composerRef, onSubmit } = await mount({
253
- contextMenuItems: [
254
- { id: "host", label: "Host source", onSelect: vi.fn() },
255
- ],
256
- extensionTools: true,
257
- });
258
- await open(trigger, false);
259
- expect(labels()).not.toContain("Host source");
260
- if (action === "Create new skill") await choose("Create Skill");
261
- await choose(action);
262
- expect(
263
- container.querySelector('[data-agent-composer-slot="mode-row"]'),
264
- ).not.toBeNull();
265
- await act(async () => {
266
- expect(await composerRef.current!.submitWithText("Draft")).toBe(true);
267
- });
268
- expect(onSubmit).toHaveBeenCalledExactlyOnceWith(
269
- message,
270
- [],
271
- [],
272
- expect.objectContaining({
273
- composerModeContext: expect.stringContaining(context),
274
- intent: "immediate",
275
- }),
276
- );
277
- expect(sendToAgentChat).not.toHaveBeenCalled();
195
+ it.each(["full", "upload-only", "terminal", "hidden"] as const)(
196
+ "+ respects %s action and resource gates",
197
+ async (mode) => {
198
+ const onChange = vi.fn();
199
+ await mount({
200
+ plusMenuMode: mode,
201
+ terminalModeControl: { enabled: false, onChange },
202
+ });
203
+ if (mode === "hidden") {
278
204
  expect(
279
- container.querySelector('[data-agent-composer-slot="mode-row"]'),
205
+ container.querySelector('button[aria-label="Add context"]'),
280
206
  ).toBeNull();
281
- },
282
- );
207
+ } else {
208
+ await open();
209
+ const expected =
210
+ mode === "full"
211
+ ? [
212
+ "Upload File",
213
+ "Schedule Task",
214
+ "Create Automation",
215
+ "Integrations",
216
+ "Create Skill",
217
+ ]
218
+ : mode === "terminal"
219
+ ? ["New terminal", "CLI terminal mode"]
220
+ : ["Upload File"];
221
+ expect(labels()).toEqual(expected);
222
+ }
223
+ if (mode === "full") {
224
+ expect(useOrg).toHaveBeenCalled();
225
+ expect(useCreateMcpServer).toHaveBeenCalled();
226
+ } else {
227
+ expect(useOrg).not.toHaveBeenCalled();
228
+ expect(useCreateMcpServer).not.toHaveBeenCalled();
229
+ expect(isMcpIntegrationAvailable).not.toHaveBeenCalled();
230
+ }
231
+ },
232
+ );
283
233
 
284
- it(`${trigger} clears automation mode through its accessible Cancel button`, async () => {
285
- const { composerRef, onSubmit } = await mount();
286
- await open(trigger, false);
287
- await choose("Create Automation");
288
- const cancel = container.querySelector<HTMLButtonElement>(
289
- '[data-agent-composer-slot="mode-row"] button[aria-label="Cancel"]',
290
- );
291
- expect(cancel).not.toBeNull();
292
- await act(async () => {
293
- cancel!.focus();
294
- cancel!.click();
234
+ it.each([
235
+ ["Schedule Task", "Create a recurring job: Draft", "manage-jobs"],
236
+ ["Create Automation", "Create an automation: Draft", "manage-automations"],
237
+ ["Create Extension", "Create an extension: Draft", "create-extension"],
238
+ ["Create new skill", "Create a skill: Draft", "resources"],
239
+ ])(
240
+ "+ submits %s instructions through the accepted host path",
241
+ async (action, message, context) => {
242
+ const { composerRef, onSubmit } = await mount({
243
+ contextMenuItems: [
244
+ { id: "host", label: "Host source", onSelect: vi.fn() },
245
+ ],
246
+ extensionTools: true,
295
247
  });
248
+ await open();
249
+ expect(labels()).not.toContain("Host source");
250
+ if (action === "Create new skill") await choose("Create Skill");
251
+ await choose(action);
296
252
  expect(
297
253
  container.querySelector('[data-agent-composer-slot="mode-row"]'),
298
- ).toBeNull();
299
- expect(container.querySelector(".ProseMirror")?.textContent).toContain(
300
- "Draft",
301
- );
302
- expect(document.activeElement).toBe(
303
- container.querySelector(".ProseMirror"),
304
- );
254
+ ).not.toBeNull();
305
255
  await act(async () => {
306
256
  expect(await composerRef.current!.submitWithText("Draft")).toBe(true);
307
257
  });
308
- expect(onSubmit.mock.calls[0][0]).toBe("Draft");
309
- expect(onSubmit.mock.calls[0][3]).not.toHaveProperty(
310
- "composerModeContext",
258
+ expect(onSubmit).toHaveBeenCalledExactlyOnceWith(
259
+ message,
260
+ [],
261
+ [],
262
+ expect.objectContaining({
263
+ composerModeContext: expect.stringContaining(context),
264
+ intent: "immediate",
265
+ }),
311
266
  );
312
267
  expect(sendToAgentChat).not.toHaveBeenCalled();
268
+ expect(
269
+ container.querySelector('[data-agent-composer-slot="mode-row"]'),
270
+ ).toBeNull();
271
+ },
272
+ );
273
+
274
+ it("+ clears automation mode through its accessible Cancel button", async () => {
275
+ const { composerRef, onSubmit } = await mount();
276
+ await open();
277
+ await choose("Create Automation");
278
+ const cancel = container.querySelector<HTMLButtonElement>(
279
+ '[data-agent-composer-slot="mode-row"] button[aria-label="Cancel"]',
280
+ );
281
+ expect(cancel).not.toBeNull();
282
+ await act(async () => {
283
+ cancel!.focus();
284
+ cancel!.click();
285
+ });
286
+ expect(
287
+ container.querySelector('[data-agent-composer-slot="mode-row"]'),
288
+ ).toBeNull();
289
+ expect(container.querySelector(".ProseMirror")?.textContent).toContain(
290
+ "Draft",
291
+ );
292
+ expect(document.activeElement).toBe(
293
+ container.querySelector(".ProseMirror"),
294
+ );
295
+ await act(async () => {
296
+ expect(await composerRef.current!.submitWithText("Draft")).toBe(true);
313
297
  });
298
+ expect(onSubmit.mock.calls[0][0]).toBe("Draft");
299
+ expect(onSubmit.mock.calls[0][3]).not.toHaveProperty("composerModeContext");
300
+ expect(sendToAgentChat).not.toHaveBeenCalled();
301
+ });
314
302
 
315
- it(`${trigger} uses host Integrations, without abandoning its configuration action`, async () => {
316
- const configure = vi.fn();
317
- await mount({
318
- contextMenuItems: [
319
- {
320
- id: "integrations",
321
- label: "Integrations",
322
- children: [
323
- { id: "connect", label: "Connect / Manage", onSelect: configure },
324
- ],
325
- },
326
- ],
327
- });
328
- await open(trigger, false);
329
- expect(labels().filter((label) => label === "Integrations")).toHaveLength(
330
- 1,
331
- );
332
- await choose("Integrations");
333
- await choose("Connect / Manage");
334
- expect(configure).toHaveBeenCalledOnce();
335
- expect(document.querySelector('[role="dialog"]')).toBeNull();
303
+ it("+ uses host Integrations, without abandoning its configuration action", async () => {
304
+ const configure = vi.fn();
305
+ await mount({
306
+ contextMenuItems: [
307
+ {
308
+ id: "integrations",
309
+ label: "Integrations",
310
+ children: [
311
+ { id: "connect", label: "Connect / Manage", onSelect: configure },
312
+ ],
313
+ },
314
+ ],
336
315
  });
316
+ await open();
317
+ expect(labels().filter((label) => label === "Integrations")).toHaveLength(
318
+ 1,
319
+ );
320
+ await choose("Integrations");
321
+ await choose("Connect / Manage");
322
+ expect(configure).toHaveBeenCalledOnce();
323
+ expect(document.querySelector('[role="dialog"]')).toBeNull();
324
+ });
337
325
 
338
- it(`${trigger} preserves native upload and storage-setup handoff`, async () => {
339
- const onAttachmentRequest = vi.fn();
340
- await mount({ attachmentsEnabled: false, onAttachmentRequest });
341
- await open(trigger, false);
342
- expect(onAttachmentRequest).not.toHaveBeenCalled();
343
- await choose("Upload File");
344
- await act(async () => {
345
- await new Promise(requestAnimationFrame);
346
- });
347
- expect(onAttachmentRequest).toHaveBeenCalledOnce();
348
- await mount({ attachmentsEnabled: false });
349
- await open(trigger, false);
350
- expect(labels()).not.toContain("Upload File");
351
- await close();
352
- await mount();
353
- const input = container.querySelector<HTMLInputElement>(
354
- 'input[type="file"][multiple]',
355
- )!;
356
- const click = vi.spyOn(input, "click").mockImplementation(() => {});
357
- await open(trigger, false);
358
- await choose("Upload File");
359
- expect(click).toHaveBeenCalledOnce();
326
+ it("+ preserves native upload and storage-setup handoff", async () => {
327
+ const onAttachmentRequest = vi.fn();
328
+ await mount({ attachmentsEnabled: false, onAttachmentRequest });
329
+ await open();
330
+ expect(onAttachmentRequest).not.toHaveBeenCalled();
331
+ await choose("Upload File");
332
+ await act(async () => {
333
+ await new Promise(requestAnimationFrame);
360
334
  });
335
+ expect(onAttachmentRequest).toHaveBeenCalledOnce();
336
+ await mount({ attachmentsEnabled: false });
337
+ await open();
338
+ expect(labels()).not.toContain("Upload File");
339
+ await close();
340
+ await mount();
341
+ const input = container.querySelector<HTMLInputElement>(
342
+ 'input[type="file"][multiple]',
343
+ )!;
344
+ const click = vi.spyOn(input, "click").mockImplementation(() => {});
345
+ await open();
346
+ await choose("Upload File");
347
+ expect(click).toHaveBeenCalledOnce();
348
+ });
361
349
 
362
- it(`${trigger} retains terminal creation and checked mode controls`, async () => {
363
- const onChange = vi.fn();
364
- const onNewTerminal = vi.fn();
365
- await mount({
366
- plusMenuMode: "terminal",
367
- terminalModeControl: { enabled: false, onChange, onNewTerminal },
368
- });
369
- await open(trigger, false);
370
- await choose("New terminal");
371
- expect(onChange).toHaveBeenCalledExactlyOnceWith(true);
372
- expect(onNewTerminal).not.toHaveBeenCalled();
373
- await mount({
374
- plusMenuMode: "terminal",
375
- terminalModeControl: { enabled: true, onChange, onNewTerminal },
376
- });
377
- await open(trigger, false);
378
- expect(item("CLI terminal mode").getAttribute("aria-checked")).toBe(
379
- "true",
380
- );
381
- await choose("New terminal");
382
- expect(onNewTerminal).toHaveBeenCalledOnce();
383
- await open(trigger, false);
384
- await choose("CLI terminal mode");
385
- expect(onChange).toHaveBeenLastCalledWith(false);
386
- expect(useOrg).not.toHaveBeenCalled();
350
+ it("+ retains terminal creation and checked mode controls", async () => {
351
+ const onChange = vi.fn();
352
+ const onNewTerminal = vi.fn();
353
+ await mount({
354
+ plusMenuMode: "terminal",
355
+ terminalModeControl: { enabled: false, onChange, onNewTerminal },
387
356
  });
357
+ await open();
358
+ await choose("New terminal");
359
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(true);
360
+ expect(onNewTerminal).not.toHaveBeenCalled();
361
+ await mount({
362
+ plusMenuMode: "terminal",
363
+ terminalModeControl: { enabled: true, onChange, onNewTerminal },
364
+ });
365
+ await open();
366
+ expect(item("CLI terminal mode").getAttribute("aria-checked")).toBe("true");
367
+ await choose("New terminal");
368
+ expect(onNewTerminal).toHaveBeenCalledOnce();
369
+ await open();
370
+ await choose("CLI terminal mode");
371
+ expect(onChange).toHaveBeenLastCalledWith(false);
372
+ expect(useOrg).not.toHaveBeenCalled();
373
+ });
388
374
 
389
- it(`${trigger} keeps the skill filename, review, save payload, and duplicate-save lock`, async () => {
390
- let resolve!: (response: Response) => void;
391
- const fetch = vi.fn(
392
- () =>
393
- new Promise<Response>((done) => {
394
- resolve = done;
395
- }),
396
- );
397
- vi.stubGlobal("fetch", fetch);
398
- await mount();
399
- await open(trigger, false);
400
- const form = await uploadSkill();
401
- expect(form).not.toBeNull();
402
- expect(form.textContent).toContain(
403
- "Review the content from Test Skill.md before saving.",
404
- );
405
- expect(form.textContent).toContain("skills/test-skill/SKILL.md");
406
- expect(form.querySelector("textarea")?.value).toBe(
407
- "# Test skill\nDo the test.",
408
- );
409
- await act(async () => {
410
- form.dispatchEvent(
411
- new Event("submit", { bubbles: true, cancelable: true }),
412
- );
413
- form.dispatchEvent(
414
- new Event("submit", { bubbles: true, cancelable: true }),
415
- );
416
- await settle();
417
- });
418
- expect(fetch).toHaveBeenCalledExactlyOnceWith(
419
- "/test-app/_agent-native/resources",
420
- expect.objectContaining({
421
- method: "POST",
422
- signal: expect.any(AbortSignal),
423
- body: JSON.stringify({
424
- path: "skills/test-skill/SKILL.md",
425
- content: "# Test skill\nDo the test.",
426
- mimeType: "text/markdown",
427
- shared: false,
428
- }),
375
+ it("+ keeps the skill filename, review, save payload, and duplicate-save lock", async () => {
376
+ let resolve!: (response: Response) => void;
377
+ const fetch = vi.fn(
378
+ () =>
379
+ new Promise<Response>((done) => {
380
+ resolve = done;
429
381
  }),
382
+ );
383
+ vi.stubGlobal("fetch", fetch);
384
+ await mount();
385
+ await open();
386
+ const form = await uploadSkill();
387
+ expect(form).not.toBeNull();
388
+ expect(form.textContent).toContain(
389
+ "Review the content from Test Skill.md before saving.",
390
+ );
391
+ expect(form.textContent).toContain("skills/test-skill/SKILL.md");
392
+ expect(form.querySelector("textarea")?.value).toBe(
393
+ "# Test skill\nDo the test.",
394
+ );
395
+ await act(async () => {
396
+ form.dispatchEvent(
397
+ new Event("submit", { bubbles: true, cancelable: true }),
430
398
  );
431
- await act(async () => {
432
- resolve(new Response("{}"));
433
- await settle();
434
- });
435
- expect(form.querySelector('[role="status"]')?.textContent).toContain(
436
- 'Skill "Test Skill.md" added',
399
+ form.dispatchEvent(
400
+ new Event("submit", { bubbles: true, cancelable: true }),
437
401
  );
402
+ await settle();
438
403
  });
439
- }
404
+ expect(fetch).toHaveBeenCalledExactlyOnceWith(
405
+ "/test-app/_agent-native/resources",
406
+ expect.objectContaining({
407
+ method: "POST",
408
+ signal: expect.any(AbortSignal),
409
+ body: JSON.stringify({
410
+ path: "skills/test-skill/SKILL.md",
411
+ content: "# Test skill\nDo the test.",
412
+ mimeType: "text/markdown",
413
+ shared: false,
414
+ }),
415
+ }),
416
+ );
417
+ await act(async () => {
418
+ resolve(new Response("{}"));
419
+ await settle();
420
+ });
421
+ expect(form.querySelector('[role="status"]')?.textContent).toContain(
422
+ 'Skill "Test Skill.md" added',
423
+ );
424
+ });
440
425
 
441
426
  it("keeps explicit hidden host menus lightweight and preserves the host action", async () => {
442
427
  const onSelect = vi.fn();
@@ -444,7 +429,7 @@ describe("shared default action preservation", () => {
444
429
  plusMenuMode: "hidden",
445
430
  contextMenuItems: [{ id: "host", label: "Host source", onSelect }],
446
431
  });
447
- await open("@", true);
432
+ await open(true);
448
433
  expect(labels()).toEqual(["Upload File", "Add context", "Host source"]);
449
434
  await choose("Host source");
450
435
  expect(onSelect).toHaveBeenCalledOnce();
@@ -476,7 +461,7 @@ describe("shared default action preservation", () => {
476
461
  root.render(<RawHost />);
477
462
  await settle();
478
463
  });
479
- await open("+");
464
+ await open();
480
465
  await choose("Schedule Task");
481
466
  await act(async () => {
482
467
  expect(await composerRef.current!.submitWithText("Draft")).toBe(true);
@@ -572,9 +557,9 @@ describe("shared default action preservation", () => {
572
557
  root.render(<Host />);
573
558
  await settle();
574
559
  });
575
- await open("@", true);
560
+ await open(true);
576
561
  await choose("Attach integration");
577
- await open("+");
562
+ await open();
578
563
  await choose("Schedule Task");
579
564
  const file = new File(["test document"], "reference.pdf", {
580
565
  type: "application/pdf",
@@ -640,7 +625,7 @@ describe("shared default action preservation", () => {
640
625
 
641
626
  it("retains the fallback MCP dialog, role gates, and existing creation adapter", async () => {
642
627
  await mount();
643
- await open("+");
628
+ await open();
644
629
  await choose("Integrations");
645
630
  expect(dialogProps).toHaveBeenLastCalledWith(
646
631
  expect.objectContaining({
@@ -676,7 +661,7 @@ describe("shared default action preservation", () => {
676
661
  { id: "host-source", label: "Host source", onSelect: vi.fn() },
677
662
  ],
678
663
  });
679
- await open("+");
664
+ await open();
680
665
  expect(labels()).toContain("Schedule Task");
681
666
  expect(labels()).toContain("Create Automation");
682
667
  expect(labels()).toContain("Create Skill");
@@ -389,7 +389,10 @@ export const MentionPopover = forwardRef<
389
389
  {item.label}
390
390
  </span>
391
391
  {item.description && (
392
- <span className="ms-auto max-w-[45%] shrink-0 truncate text-[14px] text-muted-foreground">
392
+ <span
393
+ data-mention-description
394
+ className="ms-auto max-w-[45%] shrink-0 truncate text-[14px] text-muted-foreground"
395
+ >
393
396
  {item.description}
394
397
  </span>
395
398
  )}
@@ -431,7 +434,10 @@ export const MentionPopover = forwardRef<
431
434
  /{cmd.name}
432
435
  </span>
433
436
  {cmd.description && (
434
- <span className="ms-auto max-w-[60%] shrink-0 truncate text-[13px] leading-4 text-muted-foreground">
437
+ <span
438
+ data-mention-description
439
+ className="ms-auto max-w-[60%] shrink-0 truncate text-[13px] leading-4 text-muted-foreground"
440
+ >
435
441
  {cmd.description}
436
442
  </span>
437
443
  )}
@@ -472,7 +478,10 @@ export const MentionPopover = forwardRef<
472
478
  {skill.name}
473
479
  </span>
474
480
  {skill.description && (
475
- <span className="ms-auto max-w-[60%] shrink-0 truncate text-[13px] leading-4 text-muted-foreground">
481
+ <span
482
+ data-mention-description
483
+ className="ms-auto max-w-[60%] shrink-0 truncate text-[13px] leading-4 text-muted-foreground"
484
+ >
476
485
  {skill.description}
477
486
  </span>
478
487
  )}