@agent-native/toolkit 0.198.5 → 0.198.7-nightly-20260930213124

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 (87) hide show
  1. package/dist/app/agentkit/react/components.d.ts +2 -1
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +86 -62
  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.js +14 -7
  7. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  8. package/dist/app/chat/AgentPanel.d.ts +1 -1
  9. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  10. package/dist/app/chat/AgentPanel.js +11 -38
  11. package/dist/app/chat/AgentPanel.js.map +1 -1
  12. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  13. package/dist/app/chat/AgentSidebar.js +3 -11
  14. package/dist/app/chat/AgentSidebar.js.map +1 -1
  15. package/dist/app/chat/chat/tool-call-display.d.ts +2 -1
  16. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  17. package/dist/app/chat/chat/tool-call-display.js +83 -55
  18. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  19. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  20. package/dist/app/i18n/catalogs/ar-SA.js +1 -0
  21. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  22. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  23. package/dist/app/i18n/catalogs/de-DE.js +1 -0
  24. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  25. package/dist/app/i18n/catalogs/en-US.d.ts +1 -0
  26. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  27. package/dist/app/i18n/catalogs/en-US.js +1 -0
  28. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  29. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  30. package/dist/app/i18n/catalogs/es-ES.js +1 -0
  31. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  32. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/fr-FR.js +1 -0
  34. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  35. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/hi-IN.js +1 -0
  37. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  38. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  39. package/dist/app/i18n/catalogs/ja-JP.js +1 -0
  40. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  41. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  42. package/dist/app/i18n/catalogs/ko-KR.js +1 -0
  43. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  44. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  45. package/dist/app/i18n/catalogs/pt-BR.js +1 -0
  46. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  47. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  48. package/dist/app/i18n/catalogs/zh-CN.js +1 -0
  49. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  50. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  51. package/dist/app/i18n/catalogs/zh-TW.js +1 -0
  52. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  53. package/dist/composer/TiptapComposer.d.ts +1 -1
  54. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  55. package/dist/composer/TiptapComposer.js +26 -23
  56. package/dist/composer/TiptapComposer.js.map +1 -1
  57. package/dist/ui/sonner.js +1 -1
  58. package/dist/ui/sonner.js.map +1 -1
  59. package/package.json +4 -4
  60. package/src/app/agentkit/react/components.tsx +156 -128
  61. package/src/app/agentkit/react/lifecycle.spec.tsx +556 -39
  62. package/src/app/agentkit/react/styles.css +36 -24
  63. package/src/app/chat/AgentKitAssistantChat.spec.tsx +22 -0
  64. package/src/app/chat/AgentKitAssistantChat.tsx +19 -9
  65. package/src/app/chat/AgentPanel.overlay.spec.tsx +28 -4
  66. package/src/app/chat/AgentPanel.tsx +51 -168
  67. package/src/app/chat/AgentSidebar.spec.tsx +19 -19
  68. package/src/app/chat/AgentSidebar.tsx +59 -94
  69. package/src/app/chat/chat/tool-call-display.spec.tsx +371 -15
  70. package/src/app/chat/chat/tool-call-display.tsx +154 -80
  71. package/src/app/i18n/catalogs/ar-SA.ts +1 -0
  72. package/src/app/i18n/catalogs/de-DE.ts +1 -0
  73. package/src/app/i18n/catalogs/en-US.ts +1 -0
  74. package/src/app/i18n/catalogs/es-ES.ts +1 -0
  75. package/src/app/i18n/catalogs/fr-FR.ts +1 -0
  76. package/src/app/i18n/catalogs/hi-IN.ts +1 -0
  77. package/src/app/i18n/catalogs/ja-JP.ts +1 -0
  78. package/src/app/i18n/catalogs/ko-KR.ts +1 -0
  79. package/src/app/i18n/catalogs/pt-BR.ts +1 -0
  80. package/src/app/i18n/catalogs/zh-CN.ts +1 -0
  81. package/src/app/i18n/catalogs/zh-TW.ts +1 -0
  82. package/src/app-shell/sidebar-overflow.spec.tsx +2 -1
  83. package/src/composer/PromptComposer.context.spec.tsx +28 -15
  84. package/src/composer/TiptapComposer.spec.ts +14 -2
  85. package/src/composer/TiptapComposer.tsx +77 -86
  86. package/src/ui/sonner.spec.ts +15 -0
  87. package/src/ui/sonner.tsx +1 -1
@@ -984,6 +984,15 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
984
984
  opacity: 1;
985
985
  }
986
986
 
987
+ .agentkit-message .agentkit-message-actions[data-streaming="true"],
988
+ .agentkit-message:hover .agentkit-message-actions[data-streaming="true"],
989
+ .agentkit-message:focus-within
990
+ .agentkit-message-actions[data-streaming="true"] {
991
+ opacity: 0;
992
+ visibility: hidden;
993
+ pointer-events: none;
994
+ }
995
+
987
996
  @media (hover: none), (any-pointer: coarse) {
988
997
  .agentkit-message-actions[data-role="user"] {
989
998
  opacity: 1;
@@ -1133,11 +1142,11 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1133
1142
  }
1134
1143
 
1135
1144
  .agentkit-activities-summary {
1136
- display: grid;
1145
+ display: flex;
1137
1146
  inline-size: fit-content;
1138
1147
  max-inline-size: 100%;
1139
- justify-items: start;
1140
- gap: 0.125rem;
1148
+ align-items: center;
1149
+ gap: 0.375rem;
1141
1150
  }
1142
1151
 
1143
1152
  .agentkit-activities {
@@ -1263,6 +1272,12 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1263
1272
  gap: 0.375rem;
1264
1273
  }
1265
1274
 
1275
+ .agentkit-activities-summary-content {
1276
+ display: grid;
1277
+ min-inline-size: 0;
1278
+ gap: 0.125rem;
1279
+ }
1280
+
1266
1281
  .agentkit-activities-current {
1267
1282
  display: flex;
1268
1283
  align-items: center;
@@ -1280,20 +1295,6 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1280
1295
  white-space: nowrap;
1281
1296
  }
1282
1297
 
1283
- .agentkit-activities-current[data-running="true"]
1284
- .agentkit-activities-current-label {
1285
- color: transparent;
1286
- background: linear-gradient(
1287
- 90deg,
1288
- var(--agentkit-text),
1289
- color-mix(in srgb, var(--agentkit-text) 32%, transparent),
1290
- var(--agentkit-text)
1291
- );
1292
- background-size: 200% 100%;
1293
- background-clip: text;
1294
- animation: agentkit-shimmer 1.8s linear infinite;
1295
- }
1296
-
1297
1298
  .agentkit-summary-chevron,
1298
1299
  .agentkit-disclosure-icon {
1299
1300
  transition:
@@ -1301,16 +1302,24 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1301
1302
  transform 160ms ease;
1302
1303
  }
1303
1304
 
1304
- .agentkit-activities-summary .agentkit-summary-chevron {
1305
+ .agentkit-activities-summary > .agentkit-summary-chevron {
1305
1306
  opacity: 0;
1306
1307
  }
1307
1308
 
1308
- .agentkit-activities-summary:hover .agentkit-summary-chevron,
1309
- .agentkit-activities-summary:focus-visible .agentkit-summary-chevron,
1310
- .agentkit-activities[open] .agentkit-summary-chevron {
1309
+ .agentkit-activities[data-expandable="true"]
1310
+ .agentkit-activities-summary:hover
1311
+ > .agentkit-summary-chevron,
1312
+ .agentkit-activities[data-expandable="true"]
1313
+ .agentkit-activities-summary:focus-visible
1314
+ > .agentkit-summary-chevron,
1315
+ .agentkit-activities[data-expandable="true"][open] .agentkit-summary-chevron {
1311
1316
  opacity: 1;
1312
1317
  }
1313
1318
 
1319
+ .agentkit-activities[data-expandable="false"] .agentkit-activities-summary {
1320
+ cursor: default;
1321
+ }
1322
+
1314
1323
  .agentkit-activities-static {
1315
1324
  display: grid;
1316
1325
  min-inline-size: 0;
@@ -1346,7 +1355,10 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1346
1355
 
1347
1356
  .agentkit-activities-list {
1348
1357
  display: grid;
1358
+ max-block-size: 15.625rem;
1349
1359
  gap: 0.5rem;
1360
+ overflow: auto;
1361
+ overscroll-behavior: contain;
1350
1362
  padding-block-start: 0.25rem;
1351
1363
  }
1352
1364
 
@@ -1384,7 +1396,9 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1384
1396
 
1385
1397
  @media (hover: none) {
1386
1398
  .agentkit-message-actions[data-role="user"],
1387
- .agentkit-activities-summary .agentkit-summary-chevron {
1399
+ .agentkit-activities[data-expandable="true"]
1400
+ .agentkit-activities-summary
1401
+ .agentkit-summary-chevron {
1388
1402
  opacity: 1;
1389
1403
  }
1390
1404
  }
@@ -1844,8 +1858,6 @@ button.agentkit-object {
1844
1858
  }
1845
1859
 
1846
1860
  @media (prefers-reduced-motion: reduce) {
1847
- .agentkit-activities-current[data-running="true"]
1848
- .agentkit-activities-current-label,
1849
1861
  .agentkit-activity-item[data-status="running"] .agentkit-activity-label,
1850
1862
  .agentkit-agent[data-status="working"] .agentkit-agent-name,
1851
1863
  .agentkit-reasoning[data-active="true"] .agentkit-reasoning-label {
@@ -794,6 +794,12 @@ describe("AgentKitAssistantChat host behavior", () => {
794
794
  ];
795
795
  await mount(baseProps());
796
796
 
797
+ const thinking = container.querySelector('[role="status"]');
798
+ expect(thinking?.textContent).toBe("agentChat.status.thinking");
799
+ expect(thinking?.classList.contains("agent-thinking-indicator")).toBe(true);
800
+ expect(
801
+ thinking?.querySelector(".agent-thinking-indicator__text"),
802
+ ).not.toBeNull();
797
803
  expect(chatMocks.composerProps.disabled).toBe(false);
798
804
  expect(chatMocks.composerProps.submissionDisabled).toBe(true);
799
805
  expect(chatMocks.composerProps.announcePendingSubmission).toBe(false);
@@ -802,6 +808,22 @@ describe("AgentKitAssistantChat host behavior", () => {
802
808
  );
803
809
  });
804
810
 
811
+ it("does not duplicate Thinking after the run becomes active", async () => {
812
+ chatMocks.history = { isSubmissionInFlight: true };
813
+ chatMocks.thread.activeRunIds = ["run-active"];
814
+ chatMocks.thread.messages = [
815
+ {
816
+ id: "user-pending",
817
+ role: "user",
818
+ parts: [{ type: "text", text: "Summarize my inbox" }],
819
+ metadata: {},
820
+ },
821
+ ];
822
+ await mount(baseProps());
823
+
824
+ expect(container.querySelector(".agent-thinking-indicator")).toBeNull();
825
+ });
826
+
805
827
  it("keeps the pending announcement when the transcript has no visible user message", async () => {
806
828
  chatMocks.history = { isSubmissionInFlight: true };
807
829
  chatMocks.thread.messages = [
@@ -3080,6 +3080,12 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
3080
3080
  const threadMessageIds = new Set(
3081
3081
  thread.messages.map((message) => message.id),
3082
3082
  );
3083
+ const lastMessage = thread.messages.at(-1);
3084
+ const showThinking =
3085
+ surface.isSubmissionInFlight &&
3086
+ !surface.isRunning &&
3087
+ lastMessage?.role === "user" &&
3088
+ lastMessage.metadata?.hideUserMessage !== true;
3083
3089
  const pendingVoiceMessages = surface.voiceTranscriptMessages.filter(
3084
3090
  (message) => !threadMessageIds.has(message.id),
3085
3091
  );
@@ -3257,13 +3263,15 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
3257
3263
  threadId={threadId}
3258
3264
  />
3259
3265
  ))}
3260
- {surface.isThinkingVisibleInTranscript ? (
3266
+ {showThinking ? (
3261
3267
  <div
3268
+ className="agent-thinking-indicator"
3262
3269
  role="status"
3263
3270
  aria-live="polite"
3264
- className="px-3 py-2 text-sm text-muted-foreground"
3265
3271
  >
3266
- {t("agentChat.status.thinking")}
3272
+ <span className="agent-thinking-indicator__text">
3273
+ {t("agentChat.status.thinking")}
3274
+ </span>
3267
3275
  </div>
3268
3276
  ) : null}
3269
3277
  {guided.questions?.length ? (
@@ -4061,11 +4069,13 @@ function AgentKitTool({ value, active }: AgentKitRenderProps<AgentToolCall>) {
4061
4069
  const metadata = value.metadata ?? {};
4062
4070
  const input = asRecord(value.input) ?? {};
4063
4071
  const output =
4064
- typeof value.output === "string"
4065
- ? value.output
4066
- : value.output === undefined
4067
- ? undefined
4068
- : JSON.stringify(value.output);
4072
+ value.status === "failed" && value.error?.message
4073
+ ? value.error.message
4074
+ : typeof value.output === "string"
4075
+ ? value.output
4076
+ : value.output === undefined
4077
+ ? undefined
4078
+ : JSON.stringify(value.output);
4069
4079
  return (
4070
4080
  <ChatRunningContext.Provider
4071
4081
  value={active === true || value.status === "running"}
@@ -4079,8 +4089,8 @@ function AgentKitTool({ value, active }: AgentKitRenderProps<AgentToolCall>) {
4079
4089
  args={input}
4080
4090
  argsText={JSON.stringify(input)}
4081
4091
  result={output}
4092
+ isError={value.status === "failed"}
4082
4093
  isRunning={value.status === "running"}
4083
- outcome={value.status === "failed" ? "unknown" : undefined}
4084
4094
  structuredMeta={metadata}
4085
4095
  mcpApp={asRecord(metadata.mcpApp) as never}
4086
4096
  chatUI={asRecord(metadata.chatUI) as never}
@@ -160,6 +160,7 @@ describe("AgentPanel sibling overlay handoff", () => {
160
160
  container.remove();
161
161
  document.body.innerHTML = "";
162
162
  window.requestAnimationFrame = requestAnimationFrame;
163
+ vi.useRealTimers();
163
164
  vi.unstubAllGlobals();
164
165
  });
165
166
 
@@ -216,6 +217,7 @@ describe("AgentPanel sibling overlay handoff", () => {
216
217
 
217
218
  it("keeps All chats open after the menu restores focus", async () => {
218
219
  const focusRestorePrevented = vi.fn();
220
+ vi.useFakeTimers();
219
221
 
220
222
  await act(async () => {
221
223
  root.render(
@@ -249,8 +251,19 @@ describe("AgentPanel sibling overlay handoff", () => {
249
251
  );
250
252
  });
251
253
 
252
- await act(async () => {
253
- await new Promise<void>((resolve) => setTimeout(resolve, 0));
254
+ expect(
255
+ document.body.querySelector('[data-testid="history-content"]'),
256
+ ).toBeNull();
257
+
258
+ act(() => {
259
+ vi.advanceTimersByTime(99);
260
+ });
261
+ expect(
262
+ document.body.querySelector('[data-testid="history-content"]'),
263
+ ).toBeNull();
264
+
265
+ act(() => {
266
+ vi.advanceTimersByTime(1);
254
267
  });
255
268
 
256
269
  expect(focusRestorePrevented).toHaveBeenCalledWith(true);
@@ -261,6 +274,7 @@ describe("AgentPanel sibling overlay handoff", () => {
261
274
 
262
275
  it("opens the share popover after clicking Share in the overflow menu", async () => {
263
276
  const focusRestorePrevented = vi.fn();
277
+ vi.useFakeTimers();
264
278
 
265
279
  await act(async () => {
266
280
  root.render(
@@ -296,9 +310,19 @@ describe("AgentPanel sibling overlay handoff", () => {
296
310
  });
297
311
 
298
312
  expect(frames).toHaveLength(0);
313
+ expect(
314
+ document.body.querySelector('[data-testid="share-content"]'),
315
+ ).toBeNull();
299
316
 
300
- await act(async () => {
301
- await new Promise<void>((resolve) => setTimeout(resolve, 0));
317
+ act(() => {
318
+ vi.advanceTimersByTime(99);
319
+ });
320
+ expect(
321
+ document.body.querySelector('[data-testid="share-content"]'),
322
+ ).toBeNull();
323
+
324
+ act(() => {
325
+ vi.advanceTimersByTime(1);
302
326
  });
303
327
 
304
328
  expect(focusRestorePrevented).toHaveBeenCalledWith(true);
@@ -1,5 +1,11 @@
1
1
  import { getBrowserTabId } from "@agent-native/core/client/hooks";
2
+ import { buildSettingsRoute } from "@agent-native/core/navigation";
3
+ import {
4
+ isSettingsSectionId,
5
+ SETTINGS_SECTION_ALIASES,
6
+ } from "@agent-native/core/navigation";
2
7
  import type { AgentRun } from "@agent-native/core/progress";
8
+ import { withBuilderUtmTrackingParams } from "@agent-native/core/shared/builder-link-tracking";
3
9
  import { Tooltip as DesignSystemTooltip } from "@agent-native/toolkit/design-system";
4
10
  import {
5
11
  DropdownMenu,
@@ -36,30 +42,23 @@ import React, {
36
42
  Suspense,
37
43
  startTransition,
38
44
  } from "react";
45
+ import { useLocation, useNavigate } from "react-router";
39
46
 
40
47
  import { resolveFeedbackUrl } from "../feedback/FeedbackButton.js";
41
48
  import { ErrorReportActions, FeedbackButton } from "../feedback/index.js";
42
49
  import { RunsTrayMenuItem } from "../progress/index.js";
43
50
  import { ShareButton } from "../sharing/index.js";
44
- import { ThinkingDisplayProvider } from "./thinking-display.js";
45
- const loadMultiTabAssistantChat = () =>
46
- import("./MultiTabAssistantChat.js").then((m) => ({
47
- default: m.MultiTabAssistantChat,
48
- }));
49
- const MultiTabAssistantChatLazy = lazy(loadMultiTabAssistantChat);
50
- import { buildSettingsRoute } from "@agent-native/core/navigation";
51
- import {
52
- isSettingsSectionId,
53
- SETTINGS_SECTION_ALIASES,
54
- } from "@agent-native/core/navigation";
55
- import { withBuilderUtmTrackingParams } from "@agent-native/core/shared/builder-link-tracking";
56
- import { useLocation, useNavigate } from "react-router";
57
-
58
51
  import {
59
52
  AGENT_PANEL_OPEN_SETTINGS_EVENT,
60
53
  AGENT_PANEL_SET_MODE_EVENT,
61
54
  } from "./agent-sidebar-events.js";
62
55
  import type { AgentChatSurfaceKind } from "./chat/surface-types.js";
56
+ import {
57
+ MultiTabAssistantChat,
58
+ type MultiTabAssistantChatHeaderProps,
59
+ type MultiTabAssistantChatProps,
60
+ } from "./MultiTabAssistantChat.js";
61
+ import { ThinkingDisplayProvider } from "./thinking-display.js";
63
62
  export {
64
63
  shouldHandleAgentPanelChatShortcut,
65
64
  shouldHandleAgentSidebarToggle,
@@ -99,11 +98,6 @@ import {
99
98
  } from "./agent-sidebar-url-sync.js";
100
99
  import type { AssistantChatProps } from "./chat/surface-types.js";
101
100
  import { assistantUiRecoverableRenderErrorKind } from "./composer/assistant-ui-recovery.js";
102
- import type {
103
- MultiTabAssistantChatHeaderProps,
104
- MultiTabAssistantChatProps,
105
- } from "./MultiTabAssistantChat.js";
106
-
107
101
  function parentFrameTargetOrigin(): string {
108
102
  return getFramePostMessageTargetOrigin() ?? window.location.origin;
109
103
  }
@@ -293,6 +287,7 @@ const AGENT_PANEL_CONTROL_STYLE = {
293
287
  lineHeight: 1,
294
288
  } satisfies React.CSSProperties;
295
289
  const ACTIVATE_KEYS = new Set(["Enter", " "]);
290
+ const AGENT_PANEL_MENU_EXIT_DURATION_MS = 100;
296
291
  type AgentPanelOverlayOpenTiming = "animation-frame" | "timeout";
297
292
 
298
293
  export function deferAgentPanelOverlayOpen(
@@ -304,7 +299,7 @@ export function deferAgentPanelOverlayOpen(
304
299
  event.preventDefault();
305
300
  closeMenu();
306
301
  if (timing === "timeout") {
307
- setTimeout(openOverlay, 0);
302
+ setTimeout(openOverlay, AGENT_PANEL_MENU_EXIT_DURATION_MS);
308
303
  return;
309
304
  }
310
305
  if (
@@ -379,96 +374,6 @@ function IconTooltip({
379
374
  );
380
375
  }
381
376
 
382
- type ChatHeaderRenderer = (
383
- props: MultiTabAssistantChatHeaderProps,
384
- ) => React.ReactNode;
385
-
386
- function ChatLoadingSkeleton({
387
- renderHeader,
388
- centerComposerWhenEmpty = false,
389
- composerSlot,
390
- composerAreaClassName,
391
- composerLayoutVariant = "default",
392
- }: {
393
- renderHeader?: ChatHeaderRenderer;
394
- centerComposerWhenEmpty?: boolean;
395
- composerSlot?: React.ReactNode;
396
- composerAreaClassName?: string;
397
- composerLayoutVariant?: AssistantChatProps["composerLayoutVariant"];
398
- }) {
399
- const t = useT();
400
- const noop = useCallback(() => {}, []);
401
- const noopStr = useCallback((_id: string) => {}, []);
402
- const stubProps: MultiTabAssistantChatHeaderProps = {
403
- tabs: [],
404
- activeTabId: "",
405
- activeTabMessageCount: 0,
406
- setActiveTabId: noopStr,
407
- addTab: noop,
408
- closeTab: noopStr,
409
- closeOtherTabs: noopStr,
410
- closeAllTabs: noop,
411
- clearActiveTab: noop,
412
- showHistory: false,
413
- tabCount: 0,
414
- toggleHistory: noop,
415
- };
416
- if (centerComposerWhenEmpty) {
417
- return (
418
- <div className="flex flex-col flex-1 min-h-0">
419
- {renderHeader ? renderHeader(stubProps) : null}
420
- <div
421
- data-agent-empty-state="centered"
422
- className="relative flex flex-1 flex-col h-full min-h-0 text-foreground"
423
- >
424
- <div className="agent-chat-scroll flex-1 overflow-y-auto overflow-x-hidden min-h-0">
425
- <div className="agent-empty-state sr-only" aria-busy="true">
426
- {t("agentChat.empty.loadingChat")}
427
- </div>
428
- </div>
429
- {composerSlot}
430
- <div className="agent-composer-stack">
431
- <div
432
- className={cn(
433
- "agent-composer-area shrink-0 px-3 py-2",
434
- composerLayoutVariant !== "default" &&
435
- `agent-composer-area--${composerLayoutVariant}`,
436
- composerAreaClassName,
437
- )}
438
- >
439
- <div
440
- className={cn(
441
- "agent-composer-root flex flex-col rounded-lg border border-input bg-muted/45 transition-colors",
442
- composerLayoutVariant !== "default" &&
443
- `agent-composer-root--${composerLayoutVariant}`,
444
- )}
445
- >
446
- <div className="px-3 pt-3">
447
- <div className="h-5 w-3/5 rounded bg-muted animate-pulse motion-reduce:animate-none" />
448
- </div>
449
- <div className="mt-auto flex items-center gap-2 px-3 py-2">
450
- <div className="h-5 w-5 rounded bg-muted animate-pulse motion-reduce:animate-none" />
451
- <div className="ml-auto h-4 w-28 rounded bg-muted animate-pulse motion-reduce:animate-none" />
452
- <div className="h-7 w-7 rounded-md bg-muted animate-pulse motion-reduce:animate-none" />
453
- </div>
454
- </div>
455
- </div>
456
- </div>
457
- </div>
458
- </div>
459
- );
460
- }
461
- return (
462
- <div className="flex flex-col flex-1 min-h-0">
463
- {renderHeader ? renderHeader(stubProps) : null}
464
- {/* Composer-shaped placeholder keeps layout stable during chunk load */}
465
- <div className="mt-auto shrink-0 border-t border-border p-3">
466
- <div className="h-16 rounded-xl bg-muted/40 animate-pulse motion-reduce:animate-none" />
467
- </div>
468
- </div>
469
- );
470
- }
471
-
472
377
  export function getAgentPanelChatTabGroups(
473
378
  tabs: MultiTabAssistantChatHeaderProps["tabs"],
474
379
  activeTabId: string,
@@ -2544,13 +2449,9 @@ function AgentPanelInner({
2544
2449
  </Suspense>
2545
2450
  )}
2546
2451
 
2547
- {/* Chat view — always mounted to preserve state.
2548
- Header (with tabs + mode buttons) is always visible.
2549
- Chat content is hidden when CLI or resources mode is active.
2550
- The wrapper collapses (no flex-1) when another mode is active
2551
- so it only takes the height of its header.
2552
- The Suspense boundary renders the header chrome immediately while
2553
- the lazy assistant-ui chunk loads in the background. */}
2452
+ {/* Chat view stays mounted to preserve state. MultiTabAssistantChat
2453
+ owns its transcript loading state; mounting it here keeps the
2454
+ interactive composer available while that data loads. */}
2554
2455
  <div
2555
2456
  className={cn(
2556
2457
  "flex flex-col min-h-0",
@@ -2558,58 +2459,40 @@ function AgentPanelInner({
2558
2459
  )}
2559
2460
  >
2560
2461
  {mounted && (
2561
- <Suspense
2562
- fallback={
2563
- <ChatLoadingSkeleton
2564
- renderHeader={showHeader ? renderChatHeader : undefined}
2565
- centerComposerWhenEmpty={
2566
- assistantChatProps.centerComposerWhenEmpty
2567
- }
2568
- composerSlot={assistantChatProps.composerSlot}
2569
- composerAreaClassName={
2570
- assistantChatProps.composerAreaClassName
2571
- }
2572
- composerLayoutVariant={
2573
- assistantChatProps.composerLayoutVariant
2574
- }
2575
- />
2462
+ <MultiTabAssistantChat
2463
+ {...assistantChatProps}
2464
+ threadContentSlot={assistantChatProps.threadContentSlot}
2465
+ agentChatSurface={effectiveAgentChatSurface}
2466
+ apiUrl={apiUrl}
2467
+ showHeader={false}
2468
+ renderHeader={showHeader ? renderChatHeader : undefined}
2469
+ showTabBar={showTabBar}
2470
+ renderOverlay={
2471
+ showPageHeader && !showHeader
2472
+ ? renderPageChatOverlay
2473
+ : undefined
2576
2474
  }
2577
- >
2578
- <MultiTabAssistantChatLazy
2579
- {...assistantChatProps}
2580
- threadContentSlot={assistantChatProps.threadContentSlot}
2581
- agentChatSurface={effectiveAgentChatSurface}
2582
- apiUrl={apiUrl}
2583
- showHeader={false}
2584
- renderHeader={showHeader ? renderChatHeader : undefined}
2585
- showTabBar={showTabBar}
2586
- renderOverlay={
2587
- showPageHeader && !showHeader
2588
- ? renderPageChatOverlay
2589
- : undefined
2590
- }
2591
- contentHidden={mode !== "chat"}
2592
- emptyStateText={emptyStateText}
2593
- emptyStateAddon={emptyStateAddon}
2594
- emptyStateFooter={emptyStateFooter}
2595
- onMessageCountChange={onMessageCountChange}
2596
- suggestions={suggestions}
2597
- dynamicSuggestions={dynamicSuggestions}
2598
- suggestionPlacement={
2599
- assistantChatProps.suggestionPlacement ?? "context-chips"
2600
- }
2601
- onSwitchToCli={() => switchMode("cli")}
2602
- execMode={execMode}
2603
- onExecModeChange={switchExecMode}
2604
- storageKey={storageKey}
2605
- restoreActiveThread={restoreActiveThread}
2606
- scope={scope}
2607
- isolateHistoryByScope={isolateHistoryByScope}
2608
- showScopeBadge={showScopeBadge}
2609
- browserTabId={browserTabId}
2610
- threadUrlSync={threadUrlSync}
2611
- />
2612
- </Suspense>
2475
+ contentHidden={mode !== "chat"}
2476
+ emptyStateText={emptyStateText}
2477
+ emptyStateAddon={emptyStateAddon}
2478
+ emptyStateFooter={emptyStateFooter}
2479
+ onMessageCountChange={onMessageCountChange}
2480
+ suggestions={suggestions}
2481
+ dynamicSuggestions={dynamicSuggestions}
2482
+ suggestionPlacement={
2483
+ assistantChatProps.suggestionPlacement ?? "context-chips"
2484
+ }
2485
+ onSwitchToCli={() => switchMode("cli")}
2486
+ execMode={execMode}
2487
+ onExecModeChange={switchExecMode}
2488
+ storageKey={storageKey}
2489
+ restoreActiveThread={restoreActiveThread}
2490
+ scope={scope}
2491
+ isolateHistoryByScope={isolateHistoryByScope}
2492
+ showScopeBadge={showScopeBadge}
2493
+ browserTabId={browserTabId}
2494
+ threadUrlSync={threadUrlSync}
2495
+ />
2613
2496
  )}
2614
2497
  </div>
2615
2498
 
@@ -12,7 +12,11 @@ const mockHostedHarness = vi.hoisted(() => ({
12
12
  }));
13
13
 
14
14
  vi.mock("./AgentSidebarPanel.js", () => ({
15
- AgentSidebarPanel: () => <div data-agent-sidebar-panel-loaded="true" />,
15
+ AgentSidebarPanel: () => (
16
+ <div data-agent-sidebar-panel-loaded="true">
17
+ <textarea aria-label="Chat composer" />
18
+ </div>
19
+ ),
16
20
  }));
17
21
  vi.mock("./agent-sidebar-url-sync.js", () => ({
18
22
  ScreenRefreshBoundary: ({
@@ -146,7 +150,7 @@ beforeEach(() => {
146
150
  });
147
151
  });
148
152
 
149
- describe("AgentSidebar lazy panel boundary", () => {
153
+ describe("AgentSidebar panel", () => {
150
154
  it("pauses screen refresh until the panel is active", () => {
151
155
  renderSidebar(false);
152
156
 
@@ -229,7 +233,7 @@ describe("AgentSidebar lazy panel boundary", () => {
229
233
  ).toBeTruthy();
230
234
  });
231
235
 
232
- it("shows the panel skeleton before the lazy body resolves for open-by-default users", async () => {
236
+ it("renders an interactive composer without a loading skeleton", () => {
233
237
  localStorage.setItem("agent-native-sidebar-open", "true");
234
238
  renderSidebar(true);
235
239
 
@@ -238,10 +242,11 @@ describe("AgentSidebar lazy panel boundary", () => {
238
242
  ).toBeTruthy();
239
243
  expect(
240
244
  container?.querySelector("[data-agent-sidebar-panel-skeleton='true']"),
241
- ).toBeTruthy();
242
- expect(
243
- container?.querySelector("[data-agent-sidebar-panel-loaded='true']"),
244
245
  ).toBeNull();
246
+ const composer = container?.querySelector<HTMLTextAreaElement>(
247
+ "textarea[aria-label='Chat composer']",
248
+ );
249
+ expect(composer).toBeTruthy();
245
250
  const panel = container?.querySelector<HTMLElement>(
246
251
  ".agent-sidebar-panel[data-agent-sidebar-layout='desktop']",
247
252
  );
@@ -249,16 +254,13 @@ describe("AgentSidebar lazy panel boundary", () => {
249
254
  "var(--agent-kit-nav-surface)",
250
255
  );
251
256
 
252
- await act(async () => {
253
- await Promise.resolve();
254
- });
255
-
256
- expect(
257
- container?.querySelector("[data-agent-sidebar-panel-loaded='true']"),
258
- ).toBeTruthy();
257
+ expect(composer?.isConnected).toBe(true);
258
+ expect(composer?.disabled).toBe(false);
259
+ composer!.value = "hello";
260
+ expect(composer?.value).toBe("hello");
259
261
  });
260
262
 
261
- it("opens from the global shortcut while the panel body is still loading", async () => {
263
+ it("opens from the global shortcut with the composer available", async () => {
262
264
  renderSidebar(false);
263
265
  await act(async () => {});
264
266
 
@@ -278,14 +280,12 @@ describe("AgentSidebar lazy panel boundary", () => {
278
280
  ".agent-sidebar-panel[data-agent-sidebar-state='open']",
279
281
  ),
280
282
  ).toBeTruthy();
281
-
282
- await act(async () => {
283
- await Promise.resolve();
284
- });
285
-
286
283
  expect(
287
284
  container?.querySelector("[data-agent-sidebar-panel-loaded='true']"),
288
285
  ).toBeTruthy();
286
+ expect(
287
+ container?.querySelector("textarea[aria-label='Chat composer']"),
288
+ ).toBeTruthy();
289
289
  });
290
290
 
291
291
  it("leaves Cmd+I available to the app when the chat shortcut is disabled", async () => {