@agent-native/core 0.172.10 → 0.173.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/actions/view-screen.ts +0 -5
  3. package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +1 -1
  4. package/corpus/templates/clips/app/components/player/video-player.tsx +3 -1
  5. package/corpus/templates/clips/changelog/2026-08-24-shared-clips-now-expose-agent-readable-transcript-and-frame-metadata-from-public-links.md +6 -0
  6. package/corpus/templates/clips/server/routes/[...page].get.ts +172 -3
  7. package/corpus/templates/plan/app/components/layout/Header.tsx +1 -1
  8. package/corpus/templates/plan/app/components/layout/Layout.tsx +19 -3
  9. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +11 -10
  10. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +10 -9
  11. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
  12. package/corpus/templates/plan/app/root.tsx +9 -8
  13. package/corpus/templates/plan/app/routes/_index.tsx +6 -30
  14. package/corpus/templates/plan/app/routes/chat.tsx +34 -0
  15. package/corpus/templates/plan/server/plugins/auth.ts +1 -0
  16. package/dist/client/AgentPanel.d.ts +3 -1
  17. package/dist/client/AgentPanel.js +67 -46
  18. package/dist/client/AssistantChat.d.ts +8 -0
  19. package/dist/client/AssistantChat.js +2 -1
  20. package/dist/client/agent-chat/index.d.ts +2 -0
  21. package/dist/client/agent-chat/index.js +2 -0
  22. package/dist/client/chat/index.d.ts +2 -0
  23. package/dist/client/chat/index.js +2 -0
  24. package/dist/client/chat/message-components.d.ts +5 -14
  25. package/dist/client/chat/message-components.js +15 -8
  26. package/dist/client/chat/tool-call-display.js +24 -11
  27. package/dist/client/thinking-display.d.ts +23 -0
  28. package/dist/client/thinking-display.js +86 -0
  29. package/dist/collab/awareness.d.ts +2 -2
  30. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  31. package/dist/localization/core-messages/ar-SA.js +4 -0
  32. package/dist/localization/core-messages/de-DE.js +4 -0
  33. package/dist/localization/core-messages/en-US.d.ts +4 -0
  34. package/dist/localization/core-messages/en-US.js +4 -0
  35. package/dist/localization/core-messages/es-ES.js +4 -0
  36. package/dist/localization/core-messages/fr-FR.js +4 -0
  37. package/dist/localization/core-messages/hi-IN.js +4 -0
  38. package/dist/localization/core-messages/ja-JP.js +4 -0
  39. package/dist/localization/core-messages/ko-KR.js +4 -0
  40. package/dist/localization/core-messages/pt-BR.js +4 -0
  41. package/dist/localization/core-messages/zh-CN.js +4 -0
  42. package/dist/localization/core-messages/zh-TW.js +4 -0
  43. package/dist/localization/core-messages.d.ts +4 -0
  44. package/dist/observability/posthog-ai.d.ts +6 -0
  45. package/dist/observability/posthog-ai.js +6 -0
  46. package/dist/observability/routes.d.ts +3 -3
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  48. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  49. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  50. package/dist/resources/handlers.d.ts +1 -1
  51. package/dist/secrets/routes.d.ts +6 -6
  52. package/dist/server/transcribe-voice.d.ts +1 -1
  53. package/dist/shared/thinking-display.d.ts +23 -0
  54. package/dist/shared/thinking-display.js +31 -0
  55. package/dist/tracking/providers.js +22 -1
  56. package/package.json +3 -3
@@ -22,16 +22,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
22
22
  * <AgentChatSurface mode="page" className="h-screen" />
23
23
  */
24
24
  import { Tooltip as DesignSystemTooltip } from "@agent-native/toolkit/design-system";
25
- import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, IconShare3, } from "@tabler/icons-react";
25
+ import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, IconShare3, IconBulb, } from "@tabler/icons-react";
26
26
  import React, { useState, useEffect, useRef, useCallback, useMemo, lazy, Suspense, startTransition, } from "react";
27
27
  import { flushSync } from "react-dom";
28
28
  import { hostedHarnessAgentOption, isHostedHarnessConfigured, isHostedHarnessRuntime, normalizeHostedHarnessRuntimes, } from "../agent/harness/hosted.js";
29
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
29
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
30
30
  import { normalizeTooltipText } from "./components/ui/tooltip.js";
31
31
  import { ErrorReportActions } from "./ErrorReportActions.js";
32
32
  import { FeedbackButton, resolveFeedbackUrl } from "./FeedbackButton.js";
33
33
  import { RunsTrayMenuItem } from "./progress/RunsTray.js";
34
34
  import { ShareButton } from "./sharing/ShareButton.js";
35
+ import { ThinkingDisplayProvider, useThinkingDisplayControl, } from "./thinking-display.js";
35
36
  // Lazy-load the full assistant-ui chat stack (tiptap composer + react-markdown +
36
37
  // assistant-ui + zod block schemas) so it is NOT in the static import closure of
37
38
  // every page. The header/tab chrome renders immediately; chat streams in once the
@@ -184,6 +185,26 @@ export function consumeAgentPanelOverlayFocusRestore(pendingOverlayRef, event) {
184
185
  pendingOverlayRef.current = false;
185
186
  event.preventDefault();
186
187
  }
188
+ /**
189
+ * Reasoning visibility for this browser. Absent when a host pinned the mode
190
+ * through `thinkingDisplay`, so the menu never offers a control that cannot
191
+ * change anything.
192
+ */
193
+ function ThinkingDisplayMenuItem() {
194
+ const t = useT();
195
+ const { mode, setMode, pinned } = useThinkingDisplayControl();
196
+ if (pinned)
197
+ return null;
198
+ return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(IconBulb, { size: 14, className: "shrink-0" }), t("agentChat.thinking.display")] }), _jsx(DropdownMenuSubContent, { children: _jsxs(DropdownMenuRadioGroup, { value: mode, onValueChange: (next) => {
199
+ // The radio group hands back a bare string; anything that is not a
200
+ // known mode would silently persist and read back as the default.
201
+ if (next === "expanded" ||
202
+ next === "collapsed" ||
203
+ next === "hidden") {
204
+ setMode(next);
205
+ }
206
+ }, children: [_jsx(DropdownMenuRadioItem, { value: "expanded", children: t("agentChat.thinking.expanded") }), _jsx(DropdownMenuRadioItem, { value: "collapsed", children: t("agentChat.thinking.collapsed") }), _jsx(DropdownMenuRadioItem, { value: "hidden", children: t("agentChat.thinking.hidden") })] }) })] }));
207
+ }
187
208
  function useAvailableClis() {
188
209
  const [clis, setClis] = useState([]);
189
210
  useEffect(() => {
@@ -763,7 +784,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
763
784
  ? t("agentPanel.hideChats")
764
785
  : t("agentPanel.allChats")] })), mode === "chat" && (_jsx(RunsTrayMenuItem, { pollMs: 0, limit: 12, showRecent: true, onOpenThread: openRunThread })), mode === "chat" && _jsx(DropdownMenuSeparator, {}), mode === "cli" && availableClis.length > 0 && (_jsxs(_Fragment, { children: [availableClis.map((cli) => (_jsxs(DropdownMenuItem, { onSelect: () => selectCli(cli.command), className: cn(cli.command === selectedCli
765
786
  ? "font-medium"
766
- : "text-muted-foreground"), children: [cli.command === selectedCli ? (_jsx(IconCheck, { size: 12, className: "shrink-0" })) : (_jsx("span", { className: "w-3" })), cli.label] }, cli.command))), _jsx(DropdownMenuSeparator, {})] })), allowSettingsMode && (_jsxs(DropdownMenuItem, { onSelect: () => switchMode("settings"), className: cn(mode === "settings" ? "font-medium" : "text-muted-foreground"), children: [_jsx(IconSettings, { size: 14, className: "shrink-0" }), t("agentPanel.settings")] })), feedbackEnabled ? (_jsxs(DropdownMenuItem, { onSelect: (event) => deferAgentPanelOverlayOpen(event, closeHeaderMenuForOverlay, () => setFeedbackOpen(true)), children: [_jsx(IconMessageDots, { size: 14, className: "shrink-0" }), t("agentPanel.feedback")] })) : null, ((mode === "chat" && activeTabId) ||
787
+ : "text-muted-foreground"), children: [cli.command === selectedCli ? (_jsx(IconCheck, { size: 12, className: "shrink-0" })) : (_jsx("span", { className: "w-3" })), cli.label] }, cli.command))), _jsx(DropdownMenuSeparator, {})] })), mode === "chat" && _jsx(ThinkingDisplayMenuItem, {}), allowSettingsMode && (_jsxs(DropdownMenuItem, { onSelect: () => switchMode("settings"), className: cn(mode === "settings" ? "font-medium" : "text-muted-foreground"), children: [_jsx(IconSettings, { size: 14, className: "shrink-0" }), t("agentPanel.settings")] })), feedbackEnabled ? (_jsxs(DropdownMenuItem, { onSelect: (event) => deferAgentPanelOverlayOpen(event, closeHeaderMenuForOverlay, () => setFeedbackOpen(true)), children: [_jsx(IconMessageDots, { size: 14, className: "shrink-0" }), t("agentPanel.feedback")] })) : null, ((mode === "chat" && activeTabId) ||
767
788
  (mode === "cli" && canUseCodeTools && activeCliTab)) && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), mode === "chat" ? (shouldShowAgentPanelChatTabBar(tabs, activeTabId) ? (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: () => closeTab(activeTabId), children: [_jsx(IconX, { size: 14, className: "shrink-0" }), t("agentPanel.closeTab"), _jsx(DropdownMenuShortcut, { children: closeTabHint })] }), _jsx(DropdownMenuItem, { onSelect: () => closeOtherTabs(activeTabId), children: t("agentPanel.closeOtherTabs") }), _jsxs(DropdownMenuItem, { onSelect: () => closeAllTabs(), children: [t("agentPanel.closeAllTabs"), _jsx(DropdownMenuShortcut, { children: closeAllTabsHint })] })] })) : (_jsxs(DropdownMenuItem, { onSelect: clearActiveTab, children: [_jsx(IconX, { size: 14, className: "shrink-0" }), t("agentPanel.clearChat")] }))) : (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: () => closeCliTab(activeCliTab), children: [_jsx(IconX, { size: 14, className: "shrink-0" }), t("agentPanel.closeTab"), _jsx(DropdownMenuShortcut, { children: closeTabHint })] }), _jsx(DropdownMenuItem, { onSelect: () => closeOtherCliTabs(activeCliTab), children: t("agentPanel.closeOtherTabs") }), _jsxs(DropdownMenuItem, { onSelect: () => closeAllCliTabs(), children: [t("agentPanel.closeAllTabs"), _jsx(DropdownMenuShortcut, { children: closeAllTabsHint })] })] }))] }))] })] }), onCollapse && (_jsx(IconTooltip, { content: t("agentPanel.collapseSidebar"), children: _jsx("button", { type: "button", onClick: onCollapse, "aria-label": t("agentPanel.collapseSidebar"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconX, { size: 14 }) }) }))] })), [
768
789
  activeCliTab,
769
790
  addCliTab,
@@ -961,47 +982,47 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
961
982
  closeCliTab,
962
983
  t,
963
984
  ]);
964
- return (_jsxs("div", { className: cn("agent-panel-root flex flex-1 flex-col min-h-0 h-full text-[13px] leading-[1.2] antialiased", className), style: {
965
- ...AGENT_PANEL_ROOT_STYLE,
966
- ...style,
967
- // The chat view-transition container otherwise traps fixed onboarding
968
- // chrome below the app's own header instead of the viewport edge.
969
- ...(isFirstRunOnboardingSurface ? { contain: "none" } : {}),
970
- }, "data-agent-fullscreen": isFullscreen ? "true" : undefined, children: [_jsx("style", { dangerouslySetInnerHTML: {
971
- __html: "@media (hover:hover) and (pointer:fine){" +
972
- ".agent-sidebar-chat-header[data-agent-sidebar-chat-header]{opacity:0;pointer-events:none;transition:opacity 150ms ease-out;}" +
973
- ".agent-panel-root:hover .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-panel-root:focus-within .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-sidebar-chat-header[data-agent-sidebar-chat-header][data-agent-sidebar-chat-header-active]{opacity:1;pointer-events:auto;}" +
974
- ".agent-sidebar-panel[data-agent-sidebar-per-app-chat='true'] .agent-sidebar-chat-header[data-agent-sidebar-chat-header]{opacity:1;pointer-events:auto;transition:none;}" +
975
- "}" +
976
- ".agent-tab-close{opacity:0}.agent-tab:hover .agent-tab-close{opacity:1}" +
977
- ".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
978
- ".agent-tabs-scroll::-webkit-scrollbar{display:none;}" +
979
- `[data-agent-fullscreen='true'] .agent-thread-content,` +
980
- `[data-agent-fullscreen='true'] .agent-running-activity{` +
981
- `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
982
- `margin-left:auto;margin-right:auto;width:100%;}` +
983
- `[data-agent-fullscreen='true'] .agent-composer-area,` +
984
- `[data-agent-fullscreen='true'] .agent-plan-mode-callout{` +
985
- `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
986
- `margin-left:auto;margin-right:auto;width:100%;}` +
987
- `[data-agent-fullscreen='true'] .agent-composer-area:not(.agent-composer-area--compact){` +
988
- `padding-left:0;padding-right:0;}` +
989
- `[data-agent-fullscreen='true'] .agent-mcp-connection-suggestion--composer,` +
990
- `[data-agent-fullscreen='true'] .agent-mcp-connection-suggestion-error--composer{` +
991
- `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
992
- `margin-left:auto;margin-right:auto;width:100%;}`,
993
- } }), SHOW_ONBOARDING && mounted && (_jsx(Suspense, { fallback: null, children: _jsx(OnboardingPanel, {}) })), showFirstRunOnboarding && mounted && !insideAgentSidebar && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsx("div", { className: cn("flex flex-col min-h-0", mode === "chat" ? "flex-1" : "shrink-0"), children: mounted && (_jsx(Suspense, { fallback: _jsx(ChatLoadingSkeleton, { renderHeader: showHeader ? renderChatHeader : undefined, centerComposerWhenEmpty: assistantChatProps.centerComposerWhenEmpty, composerSlot: assistantChatProps.composerSlot, composerAreaClassName: assistantChatProps.composerAreaClassName, composerLayoutVariant: assistantChatProps.composerLayoutVariant }), children: _jsx(MultiTabAssistantChatLazy, { ...assistantChatProps, agentChatSurface: effectiveAgentChatSurface, apiUrl: apiUrl, showHeader: false, renderHeader: showHeader ? renderChatHeader : undefined, showTabBar: showTabBar, renderOverlay: showPageNewChatButton && !showHeader
994
- ? renderPageChatOverlay
995
- : undefined, contentHidden: mode !== "chat", emptyStateText: emptyStateText, emptyStateAddon: emptyStateAddon, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, onSwitchToCli: () => switchMode("cli"), execMode: execMode, onExecModeChange: switchExecMode, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync }) })) }), canUseCodeTools
996
- ? mode === "cli" &&
997
- cliTabs.map((id) => (_jsx("div", { className: "min-h-0 relative flex-1", style: {
998
- display: id === activeCliTab ? undefined : "none",
999
- }, children: _jsx(Suspense, { fallback: _jsx("div", { className: "flex items-center justify-center h-full text-muted-foreground text-sm", children: t("agentPanel.loadingTerminal") }), children: _jsx(AgentTerminal, { command: selectedCli, hideInFrame: false, className: "h-full", style: { background: "transparent" } }) }) }, id)))
1000
- : mode === "cli" && (_jsx("div", { className: "flex flex-1 flex-col items-center justify-center min-h-0 px-6 gap-3", children: _jsx(CodeAccessUnavailablePanel, { title: codeAccessEnabled
1001
- ? t("agentPanel.cliRequiresDevMode")
1002
- : codeUnavailableTitle, description: codeAccessEnabled
1003
- ? t("agentPanel.cliRequiresDevModeDescription")
1004
- : codeUnavailableDescription, ctaLabel: codeUnavailableCtaLabel, ctaHref: codeAccessEnabled ? undefined : codeUnavailableCtaHref, secondaryCtaLabel: codeUnavailableSecondaryCtaLabel, secondaryCtaHref: codeUnavailableSecondaryCtaHref }) })), mode === "resources" && (_jsx("div", { className: "flex flex-1 flex-col min-h-0", children: _jsx(Suspense, { fallback: _jsx("div", { className: "flex h-full flex-col min-h-0", children: _jsx("div", { className: "flex shrink-0 items-center justify-between border-b border-border px-2 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "h-5 w-16 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-5 w-14 rounded bg-muted animate-pulse" })] }) }) }), children: _jsx(ResourcesPanel, {}) }) })), mode === "settings" && (_jsx("div", { className: "flex flex-col flex-1 min-h-0", children: _jsx(Suspense, { fallback: _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" }), _jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" }), _jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" })] }), children: _jsx(SettingsPanel, { isDevMode: isDevMode, onToggleDevMode: () => setDevMode(!isDevMode), showDevToggle: showDevToggle, devAppUrl: devAppUrl, initialSection: settingsSection.section, sectionRequestKey: settingsSection.requestKey }) }) }))] }));
985
+ return (_jsx(ThinkingDisplayProvider, { value: assistantChatProps.thinkingDisplay, children: _jsxs("div", { className: cn("agent-panel-root flex flex-1 flex-col min-h-0 h-full text-[13px] leading-[1.2] antialiased", className), style: {
986
+ ...AGENT_PANEL_ROOT_STYLE,
987
+ ...style,
988
+ // The chat view-transition container otherwise traps fixed onboarding
989
+ // chrome below the app's own header instead of the viewport edge.
990
+ ...(isFirstRunOnboardingSurface ? { contain: "none" } : {}),
991
+ }, "data-agent-fullscreen": isFullscreen ? "true" : undefined, children: [_jsx("style", { dangerouslySetInnerHTML: {
992
+ __html: "@media (hover:hover) and (pointer:fine){" +
993
+ ".agent-sidebar-chat-header[data-agent-sidebar-chat-header]{opacity:0;pointer-events:none;transition:opacity 150ms ease-out;}" +
994
+ ".agent-panel-root:hover .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-panel-root:focus-within .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-sidebar-chat-header[data-agent-sidebar-chat-header][data-agent-sidebar-chat-header-active]{opacity:1;pointer-events:auto;}" +
995
+ ".agent-sidebar-panel[data-agent-sidebar-per-app-chat='true'] .agent-sidebar-chat-header[data-agent-sidebar-chat-header]{opacity:1;pointer-events:auto;transition:none;}" +
996
+ "}" +
997
+ ".agent-tab-close{opacity:0}.agent-tab:hover .agent-tab-close{opacity:1}" +
998
+ ".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
999
+ ".agent-tabs-scroll::-webkit-scrollbar{display:none;}" +
1000
+ `[data-agent-fullscreen='true'] .agent-thread-content,` +
1001
+ `[data-agent-fullscreen='true'] .agent-running-activity{` +
1002
+ `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
1003
+ `margin-left:auto;margin-right:auto;width:100%;}` +
1004
+ `[data-agent-fullscreen='true'] .agent-composer-area,` +
1005
+ `[data-agent-fullscreen='true'] .agent-plan-mode-callout{` +
1006
+ `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
1007
+ `margin-left:auto;margin-right:auto;width:100%;}` +
1008
+ `[data-agent-fullscreen='true'] .agent-composer-area:not(.agent-composer-area--compact){` +
1009
+ `padding-left:0;padding-right:0;}` +
1010
+ `[data-agent-fullscreen='true'] .agent-mcp-connection-suggestion--composer,` +
1011
+ `[data-agent-fullscreen='true'] .agent-mcp-connection-suggestion-error--composer{` +
1012
+ `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
1013
+ `margin-left:auto;margin-right:auto;width:100%;}`,
1014
+ } }), SHOW_ONBOARDING && mounted && (_jsx(Suspense, { fallback: null, children: _jsx(OnboardingPanel, {}) })), showFirstRunOnboarding && mounted && !insideAgentSidebar && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsx("div", { className: cn("flex flex-col min-h-0", mode === "chat" ? "flex-1" : "shrink-0"), children: mounted && (_jsx(Suspense, { fallback: _jsx(ChatLoadingSkeleton, { renderHeader: showHeader ? renderChatHeader : undefined, centerComposerWhenEmpty: assistantChatProps.centerComposerWhenEmpty, composerSlot: assistantChatProps.composerSlot, composerAreaClassName: assistantChatProps.composerAreaClassName, composerLayoutVariant: assistantChatProps.composerLayoutVariant }), children: _jsx(MultiTabAssistantChatLazy, { ...assistantChatProps, agentChatSurface: effectiveAgentChatSurface, apiUrl: apiUrl, showHeader: false, renderHeader: showHeader ? renderChatHeader : undefined, showTabBar: showTabBar, renderOverlay: showPageNewChatButton && !showHeader
1015
+ ? renderPageChatOverlay
1016
+ : undefined, contentHidden: mode !== "chat", emptyStateText: emptyStateText, emptyStateAddon: emptyStateAddon, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, onSwitchToCli: () => switchMode("cli"), execMode: execMode, onExecModeChange: switchExecMode, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync }) })) }), canUseCodeTools
1017
+ ? mode === "cli" &&
1018
+ cliTabs.map((id) => (_jsx("div", { className: "min-h-0 relative flex-1", style: {
1019
+ display: id === activeCliTab ? undefined : "none",
1020
+ }, children: _jsx(Suspense, { fallback: _jsx("div", { className: "flex items-center justify-center h-full text-muted-foreground text-sm", children: t("agentPanel.loadingTerminal") }), children: _jsx(AgentTerminal, { command: selectedCli, hideInFrame: false, className: "h-full", style: { background: "transparent" } }) }) }, id)))
1021
+ : mode === "cli" && (_jsx("div", { className: "flex flex-1 flex-col items-center justify-center min-h-0 px-6 gap-3", children: _jsx(CodeAccessUnavailablePanel, { title: codeAccessEnabled
1022
+ ? t("agentPanel.cliRequiresDevMode")
1023
+ : codeUnavailableTitle, description: codeAccessEnabled
1024
+ ? t("agentPanel.cliRequiresDevModeDescription")
1025
+ : codeUnavailableDescription, ctaLabel: codeUnavailableCtaLabel, ctaHref: codeAccessEnabled ? undefined : codeUnavailableCtaHref, secondaryCtaLabel: codeUnavailableSecondaryCtaLabel, secondaryCtaHref: codeUnavailableSecondaryCtaHref }) })), mode === "resources" && (_jsx("div", { className: "flex flex-1 flex-col min-h-0", children: _jsx(Suspense, { fallback: _jsx("div", { className: "flex h-full flex-col min-h-0", children: _jsx("div", { className: "flex shrink-0 items-center justify-between border-b border-border px-2 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "h-5 w-16 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-5 w-14 rounded bg-muted animate-pulse" })] }) }) }), children: _jsx(ResourcesPanel, {}) }) })), mode === "settings" && (_jsx("div", { className: "flex flex-col flex-1 min-h-0", children: _jsx(Suspense, { fallback: _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" }), _jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" }), _jsx("div", { className: "h-10 w-full rounded-lg bg-muted animate-pulse" })] }), children: _jsx(SettingsPanel, { isDevMode: isDevMode, onToggleDevMode: () => setDevMode(!isDevMode), showDevToggle: showDevToggle, devAppUrl: devAppUrl, initialSection: settingsSection.section, sectionRequestKey: settingsSection.requestKey }) }) }))] }) }));
1005
1026
  }
1006
1027
  // ─── Resize handle ──────────────────────────────────────────────────────────
1007
1028
  const SIDEBAR_STORAGE_KEY = "agent-native-sidebar-width";
@@ -1429,7 +1450,7 @@ export function AgentChatSurface({ mode = "panel", className, defaultMode = "cha
1429
1450
  * Wraps app content with a toggleable agent sidebar.
1430
1451
  * Use AgentToggleButton in your header to open/close it.
1431
1452
  */
1432
- export function AgentSidebar({ children, enabled = true, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openStorageKey, restoreActiveThread = true, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar = true, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning = false, onFullscreenRequest, scope, toggleScopeId, isolateHistoryByScope = false, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, }) {
1453
+ export function AgentSidebar({ children, enabled = true, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openStorageKey, restoreActiveThread = true, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar = true, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning = false, onFullscreenRequest, scope, toggleScopeId, isolateHistoryByScope = false, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, thinkingDisplay, }) {
1433
1454
  const staticHostedHarnessEnabled = isHostedHarnessConfigured(injectedAgentNativeConfig().harness);
1434
1455
  const hostedHarnessQuery = useActionQuery("get-hosted-harness-config", undefined, { enabled: staticHostedHarnessEnabled });
1435
1456
  const hostedHarnessStatus = hostedHarnessQuery.data;
@@ -2075,7 +2096,7 @@ export function AgentSidebar({ children, enabled = true, emptyStateText = "How c
2075
2096
  ? "desktop"
2076
2097
  : undefined, "data-agent-sidebar-layout": panelLayout, "data-agent-sidebar-position": effectivePosition, "data-agent-native-hosted-harness-ui": hostedHarnessUi ? "desktop" : undefined, "data-agent-sidebar-state": panelOpen ? "open" : "closed", "data-agent-sidebar-per-app-chat": isPerAppChatSidebar ? "true" : undefined, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, "data-agent-sidebar-chat-handoff": chatViewTransitionHandoff ? "true" : undefined, style: chatViewTransition
2077
2098
  ? getAgentChatViewTransitionStyle(panelStyle)
2078
- : panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, availableAgents: effectiveAvailableAgents, availableModels: availableModels, modelListLoading: modelListLoading, selectedAgent: effectiveSelectedAgent, onAgentChange: effectiveOnAgentChange, hostedHarness: hostedHarnessEnabled, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, runtime: runtime, adapterReloadKey: adapterReloadKey, threadFooterSlot: threadFooterSlot, apiUrl: apiUrl, agentChatSurface: agentChatSurface, desktopIdentityUnauthenticated: desktopIdentityUnauthenticated, desktopIdentityAuthenticated: desktopIdentityAuthenticated, showTabBar: effectiveShowTabBar, suppressInlineOpenApp: suppressInlineOpenApp, composerPlaceholder: composerPlaceholder, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, onFullViewRequest: onFullscreenRequest, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: effectivePosition, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
2099
+ : panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, availableAgents: effectiveAvailableAgents, availableModels: availableModels, modelListLoading: modelListLoading, selectedAgent: effectiveSelectedAgent, onAgentChange: effectiveOnAgentChange, hostedHarness: hostedHarnessEnabled, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, runtime: runtime, adapterReloadKey: adapterReloadKey, threadFooterSlot: threadFooterSlot, apiUrl: apiUrl, agentChatSurface: agentChatSurface, desktopIdentityUnauthenticated: desktopIdentityUnauthenticated, desktopIdentityAuthenticated: desktopIdentityAuthenticated, showTabBar: effectiveShowTabBar, suppressInlineOpenApp: suppressInlineOpenApp, composerPlaceholder: composerPlaceholder, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, onFullViewRequest: onFullscreenRequest, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, thinkingDisplay: thinkingDisplay, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: effectivePosition, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
2079
2100
  const drawerPlaceholder = wideDrawerEnabled && !presentationMode && panelOpen ? (_jsx("div", { "aria-hidden": "true", className: "agent-sidebar-drawer-placeholder shrink-0", "data-agent-sidebar-placeholder": "true", style: { width: drawerPlaceholderWidth + 1 } })) : null;
2080
2101
  return (_jsx(AgentSidebarOnboardingContext.Provider, { value: true, children: _jsxs(RealtimeVoiceModeProvider, { browserTabId: browserTabId, children: [showFirstRunOnboarding && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsxs("div", { className: "agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden", "data-agent-sidebar-position": effectivePosition, "data-agent-native-hosted-harness-ui": hostedHarnessUi ? "desktop" : undefined, "data-agent-native-hosted-chat": isPerAppChatHosted ? "true" : undefined, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: [isMobile &&
2081
2102
  !isPerAppChatHosted &&
@@ -2,6 +2,7 @@ import type { ComposerAgentOption, ComposerImageModelMenu } from "@agent-native/
2
2
  import type { ChatModelAdapter, ExportedMessageRepository } from "@assistant-ui/react";
3
3
  import React from "react";
4
4
  import type { ReasoningEffort } from "../shared/reasoning-effort.js";
5
+ import type { ThinkingDisplay } from "../shared/thinking-display.js";
5
6
  import { type AgentChatSurfaceKind } from "./agent-chat-adapter.js";
6
7
  import { type AgentChatContextItem } from "./agent-chat.js";
7
8
  import { type QueuedAttachment } from "./chat/attachment-adapters.js";
@@ -447,6 +448,13 @@ export interface AssistantChatProps {
447
448
  onAlwaysAllow?: (approvalKey: string, toolName: string) => void | Promise<void>;
448
449
  alwaysAllowScope?: "action" | "exact-command";
449
450
  };
451
+ /**
452
+ * Pin how much model reasoning this chat shows: "expanded" opens the live
453
+ * cell, "collapsed" keeps it one click away, "hidden" renders none. Omit to
454
+ * let the reader's own preference apply, which is what surfaces the in-chat
455
+ * control — a pinned mode hides it rather than leaving a dead menu item.
456
+ */
457
+ thinkingDisplay?: ThinkingDisplay;
450
458
  }
451
459
  export declare function shouldShowAssistantChatModelSelector(showModelSelector: boolean | undefined): boolean;
452
460
  export declare const CHAT_STORAGE_PREFIX = "agent-chat:";
@@ -38,6 +38,7 @@ import { addMcpConnectionCompleteListener, consumeMcpConnectionResume, } from ".
38
38
  import { McpConnectionSuggestion } from "./resources/McpConnectionSuggestion.js";
39
39
  import { claimRunStream, createRunStreamToken, ownsRunStream, releaseRunStream, } from "./run-stream-ownership.js";
40
40
  import { AgentAutoContinueSignal, readSSEStreamRaw, settleInterruptedToolCalls, } from "./sse-event-processor.js";
41
+ import { ThinkingDisplayProvider } from "./thinking-display.js";
41
42
  import { callAction } from "./use-action.js";
42
43
  import { useAgentEngineConfigured } from "./use-agent-engine-configured.js";
43
44
  import { appendChatThreadScopeParams, } from "./use-chat-threads.js";
@@ -4519,5 +4520,5 @@ export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentN
4519
4520
  const runtime = useLocalRuntime(adapter, {
4520
4521
  adapters: { attachments: attachmentAdapter },
4521
4522
  });
4522
- return (_jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(TooltipProvider, { delayDuration: 200, children: _jsx(ThreadPrimitive.Root, { className: "flex flex-1 flex-col h-full min-h-0 overflow-x-hidden", children: _jsx(AssistantUiStaleIndexErrorBoundary, { resetKey: `${tabId ?? ""}:${threadId ?? ""}`, componentName: "AssistantChat", children: _jsx(AssistantChatInner, { ref: ref, ...props, browserTabId: browserTabId, contextScope: contextScope, contextNamespace: contextNamespace, isActiveComposer: isActiveComposer, apiUrl: apiUrl, tabId: tabId, threadId: threadId }) }) }) }) }));
4523
+ return (_jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(ThinkingDisplayProvider, { value: props.thinkingDisplay, children: _jsx(TooltipProvider, { delayDuration: 200, children: _jsx(ThreadPrimitive.Root, { className: "flex flex-1 flex-col h-full min-h-0 overflow-x-hidden", children: _jsx(AssistantUiStaleIndexErrorBoundary, { resetKey: `${tabId ?? ""}:${threadId ?? ""}`, componentName: "AssistantChat", children: _jsx(AssistantChatInner, { ref: ref, ...props, browserTabId: browserTabId, contextScope: contextScope, contextNamespace: contextNamespace, isActiveComposer: isActiveComposer, apiUrl: apiUrl, tabId: tabId, threadId: threadId }) }) }) }) }) }));
4523
4524
  });
@@ -42,3 +42,5 @@ export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registe
42
42
  export * from "../chat/connectors.js";
43
43
  export * from "../chat/runtime.js";
44
44
  export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, type ChatFirstAppLayoutPreference, type ChatFirstModeReadResult, type ChatFirstAppRegistration, type ChatFirstAppResolution, type ChatFirstAppTarget, type ChatFirstAppSurfacePlacement, type ChatFirstAgentActivity, type ChatFirstAgentActivityStatus, type ChatFirstOpenAppDelivery, type ChatFirstOpenAppDetail, type ChatFirstOpenBrowserDetail, type ChatFirstBrowserResolution, type ChatFirstSessionKind, type ChatFirstSessionReference, type ChatFirstSessionWatchDelivery, type ChatFirstSessionWatchState, type ChatFirstSessionWatchStore, type ChatFirstSurfaceKind, type ChatFirstSurfaceTab, type ChatFirstSurfacePanelState, type ChatFirstSurfacePanelStore, type ChatFirstSurfaceTabsState, type ChatFirstSurfaceTabsStore, } from "../chat-first.js";
45
+ export { ThinkingDisplayProvider, getBrowserThinkingDisplay, setBrowserThinkingDisplay, subscribeToBrowserThinkingDisplay, useThinkingDisplay, useThinkingDisplayControl, } from "../thinking-display.js";
46
+ export { DEFAULT_THINKING_DISPLAY, THINKING_DISPLAY_MODES, isThinkingDisplay, type ThinkingDisplay, } from "../../shared/thinking-display.js";
@@ -41,3 +41,5 @@ export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registe
41
41
  export * from "../chat/connectors.js";
42
42
  export * from "../chat/runtime.js";
43
43
  export { CHAT_FIRST_APP_LAYOUT_STORAGE_KEY, CHAT_FIRST_DEFAULT_APP_IDS, CHAT_FIRST_SURFACE_TABS_STORAGE_KEY, CHAT_FIRST_SURFACE_PANEL_STORAGE_KEY, CHAT_FIRST_SURFACE_WIDTH_DEFAULT, CHAT_FIRST_SURFACE_WIDTH_MIN, clampChatFirstSurfaceWidth, CHAT_FIRST_MODE_STORAGE_KEY, CHAT_FIRST_MODE_CHANGED_EVENT, CHAT_FIRST_OPEN_APP_EVENT, CHAT_FIRST_OPEN_BROWSER_EVENT, CHAT_FIRST_WATCH_SESSION_EVENT, CHAT_FIRST_SURFACE_CATALOG, chatFirstSurfaceTabId, closeChatFirstSessionWatch, emitChatFirstOpenApp, emitChatFirstOpenBrowser, emitChatFirstSessionWatch, getChatFirstSessionWatchStore, getChatFirstSurfaceTabsStore, getChatFirstSurfacePanelStore, orderChatFirstAppIds, readChatFirstAppLayout, resolveChatFirstAppTarget, resolveChatFirstBrowserTarget, resolveChatFirstSessionId, normalizeChatFirstSessionReference, readChatFirstMode, readChatFirstModeState, readChatFirstSurfaceWidth, subscribeChatFirstOpenApp, subscribeChatFirstOpenBrowser, subscribeChatFirstSessionWatch, useChatFirstSessionWatch, useChatFirstSurfaceResize, useChatFirstSurfacePanel, useChatFirstSurfaceTabs, writeChatFirstAppLayout, writeChatFirstMode, writeChatFirstSurfaceWidth, } from "../chat-first.js";
44
+ export { ThinkingDisplayProvider, getBrowserThinkingDisplay, setBrowserThinkingDisplay, subscribeToBrowserThinkingDisplay, useThinkingDisplay, useThinkingDisplayControl, } from "../thinking-display.js";
45
+ export { DEFAULT_THINKING_DISPLAY, THINKING_DISPLAY_MODES, isThinkingDisplay, } from "../../shared/thinking-display.js";
@@ -20,3 +20,5 @@ export { useChatModels, type UseChatModelsResult, type EngineModelGroup, } from
20
20
  export { useChatThreads, type ChatThreadScope, type ChatThreadSnapshot, type ChatThreadSummary, type ChatThreadData, type ChatThreadShareLink, type ChatThreadShareState, type UseChatThreadsOptions, } from "../use-chat-threads.js";
21
21
  export { ChatHistoryList, type ChatHistoryItem, type ChatHistorySection, type ChatHistoryListProps, } from "./ChatHistoryList.js";
22
22
  export * from "../conversation/index.js";
23
+ export { ThinkingDisplayProvider, getBrowserThinkingDisplay, setBrowserThinkingDisplay, subscribeToBrowserThinkingDisplay, useThinkingDisplay, useThinkingDisplayControl, } from "../thinking-display.js";
24
+ export { DEFAULT_THINKING_DISPLAY, THINKING_DISPLAY_MODES, isThinkingDisplay, type ThinkingDisplay, } from "../../shared/thinking-display.js";
@@ -20,3 +20,5 @@ export { useChatModels, } from "../use-chat-models.js";
20
20
  export { useChatThreads, } from "../use-chat-threads.js";
21
21
  export { ChatHistoryList, } from "./ChatHistoryList.js";
22
22
  export * from "../conversation/index.js";
23
+ export { ThinkingDisplayProvider, getBrowserThinkingDisplay, setBrowserThinkingDisplay, subscribeToBrowserThinkingDisplay, useThinkingDisplay, useThinkingDisplayControl, } from "../thinking-display.js";
24
+ export { DEFAULT_THINKING_DISPLAY, THINKING_DISPLAY_MODES, isThinkingDisplay, } from "../../shared/thinking-display.js";
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { type ThinkingDisplay } from "../../shared/thinking-display.js";
2
3
  import type { ContentPart } from "../sse-event-processor.js";
3
4
  import { type RunErrorInfo } from "./run-recovery.js";
4
5
  import { toolCallHasPendingApproval } from "./tool-call-display.js";
@@ -145,7 +146,7 @@ export declare function isCollapsibleAssistantWorkPart(part: {
145
146
  approvalKey?: string;
146
147
  dismissed?: boolean;
147
148
  };
148
- }): boolean;
149
+ }, thinkingDisplay?: ThinkingDisplay): boolean;
149
150
  export declare function getAssistantToolSummaryInfo(parts: readonly {
150
151
  type?: string;
151
152
  toolCallId?: string;
@@ -161,18 +162,7 @@ export declare function getAssistantToolSummaryInfo(parts: readonly {
161
162
  startIndex: number;
162
163
  hiddenToolCount: number;
163
164
  };
164
- export declare function groupAssistantWorkParts(part: {
165
- type?: string;
166
- toolCallId?: string;
167
- toolName?: string;
168
- args?: Record<string, unknown>;
169
- chatUI?: unknown;
170
- mcpApp?: unknown;
171
- approval?: {
172
- approvalKey?: string;
173
- dismissed?: boolean;
174
- };
175
- }, index: number, parts: readonly {
165
+ export interface AssistantWorkPart {
176
166
  type?: string;
177
167
  toolCallId?: string;
178
168
  toolName?: string;
@@ -183,7 +173,8 @@ export declare function groupAssistantWorkParts(part: {
183
173
  approvalKey?: string;
184
174
  dismissed?: boolean;
185
175
  };
186
- }[]): ["group-work"] | null;
176
+ }
177
+ export declare function groupAssistantWorkParts(part: AssistantWorkPart, index: number, parts: readonly AssistantWorkPart[], thinkingDisplay?: ThinkingDisplay): ["group-work"] | null;
187
178
  export declare function shouldShowInlineRunError({ runError, bannerRunErrorKey, }: {
188
179
  runError: RunErrorInfo | null;
189
180
  bannerRunErrorKey: string | null | undefined;
@@ -8,6 +8,7 @@ import { PastedTextChip } from "@agent-native/toolkit/composer/PastedTextChip";
8
8
  import { useThreadRuntime, useMessageRuntime, useComposer, MessagePrimitive, ActionBarPrimitive, BranchPickerPrimitive, ComposerPrimitive, useMessagePartReasoning, useMessagePartRuntime, useAuiState, } from "@assistant-ui/react";
9
9
  import { IconX, IconCheck, IconCopy, IconChevronDown, IconChevronLeft, IconChevronRight, IconDots, IconGitFork, IconId, IconQuote, IconRefresh, IconArrowBackUp, IconFile, IconFolder, IconFileText, IconCheckbox, IconMail, IconUser, IconPresentation, IconStack2, IconMessageChatbot, IconPencil, IconAlertTriangle, IconLoader2, } from "@tabler/icons-react";
10
10
  import React, { useState, useEffect, useCallback, useRef } from "react";
11
+ import { DEFAULT_THINKING_DISPLAY, } from "../../shared/thinking-display.js";
11
12
  import { getActiveRun } from "../active-run-state.js";
12
13
  import { agentNativePath } from "../api-path.js";
13
14
  import { writeClipboardText } from "../clipboard.js";
@@ -18,6 +19,7 @@ import { localizeKnownChatErrorText } from "../error-format.js";
18
19
  import { DEFAULT_LOCALE, useFormatters, useOptionalLocale, useT, } from "../i18n.js";
19
20
  import { ThumbsFeedback } from "../observability/ThumbsFeedback.js";
20
21
  import { McpConnectionSuggestion } from "../resources/McpConnectionSuggestion.js";
22
+ import { useThinkingDisplay } from "../thinking-display.js";
21
23
  import { isCallAgentToolCallShadowed, isToolCallActive, shadowedCallAgentToolCallIds, } from "../tool-display.js";
22
24
  import { cn } from "../utils.js";
23
25
  import { MarkdownText, renderMarkdownToClipboardHtml, SmoothMarkdownText, } from "./markdown-renderer.js";
@@ -795,9 +797,11 @@ export function isAlwaysVisibleAssistantTool(part) {
795
797
  typeof part.toolName === "string" &&
796
798
  ALWAYS_VISIBLE_ASSISTANT_TOOLS.has(part.toolName));
797
799
  }
798
- export function isCollapsibleAssistantWorkPart(part) {
800
+ export function isCollapsibleAssistantWorkPart(part, thinkingDisplay = DEFAULT_THINKING_DISPLAY) {
801
+ // Hidden reasoning renders nothing, so counting it as work would wrap a
802
+ // reasoning-only turn in an empty "Worked for…" disclosure.
799
803
  if (part.type === "reasoning")
800
- return true;
804
+ return thinkingDisplay !== "hidden";
801
805
  return (part.type === "tool-call" &&
802
806
  !isAlwaysVisibleAssistantTool(part) &&
803
807
  part.chatUI === undefined &&
@@ -825,16 +829,17 @@ export function getAssistantToolSummaryInfo(parts) {
825
829
  hiddenToolCount: toolCallIndices.length - ASSISTANT_VISIBLE_TOOL_CALL_LIMIT,
826
830
  };
827
831
  }
828
- export function groupAssistantWorkParts(part, index, parts) {
832
+ export function groupAssistantWorkParts(part, index, parts, thinkingDisplay = DEFAULT_THINKING_DISPLAY) {
829
833
  if (isCallAgentToolCallShadowed(parts, index)) {
830
834
  const previousPart = parts[index - 1];
831
835
  const previousPartIsInWorkGroup = previousPart != null &&
832
- (isCollapsibleAssistantWorkPart(previousPart) ||
836
+ (isCollapsibleAssistantWorkPart(previousPart, thinkingDisplay) ||
833
837
  isCallAgentToolCallShadowed(parts, index - 1));
834
838
  return previousPartIsInWorkGroup ? ["group-work"] : null;
835
839
  }
836
- if (isCollapsibleAssistantWorkPart(part))
840
+ if (isCollapsibleAssistantWorkPart(part, thinkingDisplay)) {
837
841
  return ["group-work"];
842
+ }
838
843
  return null;
839
844
  }
840
845
  export function shouldShowInlineRunError({ runError, bannerRunErrorKey, }) {
@@ -882,6 +887,8 @@ export function AssistantMessage() {
882
887
  const messageTurnId = assistantMessageTurnId(msg);
883
888
  const userStoppedRun = React.useContext(UserStoppedRunContext);
884
889
  const isUserStoppedRun = assistantMessageWasUserStopped(msg) || userStoppedRun(messageRunId);
890
+ const thinkingDisplay = useThinkingDisplay();
891
+ const groupWorkParts = useCallback((part, index, parts) => groupAssistantWorkParts(part, index, parts, thinkingDisplay), [thinkingDisplay]);
885
892
  const hasRenderableContent = assistantMessageHasRenderableContent(msg);
886
893
  const hasUnresolvedTool = assistantMessageHasUnresolvedTool(msg.content);
887
894
  const hasActiveTool = assistantMessageHasActiveTool(msg.content);
@@ -1066,11 +1073,11 @@ export function AssistantMessage() {
1066
1073
  const hasCollapsibleWork = Array.isArray(msgContent) &&
1067
1074
  msgContent.some((p, index) => !isCallAgentToolCallShadowed(msgContent, index) &&
1068
1075
  (p.type !== "tool-call" || p.activity !== true) &&
1069
- isCollapsibleAssistantWorkPart(p));
1076
+ isCollapsibleAssistantWorkPart(p, thinkingDisplay));
1070
1077
  const firstWorkPartIndex = Array.isArray(msgContent)
1071
1078
  ? msgContent.findIndex((p, index) => !isCallAgentToolCallShadowed(msgContent, index) &&
1072
1079
  (p.type !== "tool-call" || p.activity !== true) &&
1073
- isCollapsibleAssistantWorkPart(p))
1080
+ isCollapsibleAssistantWorkPart(p, thinkingDisplay))
1074
1081
  : -1;
1075
1082
  const shadowedToolCallIds = Array.isArray(msgContent)
1076
1083
  ? shadowedCallAgentToolCallIds(msgContent)
@@ -1081,7 +1088,7 @@ export function AssistantMessage() {
1081
1088
  });
1082
1089
  if (!hasRenderableContent || isMissingCredential)
1083
1090
  return null;
1084
- return (_jsxs("div", { className: "group relative", style: { contentVisibility: isComplete ? "auto" : "visible" }, children: [_jsxs("div", { className: "w-full max-w-[95%] text-sm leading-relaxed text-foreground", children: [isComplete && (_jsx(McpConnectionSuggestion, { text: responseConnectionText, contextText: responseConnectionContext, variant: "response" })), _jsx(ToolCallStackMotion, { children: _jsx(MessagePrimitive.GroupedParts, { groupBy: groupAssistantWorkParts, children: ({ part, children }) => {
1091
+ return (_jsxs("div", { className: "group relative", style: { contentVisibility: isComplete ? "auto" : "visible" }, children: [_jsxs("div", { className: "w-full max-w-[95%] text-sm leading-relaxed text-foreground", children: [isComplete && (_jsx(McpConnectionSuggestion, { text: responseConnectionText, contextText: responseConnectionContext, variant: "response" })), _jsx(ToolCallStackMotion, { children: _jsx(MessagePrimitive.GroupedParts, { groupBy: groupWorkParts, children: ({ part, children }) => {
1085
1092
  switch (part.type) {
1086
1093
  case "group-work": {
1087
1094
  const showSummary = shouldShowAssistantWorkSummary({
@@ -12,6 +12,7 @@ import { useOptionalLocale, useT } from "../i18n.js";
12
12
  import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
13
13
  import { findMcpIntegrationForToolName } from "../resources/mcp-integration-catalog.js";
14
14
  import { McpIntegrationLogo } from "../resources/McpIntegrationLogo.js";
15
+ import { useThinkingDisplay } from "../thinking-display.js";
15
16
  import { BashCell, EditCell, WriteCell, FilesChangedSummary, } from "../tool-cells/index.js";
16
17
  import { humanizeToolName, isCallAgentToolCallShadowed, isToolCallActive, } from "../tool-display.js";
17
18
  import { useAgentChatContext } from "../use-agent-chat-context.js";
@@ -644,22 +645,26 @@ function isReconnectToolSummaryPart(content, index, startIndex) {
644
645
  // ─── Reasoning / Thinking cell ────────────────────────────────────────────────
645
646
  /**
646
647
  * Completed reasoning and tool calls share one outer "Worked for…"
647
- * disclosure. Reasoning cells inside it render their prose directly so
648
- * opening that summary never reveals a redundant second disclosure.
648
+ * disclosure. Inside it a reasoning cell keeps its own disclosure — the tool
649
+ * calls it sits between are collapsible there, and reasoning that could not be
650
+ * collapsed was the longest thing in an opened summary by far.
649
651
  */
650
652
  const WorkSummaryContentContext = React.createContext(false);
651
653
  export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoCollapse = false, collapseWhenReplaced = false, durationMs, }) {
652
654
  const t = useT();
653
655
  const formatDuration = useLocalizedWorkedDuration();
656
+ const display = useThinkingDisplay();
654
657
  const embeddedInWorkSummary = React.useContext(WorkSummaryContentContext);
655
- const [open, setOpen] = useState(defaultOpen ?? true);
658
+ // Only "expanded" honours a caller's request to start open. "collapsed"
659
+ // keeps every cell shut until the reader asks for it, which is the point of
660
+ // the mode — a live cell that opens itself is what pushes the answer off
661
+ // screen mid-turn.
662
+ const startOpen = display === "expanded" ? (defaultOpen ?? true) : false;
663
+ const [open, setOpen] = useState(startOpen);
656
664
  const wasStreamingRef = useRef(isStreaming);
657
665
  const wasReplacedRef = useRef(collapseWhenReplaced);
666
+ const previousDisplayRef = useRef(display);
658
667
  const trimmed = text.trim();
659
- // Reasoning is already a compact live status surface. Rendering the latest
660
- // chunk directly avoids a second character-level queue that can lag behind
661
- // the model and make the surrounding chat look like it is jumping.
662
- const visibleText = trimmed;
663
668
  useEffect(() => {
664
669
  if (autoCollapse && wasStreamingRef.current && !isStreaming) {
665
670
  setOpen(false);
@@ -672,11 +677,19 @@ export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoColl
672
677
  }
673
678
  wasReplacedRef.current = collapseWhenReplaced;
674
679
  }, [collapseWhenReplaced]);
680
+ // Switching the preference re-applies it to cells already on screen, so the
681
+ // change is visible on the turn the reader is looking at rather than only on
682
+ // the next one.
683
+ useEffect(() => {
684
+ if (previousDisplayRef.current === display)
685
+ return;
686
+ previousDisplayRef.current = display;
687
+ setOpen(startOpen);
688
+ }, [display, startOpen]);
689
+ if (display === "hidden")
690
+ return null;
675
691
  if (!trimmed && !isStreaming)
676
692
  return null;
677
- if (embeddedInWorkSummary) {
678
- return (_jsx("div", { className: "pb-1 pl-5 text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }));
679
- }
680
693
  const label = isStreaming
681
694
  ? t("agentChat.status.thinking")
682
695
  : durationMs != null
@@ -687,7 +700,7 @@ export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoColl
687
700
  // Only clamp to a scroll-free "tail" view while actively streaming and
688
701
  // expanded — once the run finishes the full text is shown, unclamped.
689
702
  const showTail = isStreaming && open;
690
- return (_jsxs("div", { className: "my-0.5 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") }), isStreaming ? (_jsx("span", { className: "agent-thinking-indicator__text", children: label })) : (_jsx("span", { children: label }))] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: cn("pl-5 pb-1", showTail && "reasoning-cell-tail"), children: _jsx("div", { className: "text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }) }) })] }));
703
+ return (_jsxs("div", { className: cn("w-full", embeddedInWorkSummary ? "my-0" : "my-0.5"), children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") }), isStreaming ? (_jsx("span", { className: "agent-thinking-indicator__text", children: label })) : (_jsx("span", { children: label }))] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: cn("ps-5 pb-1", showTail && "reasoning-cell-tail"), children: trimmed ? (_jsx("div", { className: "agent-reasoning-markdown text-[13px] leading-relaxed text-muted-foreground", children: _jsx(SmoothMarkdownText, { text: trimmed, streaming: isStreaming, animateStreaming: false, resetKey: "reasoning", statusType: isStreaming ? "running" : "complete" }) })) : (_jsx("div", { className: "text-[13px] leading-relaxed text-muted-foreground", children: isStreaming ? "…" : "" })) }) })] }));
691
704
  }
692
705
  // ─── Worked-for duration helpers ──────────────────────────────────────────────
693
706
  export function formatWorkedDuration(ms, options = {}) {
@@ -0,0 +1,23 @@
1
+ /** Browser-local reasoning-visibility preference for the chat surface.
2
+ *
3
+ * Presentation only, and deliberately device-local: it must not reach the
4
+ * agent, application state, or the engine's reasoning-effort request.
5
+ */
6
+ import type { ReactNode } from "react";
7
+ import { type ThinkingDisplay } from "../shared/thinking-display.js";
8
+ export declare const THINKING_DISPLAY_STORAGE_KEY = "agent-native:thinking-display";
9
+ export declare function getBrowserThinkingDisplay(): ThinkingDisplay;
10
+ export declare function setBrowserThinkingDisplay(mode: ThinkingDisplay): void;
11
+ export declare function subscribeToBrowserThinkingDisplay(callback: () => void): () => void;
12
+ export declare function ThinkingDisplayProvider({ value, children, }: {
13
+ value?: ThinkingDisplay;
14
+ children: ReactNode;
15
+ }): import("react").JSX.Element;
16
+ export declare function useThinkingDisplay(): ThinkingDisplay;
17
+ /** Reads and writes the preference for the in-chat control. */
18
+ export declare function useThinkingDisplayControl(): {
19
+ mode: ThinkingDisplay;
20
+ setMode: (mode: ThinkingDisplay) => void;
21
+ /** True when a host prop pins the mode, so no control should be offered. */
22
+ pinned: boolean;
23
+ };
@@ -0,0 +1,86 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** Browser-local reasoning-visibility preference for the chat surface.
3
+ *
4
+ * Presentation only, and deliberately device-local: it must not reach the
5
+ * agent, application state, or the engine's reasoning-effort request.
6
+ */
7
+ import { createContext, useCallback, useContext, useSyncExternalStore, } from "react";
8
+ import { DEFAULT_THINKING_DISPLAY, parseThinkingDisplay, } from "../shared/thinking-display.js";
9
+ export const THINKING_DISPLAY_STORAGE_KEY = "agent-native:thinking-display";
10
+ const THINKING_DISPLAY_CHANGE_EVENT = "agent-native:thinking-display-change";
11
+ export function getBrowserThinkingDisplay() {
12
+ if (typeof window === "undefined")
13
+ return DEFAULT_THINKING_DISPLAY;
14
+ try {
15
+ return (parseThinkingDisplay(window.localStorage.getItem(THINKING_DISPLAY_STORAGE_KEY)) ?? DEFAULT_THINKING_DISPLAY);
16
+ // A blocked or unavailable localStorage means "no stored preference",
17
+ // which is exactly the default. Nothing downstream can act on the
18
+ // difference, and throwing would break the whole chat header.
19
+ // coercion-ok: absent and unreadable are the same answer for this key.
20
+ }
21
+ catch {
22
+ return DEFAULT_THINKING_DISPLAY;
23
+ }
24
+ }
25
+ export function setBrowserThinkingDisplay(mode) {
26
+ if (typeof window === "undefined")
27
+ return;
28
+ try {
29
+ if (mode === DEFAULT_THINKING_DISPLAY) {
30
+ window.localStorage.removeItem(THINKING_DISPLAY_STORAGE_KEY);
31
+ }
32
+ else {
33
+ window.localStorage.setItem(THINKING_DISPLAY_STORAGE_KEY, mode);
34
+ }
35
+ // A failed write leaves the in-memory listeners correct for this tab,
36
+ // which is the whole visible effect of the change.
37
+ // coercion-ok: an unwritable localStorage is not a preference failure.
38
+ }
39
+ catch {
40
+ // Intentionally empty: the change event below still updates this tab.
41
+ }
42
+ window.dispatchEvent(new Event(THINKING_DISPLAY_CHANGE_EVENT));
43
+ }
44
+ export function subscribeToBrowserThinkingDisplay(callback) {
45
+ if (typeof window === "undefined")
46
+ return () => undefined;
47
+ const onStorage = (event) => {
48
+ if (event.key === THINKING_DISPLAY_STORAGE_KEY || event.key === null) {
49
+ callback();
50
+ }
51
+ };
52
+ const onChange = () => callback();
53
+ window.addEventListener("storage", onStorage);
54
+ window.addEventListener(THINKING_DISPLAY_CHANGE_EVENT, onChange);
55
+ return () => {
56
+ window.removeEventListener("storage", onStorage);
57
+ window.removeEventListener(THINKING_DISPLAY_CHANGE_EVENT, onChange);
58
+ };
59
+ }
60
+ /**
61
+ * A host-supplied mode. When set it wins over the stored preference and the
62
+ * in-chat control disappears, so a host that pins the mode does not leave a
63
+ * menu item that silently does nothing.
64
+ */
65
+ const ThinkingDisplayContext = createContext(undefined);
66
+ export function ThinkingDisplayProvider({ value, children, }) {
67
+ // Inherit rather than clear: a chat surface nested inside a host that pinned
68
+ // the mode renders this provider too, and passing its own absent prop
69
+ // straight through would silently un-pin the host's choice.
70
+ const inherited = useContext(ThinkingDisplayContext);
71
+ return (_jsx(ThinkingDisplayContext.Provider, { value: value ?? inherited, children: children }));
72
+ }
73
+ export function useThinkingDisplay() {
74
+ const pinned = useContext(ThinkingDisplayContext);
75
+ const stored = useSyncExternalStore(subscribeToBrowserThinkingDisplay, getBrowserThinkingDisplay, () => DEFAULT_THINKING_DISPLAY);
76
+ return pinned ?? stored;
77
+ }
78
+ /** Reads and writes the preference for the in-chat control. */
79
+ export function useThinkingDisplayControl() {
80
+ const pinned = useContext(ThinkingDisplayContext);
81
+ const mode = useThinkingDisplay();
82
+ const setMode = useCallback((next) => {
83
+ setBrowserThinkingDisplay(next);
84
+ }, []);
85
+ return { mode, setMode, pinned: pinned !== undefined };
86
+ }