@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
@@ -28,7 +28,17 @@ vi.mock("../../../design-system/index.js", async (importOriginal) => {
28
28
  "div",
29
29
  { className, onClick: () => onOpenChange?.(!open) },
30
30
  trigger,
31
- open ? children : null,
31
+ // Clicks inside an open popover never toggle it, as in the real one.
32
+ open
33
+ ? createElement(
34
+ "div",
35
+ {
36
+ onClick: (event: { stopPropagation: () => void }) =>
37
+ event.stopPropagation(),
38
+ },
39
+ children,
40
+ )
41
+ : null,
32
42
  ),
33
43
  };
34
44
  });
@@ -719,6 +729,103 @@ describe("AgentKitChat interactions", () => {
719
729
  }
720
730
  });
721
731
 
732
+ it.each([
733
+ ["resumes the stopped run's turn", true],
734
+ ["says so when the run can no longer be continued", false],
735
+ ])("Continue on a stopped run %s", async (_, accepted) => {
736
+ const continueRun = vi.fn(
737
+ async (
738
+ _input: Parameters<NonNullable<AgentTransport["continueRun"]>>[0],
739
+ ) => {
740
+ if (!accepted) throw new Error("This turn can no longer be continued.");
741
+ return { runId: "run-continued" };
742
+ },
743
+ );
744
+ const transport: AgentTransport = {
745
+ async startRun() {
746
+ return { runId: "run-crashed" };
747
+ },
748
+ async *subscribeToRun(input) {
749
+ if (input.runId !== "run-crashed") return;
750
+ const base = {
751
+ threadId: "thread-continue",
752
+ runId: "run-crashed",
753
+ occurredAt: "2026-10-05T00:00:00.000Z",
754
+ };
755
+ yield { ...base, id: "e1", sequence: 1, type: "run.started" };
756
+ yield {
757
+ ...base,
758
+ id: "e2",
759
+ sequence: 2,
760
+ type: "run.failed",
761
+ error: {
762
+ code: "stale_run",
763
+ message: "The agent stopped before it could finish.",
764
+ retryable: true,
765
+ },
766
+ };
767
+ },
768
+ continueRun,
769
+ async cancelRun() {},
770
+ };
771
+ const client = new AgentKitClient({ transport });
772
+ const run = await client.sendMessage({
773
+ threadId: "thread-continue",
774
+ text: "Bill Ana and email her the invoice",
775
+ });
776
+ await run.completed.catch(() => undefined);
777
+ const container = document.createElement("div");
778
+ document.body.appendChild(container);
779
+ const root = createRoot(container);
780
+ const actEnvironment = globalThis as typeof globalThis & {
781
+ IS_REACT_ACT_ENVIRONMENT?: boolean;
782
+ };
783
+ const previousActEnvironment = actEnvironment.IS_REACT_ACT_ENVIRONMENT;
784
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = true;
785
+ const continueButton = () =>
786
+ [
787
+ ...container.querySelectorAll<HTMLButtonElement>(
788
+ ".agentkit-run-failure button",
789
+ ),
790
+ ].find((button) => button.textContent === "Continue");
791
+
792
+ try {
793
+ await act(async () => {
794
+ root.render(
795
+ <AgentKitProvider controller={client} threadId="thread-continue">
796
+ <AgentKitChat composer={false} />
797
+ </AgentKitProvider>,
798
+ );
799
+ await Promise.resolve();
800
+ });
801
+ expect(continueButton()).toBeDefined();
802
+
803
+ await act(async () => {
804
+ continueButton()?.click();
805
+ await new Promise((resolve) => setTimeout(resolve, 0));
806
+ });
807
+
808
+ expect(continueRun).toHaveBeenCalledWith(
809
+ { threadId: "thread-continue", runId: "run-crashed" },
810
+ expect.anything(),
811
+ );
812
+ expect(container.textContent?.includes("can't be continued")).toBe(
813
+ !accepted,
814
+ );
815
+ } finally {
816
+ await act(async () => {
817
+ root.unmount();
818
+ await Promise.resolve();
819
+ });
820
+ container.remove();
821
+ if (previousActEnvironment === undefined) {
822
+ delete actEnvironment.IS_REACT_ACT_ENVIRONMENT;
823
+ } else {
824
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = previousActEnvironment;
825
+ }
826
+ }
827
+ });
828
+
722
829
  it("renders the default plus control and routes chat-wide file drops to the composer", async () => {
723
830
  const transport: AgentTransport = {
724
831
  capabilities: { uploads: true },
@@ -1190,6 +1297,209 @@ describe("AgentKitChat interactions", () => {
1190
1297
  }
1191
1298
  });
1192
1299
 
1300
+ it("fills the thumbs-down reason from chips and copies a report with the run, thread and note", async () => {
1301
+ const assistantMessage = {
1302
+ id: "assistant-report",
1303
+ role: "assistant" as const,
1304
+ status: "complete" as const,
1305
+ createdAt: "2026-09-26T00:00:00.000Z",
1306
+ parts: [{ type: "text" as const, text: "All done" }],
1307
+ };
1308
+ const transport: AgentTransport = {
1309
+ capabilities: { feedback: true },
1310
+ async submitFeedback() {},
1311
+ async startRun() {
1312
+ return { runId: "run-report" };
1313
+ },
1314
+ async *subscribeToRun() {},
1315
+ async cancelRun() {},
1316
+ async getThreadSnapshot(threadId) {
1317
+ return {
1318
+ id: threadId,
1319
+ createdAt: "2026-09-26T00:00:00.000Z",
1320
+ updatedAt: "2026-09-26T00:00:00.000Z",
1321
+ messages: [assistantMessage],
1322
+ events: [
1323
+ {
1324
+ id: "event-report",
1325
+ threadId,
1326
+ runId: "run-report",
1327
+ sequence: 1,
1328
+ occurredAt: "2026-09-26T00:00:00.000Z",
1329
+ type: "message.completed",
1330
+ message: assistantMessage,
1331
+ },
1332
+ ],
1333
+ };
1334
+ },
1335
+ };
1336
+ const client = new AgentKitClient({ transport });
1337
+ await client.loadThread("thread-report");
1338
+ const buildFeedbackReport = vi.fn(
1339
+ ({ runId, note }: { runId?: string; note: string }) =>
1340
+ `report run=${runId} note=${note}`,
1341
+ );
1342
+ const writeText = vi.fn(async () => undefined);
1343
+ const clipboardDescriptor = Object.getOwnPropertyDescriptor(
1344
+ navigator,
1345
+ "clipboard",
1346
+ );
1347
+ Object.defineProperty(navigator, "clipboard", {
1348
+ configurable: true,
1349
+ value: { writeText },
1350
+ });
1351
+ const container = document.createElement("div");
1352
+ document.body.appendChild(container);
1353
+ const root = createRoot(container);
1354
+ const actEnvironment = globalThis as typeof globalThis & {
1355
+ IS_REACT_ACT_ENVIRONMENT?: boolean;
1356
+ };
1357
+ const previousActEnvironment = actEnvironment.IS_REACT_ACT_ENVIRONMENT;
1358
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = true;
1359
+ const buttonByText = (text: string) =>
1360
+ Array.from(
1361
+ document.body.querySelectorAll<HTMLButtonElement>("button"),
1362
+ ).find((button) => button.textContent?.includes(text));
1363
+
1364
+ try {
1365
+ await act(async () => {
1366
+ root.render(
1367
+ <AgentKitProvider
1368
+ controller={client}
1369
+ threadId="thread-report"
1370
+ buildFeedbackReport={buildFeedbackReport}
1371
+ >
1372
+ <AgentKitChat composer={false} />
1373
+ </AgentKitProvider>,
1374
+ );
1375
+ await Promise.resolve();
1376
+ });
1377
+ await act(async () => {
1378
+ container
1379
+ .querySelector<HTMLButtonElement>('button[aria-label="Not helpful"]')
1380
+ ?.click();
1381
+ await Promise.resolve();
1382
+ });
1383
+
1384
+ // They sit inside the feedback form, so none may submit it.
1385
+ expect(buttonByText("Misread my ask")?.type).toBe("button");
1386
+ expect(buttonByText("Copy details")?.type).toBe("button");
1387
+ await act(async () => {
1388
+ buttonByText("Misread my ask")?.click();
1389
+ buttonByText("Wrong numbers")?.click();
1390
+ buttonByText("Misread my ask")?.click();
1391
+ });
1392
+ const textarea = document.body.querySelector<HTMLTextAreaElement>(
1393
+ ".agentkit-feedback-popover textarea",
1394
+ );
1395
+ expect(textarea?.value).toBe("Misread my ask. Wrong numbers");
1396
+
1397
+ await act(async () => {
1398
+ buttonByText("Copy details")?.click();
1399
+ await new Promise((resolve) => setTimeout(resolve, 0));
1400
+ });
1401
+ expect(buildFeedbackReport).toHaveBeenCalledWith({
1402
+ threadId: "thread-report",
1403
+ runId: "run-report",
1404
+ messageId: "assistant-report",
1405
+ note: "Misread my ask. Wrong numbers",
1406
+ });
1407
+ expect(writeText).toHaveBeenCalledWith(
1408
+ "report run=run-report note=Misread my ask. Wrong numbers",
1409
+ );
1410
+ expect(buttonByText("Copied")).toBeDefined();
1411
+ } finally {
1412
+ await act(async () => {
1413
+ root.unmount();
1414
+ await Promise.resolve();
1415
+ });
1416
+ await client.shutdown();
1417
+ container.remove();
1418
+ if (clipboardDescriptor) {
1419
+ Object.defineProperty(navigator, "clipboard", clipboardDescriptor);
1420
+ } else {
1421
+ Reflect.deleteProperty(navigator, "clipboard");
1422
+ }
1423
+ if (previousActEnvironment === undefined) {
1424
+ delete actEnvironment.IS_REACT_ACT_ENVIRONMENT;
1425
+ } else {
1426
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = previousActEnvironment;
1427
+ }
1428
+ }
1429
+ });
1430
+
1431
+ it("offers no Copy details without a report builder", async () => {
1432
+ const assistantMessage = {
1433
+ id: "assistant-no-report",
1434
+ role: "assistant" as const,
1435
+ status: "complete" as const,
1436
+ createdAt: "2026-09-26T00:00:00.000Z",
1437
+ parts: [{ type: "text" as const, text: "Answer" }],
1438
+ };
1439
+ const client = new AgentKitClient({
1440
+ transport: {
1441
+ capabilities: { feedback: true },
1442
+ async submitFeedback() {},
1443
+ async startRun() {
1444
+ return { runId: "run-no-report" };
1445
+ },
1446
+ async *subscribeToRun() {},
1447
+ async cancelRun() {},
1448
+ async getThreadSnapshot(threadId) {
1449
+ return {
1450
+ id: threadId,
1451
+ createdAt: "2026-09-26T00:00:00.000Z",
1452
+ updatedAt: "2026-09-26T00:00:00.000Z",
1453
+ messages: [assistantMessage],
1454
+ };
1455
+ },
1456
+ },
1457
+ });
1458
+ await client.loadThread("thread-no-report");
1459
+ const container = document.createElement("div");
1460
+ document.body.appendChild(container);
1461
+ const root = createRoot(container);
1462
+ const actEnvironment = globalThis as typeof globalThis & {
1463
+ IS_REACT_ACT_ENVIRONMENT?: boolean;
1464
+ };
1465
+ const previousActEnvironment = actEnvironment.IS_REACT_ACT_ENVIRONMENT;
1466
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = true;
1467
+
1468
+ try {
1469
+ await act(async () => {
1470
+ root.render(
1471
+ <AgentKitProvider controller={client} threadId="thread-no-report">
1472
+ <AgentKitChat composer={false} />
1473
+ </AgentKitProvider>,
1474
+ );
1475
+ await Promise.resolve();
1476
+ });
1477
+ await act(async () => {
1478
+ container
1479
+ .querySelector<HTMLButtonElement>('button[aria-label="Not helpful"]')
1480
+ ?.click();
1481
+ await Promise.resolve();
1482
+ });
1483
+
1484
+ expect(
1485
+ document.body.querySelector(".agentkit-feedback-popover"),
1486
+ ).not.toBeNull();
1487
+ expect(document.body.textContent).not.toContain("Copy details");
1488
+ } finally {
1489
+ await act(async () => {
1490
+ root.unmount();
1491
+ await Promise.resolve();
1492
+ });
1493
+ await client.shutdown();
1494
+ container.remove();
1495
+ if (previousActEnvironment === undefined) {
1496
+ delete actEnvironment.IS_REACT_ACT_ENVIRONMENT;
1497
+ } else {
1498
+ actEnvironment.IS_REACT_ACT_ENVIRONMENT = previousActEnvironment;
1499
+ }
1500
+ }
1501
+ });
1502
+
1193
1503
  it("falls back to plain-text clipboard copy when the host declines handling", async () => {
1194
1504
  const message = {
1195
1505
  id: "assistant-copy-fallback",
@@ -971,7 +971,7 @@ describe("AgentKitChat", () => {
971
971
  },
972
972
  {
973
973
  type: "text",
974
- text: "Also tell @Steve.",
974
+ text: "Also tell @Steve about any @builder.io booking.",
975
975
  },
976
976
  {
977
977
  type: "file",
@@ -1050,7 +1050,12 @@ describe("AgentKitChat", () => {
1050
1050
 
1051
1051
  expect(htmlWithComposer).toContain('aria-label="Edit message"');
1052
1052
  expect(htmlWithComposer).toContain('data-mention-label="latest run"');
1053
- expect(htmlWithComposer).toContain('data-mention-label="Steve"');
1053
+ // Plain "@word" text is not a reference, so it stays literal text.
1054
+ expect(htmlWithComposer).not.toContain('data-mention-label="Steve"');
1055
+ expect(htmlWithComposer).not.toContain('data-mention-label="builder"');
1056
+ expect(htmlWithComposer).toContain(
1057
+ "Also tell @Steve about any @builder.io booking.",
1058
+ );
1054
1059
  expect(htmlWithComposer).toContain('aria-label="Preview latest-run.png"');
1055
1060
  expect(htmlWithComposer).toContain("Pasted text");
1056
1061
  expect(htmlWithComposer).toContain("agentkit-file--pasted-text");
@@ -1801,4 +1806,103 @@ describe("AgentKitChat", () => {
1801
1806
  { error: failure, runId: "run-failure", threadId: "thread-1" },
1802
1807
  ]);
1803
1808
  });
1809
+
1810
+ it.each([
1811
+ { label: "with no work", work: false },
1812
+ { label: "after its last work", work: true },
1813
+ ])(
1814
+ "keeps a failed run's card in its own turn, above a newer turn ($label)",
1815
+ async ({ work }) => {
1816
+ const transport: AgentTransport = {
1817
+ async startRun(input) {
1818
+ return {
1819
+ runId: input.messages.length === 1 ? "run-failed" : "run-next",
1820
+ };
1821
+ },
1822
+ async *subscribeToRun({ runId }) {
1823
+ const base = {
1824
+ threadId: "thread-1",
1825
+ runId,
1826
+ occurredAt:
1827
+ runId === "run-failed"
1828
+ ? "2026-10-05T17:01:00.000Z"
1829
+ : "2026-10-05T17:11:00.000Z",
1830
+ };
1831
+ yield { ...base, id: `${runId}-1`, sequence: 1, type: "run.started" };
1832
+ if (runId === "run-failed") {
1833
+ let sequence = 1;
1834
+ if (work) {
1835
+ yield {
1836
+ ...base,
1837
+ id: `${runId}-work`,
1838
+ sequence: ++sequence,
1839
+ type: "activity.started",
1840
+ activity: {
1841
+ id: "activity-1",
1842
+ kind: "tool",
1843
+ label: "Charging the card",
1844
+ status: "running",
1845
+ },
1846
+ };
1847
+ }
1848
+ yield {
1849
+ ...base,
1850
+ id: `${runId}-failed`,
1851
+ sequence: ++sequence,
1852
+ type: "run.failed",
1853
+ error: {
1854
+ code: "stale_run",
1855
+ message: "The agent stopped before it could finish.",
1856
+ retryable: true,
1857
+ },
1858
+ };
1859
+ return;
1860
+ }
1861
+ yield {
1862
+ ...base,
1863
+ id: `${runId}-reply`,
1864
+ sequence: 2,
1865
+ type: "message.completed",
1866
+ message: {
1867
+ id: "assistant-next",
1868
+ role: "assistant",
1869
+ parts: [{ type: "text", text: "Newer answer" }],
1870
+ },
1871
+ };
1872
+ yield {
1873
+ ...base,
1874
+ id: `${runId}-done`,
1875
+ sequence: 3,
1876
+ type: "run.completed",
1877
+ };
1878
+ },
1879
+ async cancelRun() {},
1880
+ };
1881
+ let clock = "2026-10-05T17:00:00.000Z";
1882
+ const client = new AgentKitClient({ transport, now: () => clock });
1883
+ await (
1884
+ await client.sendMessage({ threadId: "thread-1", text: "First turn" })
1885
+ ).completed;
1886
+ clock = "2026-10-05T17:10:00.000Z";
1887
+ await (
1888
+ await client.sendMessage({ threadId: "thread-1", text: "Next turn" })
1889
+ ).completed;
1890
+
1891
+ const html = renderToStaticMarkup(
1892
+ <AgentKitProvider controller={client} threadId="thread-1">
1893
+ <AgentKitChat composer={false} />
1894
+ </AgentKitProvider>,
1895
+ );
1896
+
1897
+ const order = [
1898
+ "First turn",
1899
+ ...(work ? ["Charging the card"] : []),
1900
+ 'data-run-id="run-failed"',
1901
+ "Next turn",
1902
+ "Newer answer",
1903
+ ].map((text) => html.indexOf(text));
1904
+ expect(order.every((index) => index >= 0)).toBe(true);
1905
+ expect(order).toEqual([...order].sort((left, right) => left - right));
1906
+ },
1907
+ );
1804
1908
  });