@agent-native/toolkit 0.198.4 → 0.198.6

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 (96) hide show
  1. package/dist/app/agentkit/react/components.d.ts +6 -3
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +98 -68
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/styles.css +36 -24
  6. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  7. package/dist/app/chat/AgentKitAssistantChat.js +32 -15
  8. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  9. package/dist/app/chat/AgentPanel.d.ts +1 -1
  10. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  11. package/dist/app/chat/AgentPanel.js +11 -38
  12. package/dist/app/chat/AgentPanel.js.map +1 -1
  13. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  14. package/dist/app/chat/AgentSidebar.js +3 -11
  15. package/dist/app/chat/AgentSidebar.js.map +1 -1
  16. package/dist/app/chat/chat/tool-call-display.d.ts +2 -1
  17. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  18. package/dist/app/chat/chat/tool-call-display.js +83 -55
  19. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  20. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  21. package/dist/app/i18n/catalogs/ar-SA.js +1 -0
  22. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  23. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  24. package/dist/app/i18n/catalogs/de-DE.js +1 -0
  25. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  26. package/dist/app/i18n/catalogs/en-US.d.ts +1 -0
  27. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  28. package/dist/app/i18n/catalogs/en-US.js +1 -0
  29. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  30. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  31. package/dist/app/i18n/catalogs/es-ES.js +1 -0
  32. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  33. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  34. package/dist/app/i18n/catalogs/fr-FR.js +1 -0
  35. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  36. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  37. package/dist/app/i18n/catalogs/hi-IN.js +1 -0
  38. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  39. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/ja-JP.js +1 -0
  41. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  42. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/ko-KR.js +1 -0
  44. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  45. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/pt-BR.js +1 -0
  47. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  48. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/zh-CN.js +1 -0
  50. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  51. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/zh-TW.js +1 -0
  53. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  54. package/dist/composer/PromptComposer.d.ts +3 -0
  55. package/dist/composer/PromptComposer.d.ts.map +1 -1
  56. package/dist/composer/PromptComposer.js +2 -2
  57. package/dist/composer/PromptComposer.js.map +1 -1
  58. package/dist/composer/TiptapComposer.d.ts +4 -1
  59. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  60. package/dist/composer/TiptapComposer.js +32 -25
  61. package/dist/composer/TiptapComposer.js.map +1 -1
  62. package/dist/ui/sonner.js +1 -1
  63. package/dist/ui/sonner.js.map +1 -1
  64. package/package.json +3 -3
  65. package/src/app/agentkit/react/components.interaction.spec.tsx +93 -0
  66. package/src/app/agentkit/react/components.spec.tsx +1 -2
  67. package/src/app/agentkit/react/components.tsx +185 -132
  68. package/src/app/agentkit/react/composer-context.spec.tsx +7 -0
  69. package/src/app/agentkit/react/lifecycle.spec.tsx +556 -39
  70. package/src/app/agentkit/react/styles.css +36 -24
  71. package/src/app/chat/AgentKitAssistantChat.spec.tsx +60 -1
  72. package/src/app/chat/AgentKitAssistantChat.tsx +45 -14
  73. package/src/app/chat/AgentPanel.overlay.spec.tsx +28 -4
  74. package/src/app/chat/AgentPanel.tsx +51 -168
  75. package/src/app/chat/AgentSidebar.spec.tsx +19 -19
  76. package/src/app/chat/AgentSidebar.tsx +59 -94
  77. package/src/app/chat/chat/tool-call-display.spec.tsx +371 -15
  78. package/src/app/chat/chat/tool-call-display.tsx +154 -80
  79. package/src/app/i18n/catalogs/ar-SA.ts +1 -0
  80. package/src/app/i18n/catalogs/de-DE.ts +1 -0
  81. package/src/app/i18n/catalogs/en-US.ts +1 -0
  82. package/src/app/i18n/catalogs/es-ES.ts +1 -0
  83. package/src/app/i18n/catalogs/fr-FR.ts +1 -0
  84. package/src/app/i18n/catalogs/hi-IN.ts +1 -0
  85. package/src/app/i18n/catalogs/ja-JP.ts +1 -0
  86. package/src/app/i18n/catalogs/ko-KR.ts +1 -0
  87. package/src/app/i18n/catalogs/pt-BR.ts +1 -0
  88. package/src/app/i18n/catalogs/zh-CN.ts +1 -0
  89. package/src/app/i18n/catalogs/zh-TW.ts +1 -0
  90. package/src/app-shell/sidebar-overflow.spec.tsx +2 -1
  91. package/src/composer/PromptComposer.context.spec.tsx +28 -15
  92. package/src/composer/PromptComposer.tsx +7 -0
  93. package/src/composer/TiptapComposer.spec.ts +40 -6
  94. package/src/composer/TiptapComposer.tsx +89 -89
  95. package/src/ui/sonner.spec.ts +15 -0
  96. package/src/ui/sonner.tsx +1 -1
@@ -718,7 +718,7 @@ describe("AgentChat lifecycle", () => {
718
718
  type: "activity.started" as const,
719
719
  activity: {
720
720
  id: "activity-1",
721
- kind: "tool",
721
+ kind: "model",
722
722
  label: "Contacting model",
723
723
  status: "running" as const,
724
724
  },
@@ -760,7 +760,12 @@ describe("AgentChat lifecycle", () => {
760
760
  expect(
761
761
  runWorkBefore?.querySelector("[data-agentkit-current-activity]")
762
762
  ?.textContent,
763
- ).toBe("Contacting model");
763
+ ).toBe("Thinking");
764
+ expect(
765
+ runWorkBefore?.querySelector(
766
+ "[data-agentkit-current-activity] .agent-running-shimmer",
767
+ ),
768
+ ).not.toBeNull();
764
769
 
765
770
  const assistantMessage = {
766
771
  id: "assistant-1",
@@ -863,12 +868,19 @@ describe("AgentChat lifecycle", () => {
863
868
  const response = tree.container.querySelector(
864
869
  '[data-message-id="assistant-1"]',
865
870
  );
866
- expect(runWorkAfter).toBe(runWorkBefore);
867
- expect(runWorkAfter?.open).toBe(false);
868
871
  expect(
869
- runWorkAfter?.querySelector(".agentkit-activities-summary")?.textContent,
872
+ response
873
+ ?.querySelector(".agentkit-message-actions")
874
+ ?.getAttribute("data-streaming"),
875
+ ).toBe("true");
876
+ expect(runWorkAfter).not.toBeNull();
877
+ expect(
878
+ runWorkAfter?.querySelector(".agentkit-activities-label")?.textContent,
870
879
  ).toBe("Worked for 4s");
871
- expect(runWorkAfter?.hasAttribute("data-running")).toBe(false);
880
+ expect(runWorkAfter?.textContent).not.toContain("Thinking");
881
+ expect(
882
+ runWorkAfter?.querySelector("[data-agentkit-current-activity]"),
883
+ ).toBeNull();
872
884
  expect(runWorkAfter?.querySelector('[data-status="running"]')).toBeNull();
873
885
  expect(
874
886
  runWorkAfter && response
@@ -921,19 +933,20 @@ describe("AgentChat lifecycle", () => {
921
933
  });
922
934
  await flush();
923
935
  expect(
924
- runWorkAfter?.querySelector(".agentkit-activities-summary")?.textContent,
936
+ tree.container.querySelector(
937
+ ".agentkit-activities-summary .agentkit-activities-label",
938
+ )?.textContent,
925
939
  ).toBe("Worked for 4s");
926
-
927
- await act(async () => {
928
- runWorkAfter
929
- ?.querySelector("summary")
930
- ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
931
- await Promise.resolve();
932
- });
933
- expect(runWorkAfter?.open).toBe(true);
940
+ expect(
941
+ tree.container
942
+ .querySelector('[data-message-id="assistant-1"]')
943
+ ?.querySelector(".agentkit-message-actions")
944
+ ?.hasAttribute("data-streaming"),
945
+ ).toBe(false);
946
+ expect(tree.container.querySelector(".agentkit-reasoning")).toBeNull();
934
947
  });
935
948
 
936
- it("keeps thoughts inside one work disclosure through streaming and completion", async () => {
949
+ it("keeps thoughts in active history and omits them after completion", async () => {
937
950
  const threadId = "thread-reasoning-work";
938
951
  const runId = "run-reasoning-work";
939
952
  const base = (sequence: number, seconds: number = sequence) => ({
@@ -1019,10 +1032,10 @@ describe("AgentChat lifecycle", () => {
1019
1032
  work.querySelector("[data-agentkit-current-activity]")?.textContent,
1020
1033
  ).toBe("Searching documentation");
1021
1034
  expect(
1022
- work
1023
- .querySelector(".agentkit-activities-label")
1024
- ?.nextElementSibling?.classList.contains("agentkit-summary-chevron"),
1025
- ).toBe(true);
1035
+ work.querySelector(
1036
+ ".agentkit-activities-summary > .agentkit-summary-chevron",
1037
+ ),
1038
+ ).not.toBeNull();
1026
1039
  expect(
1027
1040
  work
1028
1041
  .querySelector('[data-thought="thread-reasoning-work:assistant-2:0"]')
@@ -1063,11 +1076,223 @@ describe("AgentChat lifecycle", () => {
1063
1076
  ).not.toBe(0);
1064
1077
  await act(async () => work.querySelector("summary")!.click());
1065
1078
  expect(work.open).toBe(true);
1066
- expect(work.querySelectorAll("[data-thought]")).toHaveLength(2);
1079
+ expect(work.querySelectorAll("[data-thought]")).toHaveLength(0);
1080
+ expect(work.querySelector('[data-activity-bucket="thinking"]')).toBeNull();
1067
1081
  await act(async () => work.querySelector("summary")!.click());
1068
1082
  expect(work.open).toBe(false);
1069
1083
  });
1070
1084
 
1085
+ it("shows localized Thinking before the first activity arrives", async () => {
1086
+ const threadId = "thread-no-activity-yet";
1087
+ const runId = "run-no-activity-yet";
1088
+ const thread = {
1089
+ ...createAgentThreadState(threadId),
1090
+ runs: {
1091
+ [runId]: {
1092
+ id: runId,
1093
+ status: "running" as const,
1094
+ lastSequence: 0,
1095
+ startedAt: "2026-09-28T00:00:00.000Z",
1096
+ },
1097
+ },
1098
+ activeRunIds: [runId],
1099
+ };
1100
+ const observable = observableController({
1101
+ connection: "connected",
1102
+ capabilities: {},
1103
+ capabilitiesStatus: "ready",
1104
+ threads: { [threadId]: thread },
1105
+ revision: 0,
1106
+ });
1107
+ const tree = mount();
1108
+
1109
+ await tree.render(
1110
+ <AgentKitProvider
1111
+ controller={observable.controller}
1112
+ threadId={threadId}
1113
+ labels={{ reasoning: "Thinking in Spanish" }}
1114
+ >
1115
+ <AgentActivityGroup runId={runId} />
1116
+ </AgentKitProvider>,
1117
+ );
1118
+
1119
+ expect(
1120
+ tree.container.querySelectorAll("[data-agentkit-current-activity]"),
1121
+ ).toHaveLength(1);
1122
+ expect(
1123
+ tree.container.querySelector(
1124
+ "[data-agentkit-current-activity] .agent-running-shimmer",
1125
+ )?.textContent,
1126
+ ).toBe("Thinking in Spanish");
1127
+ expect(tree.container.textContent).not.toContain("Working for");
1128
+ const work = tree.container.querySelector<HTMLDetailsElement>(
1129
+ ".agentkit-activities",
1130
+ );
1131
+ expect(work?.dataset.expandable).toBe("false");
1132
+ expect(
1133
+ work?.querySelector(".agentkit-activities-summary")?.textContent,
1134
+ ).toBe("Thinking in Spanish");
1135
+ await tree.unmount();
1136
+ });
1137
+
1138
+ it("falls back to Thinking when the newest activity has completed", async () => {
1139
+ const threadId = "thread-completed-latest-activity";
1140
+ const runId = "run-completed-latest-activity";
1141
+ const thread = {
1142
+ ...createAgentThreadState(threadId),
1143
+ events: [
1144
+ {
1145
+ id: "activity-started",
1146
+ threadId,
1147
+ runId,
1148
+ sequence: 1,
1149
+ occurredAt: "2026-09-28T00:00:01.000Z",
1150
+ type: "activity.started" as const,
1151
+ activity: {
1152
+ id: "read-components",
1153
+ kind: "read",
1154
+ label: "Reading components.tsx",
1155
+ status: "running" as const,
1156
+ },
1157
+ },
1158
+ {
1159
+ id: "activity-completed",
1160
+ threadId,
1161
+ runId,
1162
+ sequence: 2,
1163
+ occurredAt: "2026-09-28T00:00:02.000Z",
1164
+ type: "activity.completed" as const,
1165
+ activity: {
1166
+ id: "read-components",
1167
+ kind: "read",
1168
+ label: "Reading components.tsx",
1169
+ status: "completed" as const,
1170
+ completedAt: "2026-09-28T00:00:02.000Z",
1171
+ },
1172
+ },
1173
+ ],
1174
+ runs: {
1175
+ [runId]: {
1176
+ id: runId,
1177
+ status: "running" as const,
1178
+ lastSequence: 2,
1179
+ startedAt: "2026-09-28T00:00:00.000Z",
1180
+ },
1181
+ },
1182
+ activeRunIds: [runId],
1183
+ };
1184
+ const observable = observableController({
1185
+ connection: "connected",
1186
+ capabilities: {},
1187
+ capabilitiesStatus: "ready",
1188
+ threads: { [threadId]: thread },
1189
+ revision: 0,
1190
+ });
1191
+ const tree = mount();
1192
+
1193
+ await tree.render(
1194
+ <AgentKitProvider controller={observable.controller} threadId={threadId}>
1195
+ <AgentKitChat composer={false} />
1196
+ </AgentKitProvider>,
1197
+ );
1198
+
1199
+ const current = tree.container.querySelector(
1200
+ "[data-agentkit-current-activity]",
1201
+ );
1202
+ expect(current?.textContent).toBe("Thinking");
1203
+ expect(current?.querySelector(".agent-running-shimmer")).not.toBeNull();
1204
+ expect(
1205
+ tree.container.querySelector(".agentkit-activities-summary")?.textContent,
1206
+ ).not.toContain("Reading components.tsx");
1207
+ await tree.unmount();
1208
+ });
1209
+
1210
+ it("keeps bounded activity history settled while its run continues", async () => {
1211
+ const threadId = "thread-settled-segment";
1212
+ const runId = "run-settled-segment";
1213
+ const thread = {
1214
+ ...createAgentThreadState(threadId),
1215
+ events: [
1216
+ {
1217
+ id: "activity-started",
1218
+ threadId,
1219
+ runId,
1220
+ sequence: 1,
1221
+ occurredAt: "2026-09-30T00:00:00.000Z",
1222
+ type: "activity.started" as const,
1223
+ activity: {
1224
+ id: "read-files",
1225
+ kind: "read",
1226
+ label: "Read files",
1227
+ status: "running" as const,
1228
+ },
1229
+ },
1230
+ {
1231
+ id: "activity-completed",
1232
+ threadId,
1233
+ runId,
1234
+ sequence: 2,
1235
+ occurredAt: "2026-09-30T00:00:01.000Z",
1236
+ type: "activity.completed" as const,
1237
+ activity: {
1238
+ id: "read-files",
1239
+ kind: "read",
1240
+ label: "Read files",
1241
+ status: "completed" as const,
1242
+ },
1243
+ },
1244
+ {
1245
+ id: "response-started",
1246
+ threadId,
1247
+ runId,
1248
+ sequence: 3,
1249
+ occurredAt: "2026-09-30T00:00:01.000Z",
1250
+ type: "message.completed" as const,
1251
+ message: {
1252
+ id: "assistant-response",
1253
+ role: "assistant" as const,
1254
+ status: "complete" as const,
1255
+ parts: [{ type: "text" as const, text: "I found the files." }],
1256
+ },
1257
+ },
1258
+ ],
1259
+ runs: {
1260
+ [runId]: {
1261
+ id: runId,
1262
+ status: "running" as const,
1263
+ lastSequence: 3,
1264
+ startedAt: "2026-09-30T00:00:00.000Z",
1265
+ },
1266
+ },
1267
+ };
1268
+ const observable = observableController({
1269
+ connection: "connected",
1270
+ capabilities: {},
1271
+ capabilitiesStatus: "ready",
1272
+ threads: { [threadId]: thread },
1273
+ revision: 0,
1274
+ });
1275
+ const tree = mount();
1276
+
1277
+ await tree.render(
1278
+ <AgentKitProvider controller={observable.controller} threadId={threadId}>
1279
+ <AgentActivityGroup
1280
+ runId={runId}
1281
+ throughSequence={3}
1282
+ isCurrentSegment={false}
1283
+ />
1284
+ </AgentKitProvider>,
1285
+ );
1286
+
1287
+ const work = tree.container.querySelector<HTMLDetailsElement>(
1288
+ ".agentkit-activities",
1289
+ );
1290
+ expect(work?.querySelector("summary")?.textContent).toBe("Worked for 1s");
1291
+ expect(work?.hasAttribute("data-running")).toBe(false);
1292
+ expect(work?.querySelector("[data-agentkit-current-activity]")).toBeNull();
1293
+ await tree.unmount();
1294
+ });
1295
+
1071
1296
  it.each(["completed", "cancelled"] as const)(
1072
1297
  "keeps reasoning-only %s runs collapsed without empty message rows",
1073
1298
  async (status) => {
@@ -1119,13 +1344,10 @@ describe("AgentChat lifecycle", () => {
1119
1344
  );
1120
1345
  const work = tree.container.querySelector<HTMLDetailsElement>(
1121
1346
  ".agentkit-activities",
1122
- )!;
1123
- expect(work.open).toBe(false);
1124
- expect(work.querySelector("summary")?.textContent).toBe("Worked for 18s");
1125
- expect(work.querySelector(".agentkit-reasoning")).not.toBeNull();
1126
- expect(
1127
- work.querySelector("[data-active], [data-agentkit-current-activity]"),
1128
- ).toBeNull();
1347
+ );
1348
+ expect(work).toBeNull();
1349
+ expect(tree.container.textContent).toContain("Worked for 18s");
1350
+ expect(tree.container.querySelector(".agentkit-reasoning")).toBeNull();
1129
1351
  expect(tree.container.querySelector("article")).toBeNull();
1130
1352
  },
1131
1353
  );
@@ -1182,6 +1404,156 @@ describe("AgentChat lifecycle", () => {
1182
1404
  expect(tree.container.querySelector("article")).toBeNull();
1183
1405
  });
1184
1406
 
1407
+ it("keeps completed status and model history while hiding reasoning and expanding tool diagnostics", async () => {
1408
+ const threadId = "thread-completed-activity-history";
1409
+ const runId = "run-completed-activity-history";
1410
+ const diagnostic = {
1411
+ code: "slide_content_edit_failed",
1412
+ closestMatch: { slide: 1, text: "existing heading" },
1413
+ };
1414
+ const failedTool: AgentToolCall = {
1415
+ id: "tool-update-slide",
1416
+ name: "update-slide",
1417
+ status: "failed",
1418
+ output: diagnostic,
1419
+ };
1420
+ const thread = {
1421
+ ...createAgentThreadState(threadId),
1422
+ messages: [
1423
+ {
1424
+ id: "assistant-reasoning",
1425
+ role: "assistant" as const,
1426
+ status: "complete" as const,
1427
+ parts: [
1428
+ { type: "reasoning" as const, text: "Private reasoning text" },
1429
+ ],
1430
+ },
1431
+ ],
1432
+ tools: { [failedTool.id]: failedTool },
1433
+ events: [
1434
+ {
1435
+ id: "event-model",
1436
+ threadId,
1437
+ runId,
1438
+ sequence: 1,
1439
+ occurredAt: "2026-09-30T00:00:01.000Z",
1440
+ type: "activity.completed" as const,
1441
+ activity: {
1442
+ id: "activity-model",
1443
+ kind: "model",
1444
+ label: "Claude Sonnet 5",
1445
+ status: "completed" as const,
1446
+ },
1447
+ },
1448
+ {
1449
+ id: "event-status",
1450
+ threadId,
1451
+ runId,
1452
+ sequence: 2,
1453
+ occurredAt: "2026-09-30T00:00:02.000Z",
1454
+ type: "activity.completed" as const,
1455
+ activity: {
1456
+ id: "activity-status",
1457
+ kind: "status",
1458
+ label: "Updated the active slide",
1459
+ status: "completed" as const,
1460
+ },
1461
+ },
1462
+ {
1463
+ id: "event-reasoning",
1464
+ threadId,
1465
+ runId,
1466
+ sequence: 3,
1467
+ occurredAt: "2026-09-30T00:00:03.000Z",
1468
+ type: "reasoning.delta" as const,
1469
+ messageId: "assistant-reasoning",
1470
+ text: "Private reasoning text",
1471
+ },
1472
+ {
1473
+ id: "event-tool",
1474
+ threadId,
1475
+ runId,
1476
+ sequence: 4,
1477
+ occurredAt: "2026-09-30T00:00:04.000Z",
1478
+ type: "tool.updated" as const,
1479
+ toolCall: failedTool,
1480
+ },
1481
+ {
1482
+ id: "event-completed",
1483
+ threadId,
1484
+ runId,
1485
+ sequence: 5,
1486
+ occurredAt: "2026-09-30T00:00:05.000Z",
1487
+ type: "run.completed" as const,
1488
+ },
1489
+ ],
1490
+ runs: {
1491
+ [runId]: {
1492
+ id: runId,
1493
+ status: "completed" as const,
1494
+ lastSequence: 5,
1495
+ startedAt: "2026-09-30T00:00:00.000Z",
1496
+ completedAt: "2026-09-30T00:00:05.000Z",
1497
+ },
1498
+ },
1499
+ };
1500
+ const observable = observableController({
1501
+ connection: "connected",
1502
+ capabilities: {},
1503
+ capabilitiesStatus: "ready",
1504
+ threads: { [threadId]: thread },
1505
+ revision: 0,
1506
+ });
1507
+ const tree = mount();
1508
+
1509
+ await tree.render(
1510
+ <AgentKitProvider controller={observable.controller} threadId={threadId}>
1511
+ <AgentActivityGroup runId={runId} />
1512
+ </AgentKitProvider>,
1513
+ );
1514
+
1515
+ const work = tree.container.querySelector<HTMLDetailsElement>(
1516
+ ".agentkit-activities",
1517
+ );
1518
+ expect(
1519
+ work?.querySelector(".agentkit-activities-summary")?.textContent,
1520
+ ).toContain("Worked");
1521
+ await act(async () => {
1522
+ work
1523
+ ?.querySelector<HTMLElement>(".agentkit-activities-summary")
1524
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1525
+ });
1526
+
1527
+ expect(work?.querySelector('[data-activity-bucket="thinking"]')).toBeNull();
1528
+ const other = work?.querySelector('[data-activity-bucket="other"]');
1529
+ expect(other?.textContent).toContain("Claude Sonnet 5");
1530
+ expect(other?.textContent).toContain("Updated the active slide");
1531
+ expect(work?.textContent).not.toContain("Private reasoning text");
1532
+
1533
+ const failedToolRow = Array.from(
1534
+ work?.querySelectorAll<HTMLElement>(".agentkit-activity-item") ?? [],
1535
+ ).find(
1536
+ (row) =>
1537
+ row.querySelector(".agentkit-activity-label")?.textContent ===
1538
+ "update-slide",
1539
+ );
1540
+ expect(failedToolRow?.textContent).not.toContain(
1541
+ "slide_content_edit_failed",
1542
+ );
1543
+ await act(async () => {
1544
+ failedToolRow
1545
+ ?.querySelector<HTMLElement>(".agentkit-activity-disclosure")
1546
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
1547
+ });
1548
+ expect(
1549
+ failedToolRow?.querySelector(".agentkit-activity-summary")?.textContent,
1550
+ ).toContain("slide_content_edit_failed");
1551
+ expect(
1552
+ failedToolRow?.querySelector(".agentkit-activity-summary")?.textContent,
1553
+ ).toContain("existing heading");
1554
+ await tree.unmount();
1555
+ });
1556
+
1185
1557
  it("keeps active work compact and groups expanded history by purpose", async () => {
1186
1558
  vi.useFakeTimers();
1187
1559
  vi.setSystemTime(new Date("2026-08-31T00:00:15.000Z"));
@@ -1292,7 +1664,7 @@ describe("AgentChat lifecycle", () => {
1292
1664
  );
1293
1665
  expect(
1294
1666
  work?.querySelector(".agentkit-activities-summary")?.textContent,
1295
- ).toContain("Working for 15s");
1667
+ ).toBe("Reviewing results");
1296
1668
  expect(work?.open).toBe(false);
1297
1669
  expect(
1298
1670
  work?.querySelector("[data-agentkit-current-activity]")?.textContent,
@@ -1303,7 +1675,7 @@ describe("AgentChat lifecycle", () => {
1303
1675
  work?.querySelectorAll("[data-activity-bucket]") ?? [],
1304
1676
  (bucket) => bucket.getAttribute("data-activity-bucket"),
1305
1677
  ),
1306
- ).toEqual(["thinking", "research"]);
1678
+ ).toEqual(["research", "other"]);
1307
1679
  expect(cluster?.open).toBe(false);
1308
1680
  expect(cluster?.querySelector("summary")?.textContent).toContain(
1309
1681
  "Docs search×3",
@@ -1330,7 +1702,7 @@ describe("AgentChat lifecycle", () => {
1330
1702
  ).toHaveLength(3);
1331
1703
  expect(
1332
1704
  work?.querySelectorAll(
1333
- '[data-activity-bucket="thinking"] [data-status="running"]',
1705
+ '[data-activity-bucket="other"] [data-status="running"]',
1334
1706
  ),
1335
1707
  ).toHaveLength(1);
1336
1708
 
@@ -1339,12 +1711,96 @@ describe("AgentChat lifecycle", () => {
1339
1711
  });
1340
1712
  expect(
1341
1713
  work?.querySelector(".agentkit-activities-summary")?.textContent,
1342
- ).toContain("Working for 16s");
1714
+ ).toBe("Reviewing results");
1343
1715
  } finally {
1344
1716
  vi.useRealTimers();
1345
1717
  }
1346
1718
  });
1347
1719
 
1720
+ it("keeps the live status row mounted as activity details arrive", async () => {
1721
+ const threadId = "thread-live-status-row";
1722
+ const runId = "run-live-status-row";
1723
+ const thread = {
1724
+ ...createAgentThreadState(threadId),
1725
+ runs: {
1726
+ [runId]: {
1727
+ id: runId,
1728
+ status: "running" as const,
1729
+ lastSequence: 0,
1730
+ startedAt: "2026-09-30T00:00:00.000Z",
1731
+ },
1732
+ },
1733
+ activeRunIds: [runId],
1734
+ };
1735
+ const observable = observableController({
1736
+ connection: "connected",
1737
+ capabilities: {},
1738
+ capabilitiesStatus: "ready",
1739
+ threads: { [threadId]: thread },
1740
+ revision: 0,
1741
+ });
1742
+ const tree = mount();
1743
+
1744
+ await tree.render(
1745
+ <AgentKitProvider controller={observable.controller} threadId={threadId}>
1746
+ <AgentActivityGroup runId={runId} />
1747
+ </AgentKitProvider>,
1748
+ );
1749
+
1750
+ const work = tree.container.querySelector<HTMLDetailsElement>(
1751
+ ".agentkit-activities",
1752
+ );
1753
+ expect(work?.dataset.expandable).toBe("false");
1754
+ expect(
1755
+ work?.querySelector("[data-agentkit-current-activity]")?.textContent,
1756
+ ).toBe("Thinking");
1757
+
1758
+ observable.update({
1759
+ connection: "connected",
1760
+ capabilities: {},
1761
+ capabilitiesStatus: "ready",
1762
+ threads: {
1763
+ [threadId]: {
1764
+ ...thread,
1765
+ events: [
1766
+ {
1767
+ id: "event-reading-file",
1768
+ threadId,
1769
+ runId,
1770
+ sequence: 1,
1771
+ occurredAt: "2026-09-30T00:00:01.000Z",
1772
+ type: "activity.started",
1773
+ activity: {
1774
+ id: "activity-reading-file",
1775
+ kind: "read",
1776
+ label: "Reading a file",
1777
+ status: "running",
1778
+ },
1779
+ },
1780
+ ],
1781
+ runs: {
1782
+ [runId]: {
1783
+ ...thread.runs[runId],
1784
+ lastSequence: 1,
1785
+ },
1786
+ },
1787
+ },
1788
+ },
1789
+ revision: 1,
1790
+ });
1791
+ await flush();
1792
+
1793
+ expect(tree.container.querySelector(".agentkit-activities")).toBe(work);
1794
+ expect(work?.dataset.expandable).toBe("true");
1795
+ expect(
1796
+ work?.querySelector(".agentkit-activities-summary")?.textContent,
1797
+ ).toBe("Reading a file");
1798
+ expect(
1799
+ work?.querySelectorAll("[data-agentkit-current-activity]"),
1800
+ ).toHaveLength(1);
1801
+ await tree.unmount();
1802
+ });
1803
+
1348
1804
  it("shares integration badges between current work and history without mixing providers", async () => {
1349
1805
  const threadId = "thread-tool-sources";
1350
1806
  const runId = "run-tool-sources";
@@ -1514,7 +1970,7 @@ describe("AgentChat lifecycle", () => {
1514
1970
  await tree.unmount();
1515
1971
  });
1516
1972
 
1517
- it("keeps failed tools visible while successful work stays collapsed", async () => {
1973
+ it("keeps failed tool errors inside explicitly expanded history", async () => {
1518
1974
  const threadId = "thread-tool-failure";
1519
1975
  const runId = "run-tool-failure";
1520
1976
  const tools: AgentToolCall[] = [
@@ -1562,13 +2018,24 @@ describe("AgentChat lifecycle", () => {
1562
2018
  <AgentKitChat composer={false} />
1563
2019
  </AgentKitProvider>,
1564
2020
  );
1565
- const failure = tree.container.querySelector('[role="alert"]');
1566
- expect(failure?.textContent).toContain("The file could not be saved.");
1567
- expect(failure?.closest("details")).toBeNull();
2021
+ const work = tree.container.querySelector<HTMLDetailsElement>(
2022
+ ".agentkit-activities",
2023
+ );
2024
+ expect(tree.container.querySelector(".agentkit-run-failure")).toBeNull();
2025
+ expect(work?.open).toBe(false);
2026
+ expect(work?.querySelector(".agentkit-activity-summary")).toBeNull();
2027
+ await act(async () => work?.querySelector("summary")?.click());
2028
+ expect(work?.open).toBe(true);
2029
+ const failedTool = work?.querySelector(
2030
+ '.agentkit-activity-item[data-status="failed"]',
2031
+ );
2032
+ expect(failedTool?.textContent).not.toContain(
2033
+ "The file could not be saved.",
2034
+ );
2035
+ await act(async () => failedTool?.querySelector("button")?.click());
1568
2036
  expect(
1569
- tree.container.querySelector<HTMLDetailsElement>(".agentkit-activities")
1570
- ?.open,
1571
- ).toBe(false);
2037
+ failedTool?.querySelector(".agentkit-activity-summary")?.textContent,
2038
+ ).toBe("The file could not be saved.");
1572
2039
  });
1573
2040
 
1574
2041
  it("resets disclosure state when switching threads with reused run ids", async () => {
@@ -1755,6 +2222,14 @@ describe("AgentChat lifecycle", () => {
1755
2222
  ...createAgentThreadState(threadId),
1756
2223
  messages: [firstResponse, secondResponse],
1757
2224
  events,
2225
+ runs: {
2226
+ [runId]: {
2227
+ id: runId,
2228
+ status: "running" as const,
2229
+ lastSequence: 6,
2230
+ startedAt: "2026-08-31T00:00:00.000Z",
2231
+ },
2232
+ },
1758
2233
  };
1759
2234
  const observable = observableController({
1760
2235
  connection: "connected",
@@ -1794,6 +2269,7 @@ describe("AgentChat lifecycle", () => {
1794
2269
  expect(work[1]?.textContent).toContain("Edited transcript model");
1795
2270
  expect(work[1]?.textContent).not.toContain("Read framework files");
1796
2271
  expect(work[0]?.hasAttribute("data-running")).toBe(false);
2272
+ expect(work[1]?.hasAttribute("data-running")).toBe(false);
1797
2273
  expect(
1798
2274
  work[0] && firstMessage
1799
2275
  ? work[0].compareDocumentPosition(firstMessage) &
@@ -2781,6 +3257,47 @@ describe("AgentKit subscriptions and recovery", () => {
2781
3257
  }
2782
3258
  });
2783
3259
 
3260
+ it("omits the message menu when no actions are available", async () => {
3261
+ const thread = createAgentThreadState("thread-no-request-id");
3262
+ thread.messages = [
3263
+ {
3264
+ id: "assistant-no-request-id",
3265
+ role: "assistant",
3266
+ parts: [{ type: "text", text: "Ready." }],
3267
+ },
3268
+ ];
3269
+ const store = observableController({
3270
+ connection: "connected",
3271
+ capabilities: {},
3272
+ capabilitiesStatus: "ready",
3273
+ threads: { [thread.id]: thread },
3274
+ revision: 0,
3275
+ });
3276
+ const tree = mount();
3277
+
3278
+ try {
3279
+ await tree.render(
3280
+ <AgentKitProvider
3281
+ controller={store.controller}
3282
+ threadId="thread-no-request-id"
3283
+ >
3284
+ <AgentMessageActions
3285
+ threadId="thread-no-request-id"
3286
+ value={thread.messages[0]!}
3287
+ />
3288
+ </AgentKitProvider>,
3289
+ );
3290
+ expect(
3291
+ tree.container.querySelector('button[aria-label="Message actions"]'),
3292
+ ).toBeNull();
3293
+ expect(
3294
+ document.body.querySelector('.agentkit-message-menu[role="menu"]'),
3295
+ ).toBeNull();
3296
+ } finally {
3297
+ await tree.unmount();
3298
+ }
3299
+ });
3300
+
2784
3301
  it("uses selector equality without caching a changed selector", async () => {
2785
3302
  const initial: AgentKitSnapshot = {
2786
3303
  connection: "idle",