@agent-native/core 0.149.1 → 0.149.2

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 (115) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +2 -0
  3. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
  4. package/corpus/templates/analytics/app/global.css +1 -1
  5. package/corpus/templates/analytics/app/i18n-data.ts +37 -0
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
  7. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
  8. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
  9. package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
  10. package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  11. package/corpus/templates/assets/app/global.css +1 -1
  12. package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  13. package/corpus/templates/brain/app/global.css +1 -1
  14. package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  15. package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  16. package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
  17. package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
  18. package/corpus/templates/clips/desktop/src/app.tsx +274 -137
  19. package/corpus/templates/clips/desktop/src/styles.css +35 -0
  20. package/corpus/templates/content/app/global.css +1 -0
  21. package/corpus/templates/crm/app/global.css +1 -1
  22. package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  23. package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  24. package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  25. package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  26. package/corpus/templates/plan/app/global.css +1 -0
  27. package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  28. package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
  29. package/corpus/templates/slides/AGENTS.md +11 -10
  30. package/corpus/templates/slides/actions/add-deck.ts +2 -0
  31. package/corpus/templates/slides/actions/add-slide.ts +15 -1
  32. package/corpus/templates/slides/actions/create-deck.ts +18 -6
  33. package/corpus/templates/slides/actions/get-deck.ts +29 -1
  34. package/corpus/templates/slides/actions/patch-deck.ts +122 -9
  35. package/corpus/templates/slides/actions/save-deck.ts +24 -1
  36. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
  37. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +69 -60
  38. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
  39. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
  40. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
  41. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
  42. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +3 -3
  43. package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
  44. package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
  45. package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
  46. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
  47. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
  48. package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
  49. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  50. package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
  51. package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
  52. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
  53. package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -27
  54. package/corpus/templates/slides/app/pages/Index.tsx +240 -89
  55. package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
  56. package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
  57. package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
  58. package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
  59. package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
  60. package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
  61. package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
  62. package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
  63. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  64. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
  65. package/corpus/templates/slides/shared/api.ts +2 -1
  66. package/corpus/templates/slides/shared/deck-title.ts +135 -0
  67. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
  68. package/dist/agent/engine/ai-sdk-engine.js +1 -1
  69. package/dist/agent/production-agent.d.ts +21 -1
  70. package/dist/agent/production-agent.js +129 -10
  71. package/dist/agent/tool-approval-store.d.ts +18 -0
  72. package/dist/agent/tool-approval-store.js +122 -0
  73. package/dist/agent/types.d.ts +1 -1
  74. package/dist/cli/code-agent-executor.js +11 -2
  75. package/dist/client/AgentPanel.js +31 -6
  76. package/dist/client/AssistantChat.d.ts +3 -2
  77. package/dist/client/AssistantChat.js +7 -20
  78. package/dist/client/agent-chat/index.d.ts +1 -1
  79. package/dist/client/agent-chat-adapter.js +11 -0
  80. package/dist/client/agent-chat.d.ts +1 -1
  81. package/dist/client/chat/markdown-renderer.js +14 -2
  82. package/dist/client/chat/message-components.d.ts +6 -2
  83. package/dist/client/chat/message-components.js +25 -47
  84. package/dist/client/chat/tool-call-display.d.ts +3 -5
  85. package/dist/client/chat/tool-call-display.js +7 -212
  86. package/dist/client/chat-first/apps-rail.js +3 -3
  87. package/dist/client/chat-first/browser-pane.js +4 -1
  88. package/dist/client/chat-first/chat-history.d.ts +3 -1
  89. package/dist/client/chat-first/chat-history.js +2 -2
  90. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  91. package/dist/client/chat-first/surface-tabs.js +52 -41
  92. package/dist/client/chat-first/types.d.ts +3 -0
  93. package/dist/client/chat-first.d.ts +10 -0
  94. package/dist/client/chat-first.js +37 -2
  95. package/dist/client/components/ui/message-scroller.js +4 -1
  96. package/dist/client/i18n.js +3 -1
  97. package/dist/client/resources/McpConnectionSuggestion.js +9 -1
  98. package/dist/client/sse-event-processor.js +10 -0
  99. package/dist/client/use-chat-models.js +156 -80
  100. package/dist/collab/struct-routes.d.ts +1 -1
  101. package/dist/integrations/webhook-handler.js +1 -1
  102. package/dist/observability/routes.d.ts +1 -1
  103. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  104. package/dist/resources/handlers.d.ts +1 -1
  105. package/dist/secrets/routes.d.ts +9 -9
  106. package/dist/server/auth.js +262 -31
  107. package/dist/server/onboarding-html.js +56 -9
  108. package/dist/shared/reasoning-effort.d.ts +8 -8
  109. package/dist/shared/reasoning-effort.js +10 -10
  110. package/dist/shared/streaming-text-smoothing.js +41 -11
  111. package/dist/styles/agent-native.css +19 -32
  112. package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  113. package/docs/content/template-chat.mdx +1 -1
  114. package/package.json +2 -2
  115. package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
@@ -24,6 +24,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
24
24
  import { Tooltip as DesignSystemTooltip } from "@agent-native/toolkit/design-system";
25
25
  import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, } from "@tabler/icons-react";
26
26
  import React, { useState, useEffect, useRef, useCallback, useMemo, lazy, Suspense, startTransition, } from "react";
27
+ import { flushSync } from "react-dom";
27
28
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
28
29
  import { normalizeTooltipText } from "./components/ui/tooltip.js";
29
30
  import { ErrorReportActions } from "./ErrorReportActions.js";
@@ -1700,6 +1701,26 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1700
1701
  return next;
1701
1702
  });
1702
1703
  }, []);
1704
+ // `view-transition-name` is only legal to carry while a transition is
1705
+ // actually capturing. Left on permanently it makes the panel its own
1706
+ // stacking context and the containing block for every fixed/absolute
1707
+ // descendant, and enlists it as a captured group in unrelated transitions
1708
+ // (any React Router `viewTransition` navigation), which is how overlays end
1709
+ // up painted at stale offsets. Apply it only around the drawer morph, and
1710
+ // flush it into the DOM first: `startViewTransition` captures the old state
1711
+ // before it invokes the callback, so a name applied in a normal React commit
1712
+ // would land too late to be captured.
1713
+ const [drawerMorphing, setDrawerMorphing] = useState(false);
1714
+ const runDrawerMorph = useCallback((apply) => {
1715
+ flushSync(() => setDrawerMorphing(true));
1716
+ const settle = () => setDrawerMorphing(false);
1717
+ const transition = startAgentChatViewTransition(apply);
1718
+ if (!transition) {
1719
+ settle();
1720
+ return;
1721
+ }
1722
+ transition.finished.then(settle, settle);
1723
+ }, []);
1703
1724
  const snapTo75Percent = useCallback(() => {
1704
1725
  if (drawerExitTimerRef.current !== null) {
1705
1726
  clearTimeout(drawerExitTimerRef.current);
@@ -1719,8 +1740,8 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1719
1740
  }
1720
1741
  catch { }
1721
1742
  };
1722
- startAgentChatViewTransition(apply);
1723
- }, [drawerPlaceholderWidth, isWideDrawer, width]);
1743
+ runDrawerMorph(apply);
1744
+ }, [runDrawerMorph, drawerPlaceholderWidth, isWideDrawer, width]);
1724
1745
  const exitWideDrawer = useCallback(() => {
1725
1746
  if (!isWideDrawer || drawerExitTimerRef.current !== null)
1726
1747
  return;
@@ -1738,8 +1759,8 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1738
1759
  setIsWideDrawer(false);
1739
1760
  }, SIDEBAR_ANIMATION_MS + 32);
1740
1761
  };
1741
- startAgentChatViewTransition(apply);
1742
- }, [drawerPlaceholderWidth, isWideDrawer]);
1762
+ runDrawerMorph(apply);
1763
+ }, [runDrawerMorph, drawerPlaceholderWidth, isWideDrawer]);
1743
1764
  const handleResizeStart = useCallback(() => setIsResizing(true), []);
1744
1765
  const handleResizeEnd = useCallback(() => setIsResizing(false), []);
1745
1766
  const isLeft = position === "left";
@@ -1820,7 +1841,9 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1820
1841
  borderLeft: isLeft ? "none" : "1px solid hsl(var(--border))",
1821
1842
  borderRight: isLeft ? "1px solid hsl(var(--border))" : "none",
1822
1843
  display: "flex",
1823
- viewTransitionName: SIDEBAR_DRAWER_VIEW_TRANSITION_NAME,
1844
+ ...(drawerMorphing
1845
+ ? { viewTransitionName: SIDEBAR_DRAWER_VIEW_TRANSITION_NAME }
1846
+ : null),
1824
1847
  };
1825
1848
  }
1826
1849
  else {
@@ -1841,7 +1864,9 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1841
1864
  display: desktopAnimationEnabled || panelOpen ? "flex" : "none",
1842
1865
  minWidth: desktopAnimationEnabled ? 0 : undefined,
1843
1866
  pointerEvents: desktopAnimationEnabled && !panelOpen ? "none" : undefined,
1844
- viewTransitionName: SIDEBAR_DRAWER_VIEW_TRANSITION_NAME,
1867
+ ...(drawerMorphing
1868
+ ? { viewTransitionName: SIDEBAR_DRAWER_VIEW_TRANSITION_NAME }
1869
+ : null),
1845
1870
  };
1846
1871
  }
1847
1872
  // Mount the live chat surface only while visible or actively needed. Keeping
@@ -136,6 +136,7 @@ type QueuedMessage = {
136
136
  recoveryAction?: AgentRecoveryAction;
137
137
  trackInRunsTray?: boolean;
138
138
  hideUserMessage?: boolean;
139
+ approvedToolCalls?: string[];
139
140
  /**
140
141
  * Model/engine/effort snapshotted at enqueue time, for the same reason
141
142
  * `requestMode` is: the picker is global and live, so a queue that flushes
@@ -306,7 +307,7 @@ export interface AssistantChatProps {
306
307
  defaultModel?: string;
307
308
  /** Selected engine override for this conversation */
308
309
  selectedEngine?: string;
309
- /** Selected reasoning effort override for this conversation */
310
+ /** Selected effort override for this conversation */
310
311
  selectedEffort?: ReasoningEffort;
311
312
  /** Available engine/model list for the model picker */
312
313
  availableModels?: Array<{
@@ -319,7 +320,7 @@ export interface AssistantChatProps {
319
320
  modelListLoading?: boolean;
320
321
  /** Callback when user picks a model from the picker */
321
322
  onModelChange?: (model: string, engine: string) => void;
322
- /** Callback when user picks a reasoning effort from the picker */
323
+ /** Callback when user picks an effort from the picker */
323
324
  onEffortChange?: (effort: ReasoningEffort) => void;
324
325
  /**
325
326
  * Optional secondary model menu (e.g. an image-generation model) shown inside
@@ -3102,7 +3102,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3102
3102
  ...(messageAttachments.length > 0
3103
3103
  ? { attachments: messageAttachments }
3104
3104
  : {}),
3105
- ...createUserMessageRunConfig(next.references, next.requestMode, next.recoveryAction, next.trackInRunsTray, undefined, next.id, next.hideUserMessage, {
3105
+ ...createUserMessageRunConfig(next.references, next.requestMode, next.recoveryAction, next.trackInRunsTray, next.approvedToolCalls, next.id, next.hideUserMessage, {
3106
3106
  model: next.model,
3107
3107
  engine: next.engine,
3108
3108
  effort: next.effort,
@@ -3433,7 +3433,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3433
3433
  stopActiveRunRef.current({ preserveQueuedMessages: true });
3434
3434
  }, [applyLocalQueuedMessages]);
3435
3435
  const visibleQueuedMessages = useMemo(() => queuedMessages.filter((message) => !message.hideUserMessage), [queuedMessages]);
3436
- const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false, preserveReconnectAutoRecoveryBudget = false, hideUserMessage = false, submitMessageId) => {
3436
+ const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false, preserveReconnectAutoRecoveryBudget = false, hideUserMessage = false, submitMessageId, approvedToolCalls) => {
3437
3437
  if (isAgentChatSubmitCancelled(submitMessageId))
3438
3438
  return;
3439
3439
  if (engineSetupRequired) {
@@ -3582,6 +3582,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3582
3582
  recoveryAction,
3583
3583
  trackInRunsTray,
3584
3584
  hideUserMessage,
3585
+ approvedToolCalls,
3585
3586
  ...modelSnapshot,
3586
3587
  },
3587
3588
  ]);
@@ -3602,6 +3603,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3602
3603
  recoveryAction,
3603
3604
  trackInRunsTray,
3604
3605
  hideUserMessage,
3606
+ approvedToolCalls,
3605
3607
  ...modelSnapshot,
3606
3608
  },
3607
3609
  ]);
@@ -3615,7 +3617,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3615
3617
  ...(messageAttachments.length > 0
3616
3618
  ? { attachments: messageAttachments }
3617
3619
  : {}),
3618
- ...createUserMessageRunConfig(references, effectiveRequestMode, recoveryAction, trackInRunsTray, undefined, undefined, hideUserMessage),
3620
+ ...createUserMessageRunConfig(references, effectiveRequestMode, recoveryAction, trackInRunsTray, approvedToolCalls, undefined, hideUserMessage),
3619
3621
  });
3620
3622
  }
3621
3623
  catch (error) {
@@ -4006,31 +4008,16 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4006
4008
  getApprovalResolution,
4007
4009
  onApprovalResolved: recordApprovalResolution,
4008
4010
  onApprove: (approvalKey) => {
4009
- markOptimisticRunning();
4010
- appendThreadMessage({
4011
- role: "user",
4012
- content: [
4013
- {
4014
- type: "text",
4015
- text: "Approved. Go ahead and run the requested action.",
4016
- },
4017
- ],
4018
- ...createUserMessageRunConfig(undefined, execMode === "plan"
4019
- ? "plan"
4020
- : execMode === "build"
4021
- ? "act"
4022
- : undefined, undefined, undefined, [approvalKey]),
4023
- });
4011
+ void addToQueue("Approved. Go ahead and run the requested action.", undefined, undefined, undefined, undefined, "queued", undefined, false, false, false, false, undefined, [approvalKey]);
4024
4012
  },
4025
4013
  ...(approvalActions?.onDeny ? { onDeny: approvalActions.onDeny } : {}),
4026
4014
  ...(approvalActions?.onAlwaysAllow
4027
4015
  ? { onAlwaysAllow: approvalActions.onAlwaysAllow }
4028
4016
  : {}),
4029
4017
  }), [
4030
- appendThreadMessage,
4018
+ addToQueue,
4031
4019
  execMode,
4032
4020
  getApprovalResolution,
4033
- markOptimisticRunning,
4034
4021
  approvalActions,
4035
4022
  recordApprovalResolution,
4036
4023
  ]);
@@ -39,4 +39,4 @@ export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR
39
39
  export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registerActionChatRenderer, registerFallbackToolRenderer, registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, registerReservedToolRenderer, registerToolRenderer, resolveToolRenderer, type ActionChatRendererRegistration, type ToolRendererComponent, type ToolRendererContext, type ToolRendererMatch, type ToolRendererProps, type ToolRendererRegistration, } from "../chat/tool-render-registry.js";
40
40
  export * from "../chat/connectors.js";
41
41
  export * from "../chat/runtime.js";
42
- 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 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";
42
+ 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";
@@ -85,6 +85,16 @@ const LARGE_INPUT_TOOL_NAMES = new Set([
85
85
  ]);
86
86
  const MAX_HISTORY_LARGE_TOOL_ARGS_CHARS = 200_000;
87
87
  const STARTUP_RESPONSE_TIMEOUT_MS = 45_000;
88
+ function dispatchTerminalChatUiCleanup(tabId) {
89
+ if (typeof window === "undefined")
90
+ return;
91
+ // Some terminal outcomes are synthesized by the adapter after recovery
92
+ // (for example, a completed tool whose final response timed out) and never
93
+ // pass through a terminal SSE frame. Clear both halves of the status UI so
94
+ // the wrap-up result cannot leave a preparation label or "Resuming" state.
95
+ window.dispatchEvent(new CustomEvent("agent-chat:activity-clear", { detail: { tabId } }));
96
+ window.dispatchEvent(new CustomEvent("agent-chat:stream-progress", { detail: { tabId } }));
97
+ }
88
98
  // ── Background follow mode ──────────────────────────────────────────────────
89
99
  // For server-continued runs (dispatchMode starts with "background" or equals
90
100
  // "foreground-self-chain") the SERVER normally chains continuation chunks
@@ -3526,6 +3536,7 @@ export function createAgentChatAdapter(options) {
3526
3536
  }
3527
3537
  finally {
3528
3538
  if (typeof window !== "undefined") {
3539
+ dispatchTerminalChatUiCleanup(tabId);
3529
3540
  window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
3530
3541
  detail: { isRunning: false, tabId },
3531
3542
  }));
@@ -42,7 +42,7 @@ export interface AgentChatMessage {
42
42
  model?: string;
43
43
  /** Engine preference paired with model for cross-provider switches. */
44
44
  engine?: string;
45
- /** Reasoning effort preference paired with model. */
45
+ /** Effort preference paired with model. */
46
46
  effort?: ReasoningEffort;
47
47
  /**
48
48
  * Execution mode for this submitted turn. When omitted, sendToAgentChat
@@ -264,6 +264,8 @@ function rememberStreamingText(resetKey, targetText, visibleText) {
264
264
  if (oldestKey !== undefined)
265
265
  smoothStreamingTextCache.delete(oldestKey);
266
266
  }
267
+ /** Stable placeholder so the grapheme ref stays `string[]` before it is seeded. */
268
+ const EMPTY_GRAPHEMES = [];
267
269
  export function useSmoothStreamingText(targetText, streaming, resetKey) {
268
270
  const prefersReducedMotion = usePrefersReducedMotion();
269
271
  const [visibleText, setVisibleText] = useState(() => {
@@ -276,9 +278,19 @@ export function useSmoothStreamingText(targetText, streaming, resetKey) {
276
278
  return sliceGraphemes(targetText, graphemes, initialSmoothStreamingGraphemeCount(graphemes));
277
279
  });
278
280
  const visibleTextRef = useRef(visibleText);
279
- const visibleCountRef = useRef(splitStreamingTextGraphemes(visibleText).length);
280
281
  const targetTextRef = useRef(targetText);
281
- const targetGraphemesRef = useRef(splitStreamingTextGraphemes(targetText));
282
+ // `useRef(expr)` evaluates `expr` on EVERY render and throws it away after
283
+ // mount. Segmenting here therefore ran a full Intl.Segmenter pass per commit
284
+ // — up to 60Hz per streaming message — and, because `visibleText` is a strict
285
+ // prefix of `targetText`, that pass could never hit the incremental path and
286
+ // evicted the shared segmenter cache for every other live stream too. Seed
287
+ // once; the streaming effect below keeps both refs authoritative after that.
288
+ const visibleCountRef = useRef(-1);
289
+ const targetGraphemesRef = useRef(EMPTY_GRAPHEMES);
290
+ if (visibleCountRef.current < 0) {
291
+ targetGraphemesRef.current = splitStreamingTextGraphemes(targetText);
292
+ visibleCountRef.current = splitStreamingTextGraphemes(visibleText).length;
293
+ }
282
294
  const frameRef = useRef(null);
283
295
  const lastCommitAtRef = useRef(0);
284
296
  const pauseUntilRef = useRef(0);
@@ -49,15 +49,19 @@ export declare function shouldOfferRestore(args: {
49
49
  * messages put it at `metadata.runId`.
50
50
  */
51
51
  export declare function assistantMessageRunId(message: unknown): string | undefined;
52
+ export declare function resolveAssistantRequestId(message: unknown, activeRun: {
53
+ threadId: string;
54
+ runId: string;
55
+ } | null, threadId: string): string | undefined;
52
56
  export declare function MessageBranchPicker(): React.JSX.Element;
53
- export declare function MessageActionsMenu({ showRevert, onRevert, }?: {
57
+ export declare function MessageActionsMenu({ showRevert, onRevert, threadId, }?: {
54
58
  showRevert?: boolean;
55
59
  onRevert?: () => void;
60
+ threadId?: string;
56
61
  }): React.JSX.Element;
57
62
  export declare function UserMessage(): React.JSX.Element | null;
58
63
  export declare function messageTextFromContent(content: unknown): string;
59
64
  export declare function isMissingFinalResponseWarningText(text: string): boolean;
60
- export declare function completedAssistantToolNamesAfterLastText(content: readonly ContentPart[]): string[];
61
65
  export declare function latestUserMessageText(messages: readonly unknown[]): string;
62
66
  export declare function userMessageTextBeforeAssistant(messages: readonly unknown[], assistantMessageId: string): string;
63
67
  export declare function assistantMessageHasUnresolvedTool(content: unknown): boolean;
@@ -14,7 +14,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel,
14
14
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
15
15
  import { ThumbsFeedback } from "../observability/ThumbsFeedback.js";
16
16
  import { McpConnectionSuggestion } from "../resources/McpConnectionSuggestion.js";
17
- import { humanizeToolName, isCallAgentToolCallShadowed, shadowedCallAgentToolCallIds, } from "../tool-display.js";
17
+ import { isCallAgentToolCallShadowed, shadowedCallAgentToolCallIds, } from "../tool-display.js";
18
18
  import { cn } from "../utils.js";
19
19
  import { MarkdownText, renderMarkdownToClipboardHtml, SmoothMarkdownText, } from "./markdown-renderer.js";
20
20
  import { getAssistantRunDurationMs } from "./repo-helpers.js";
@@ -172,6 +172,10 @@ export function assistantMessageRunId(message) {
172
172
  ? metadata.runId
173
173
  : undefined;
174
174
  }
175
+ export function resolveAssistantRequestId(message, activeRun, threadId) {
176
+ return (assistantMessageRunId(message) ||
177
+ (activeRun?.threadId === threadId ? activeRun.runId : undefined));
178
+ }
175
179
  // ─── MessageBranchPicker ──────────────────────────────────────────────────────
176
180
  export function MessageBranchPicker() {
177
181
  return (_jsxs(BranchPickerPrimitive.Root, { hideWhenSingleBranch: true, className: "flex items-center gap-0.5 text-[11px] text-muted-foreground", children: [_jsx(BranchPickerPrimitive.Previous, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Previous branch", className: "flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(IconChevronLeft, { className: "h-3.5 w-3.5" }) }) }), _jsxs("span", { className: "tabular-nums select-none", children: [_jsx(BranchPickerPrimitive.Number, {}), "/", _jsx(BranchPickerPrimitive.Count, {})] }), _jsx(BranchPickerPrimitive.Next, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Next branch", className: "flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40", children: _jsx(IconChevronRight, { className: "h-3.5 w-3.5" }) }) })] }));
@@ -284,7 +288,7 @@ function UserMessageEditComposer() {
284
288
  return (_jsxs(ComposerPrimitive.Root, { className: "flex flex-col gap-2 rounded-lg border border-border bg-background px-3 py-2 shadow-sm", children: [_jsx(ComposerPrimitive.Input, { className: "w-full resize-none bg-transparent text-sm leading-relaxed text-foreground placeholder:text-muted-foreground focus:outline-none", rows: 1, submitMode: "enter" }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx("button", { type: "button", className: "cursor-pointer rounded-md px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: "Cancel" }) }), _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx("button", { type: "submit", className: "cursor-pointer rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background hover:opacity-90 disabled:opacity-50", children: "Save" }) })] })] }));
285
289
  }
286
290
  // ─── MessageActionsMenu ────────────────────────────────────────────────────────
287
- export function MessageActionsMenu({ showRevert, onRevert, } = {}) {
291
+ export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}) {
288
292
  const [open, setOpen] = useState(false);
289
293
  const [copied, setCopied] = useState(null);
290
294
  const messageRuntime = useMessageRuntime();
@@ -311,24 +315,23 @@ export function MessageActionsMenu({ showRevert, onRevert, } = {}) {
311
315
  }, [messageRuntime]);
312
316
  const handleCopyRequestId = useCallback(() => {
313
317
  const m = messageRuntime.getState();
314
- // If the message carries no run id (e.g. the run is still in flight and
315
- // this is the first message), fall back to the active-run state so a hung /
316
- // mid-stream chat still surfaces a usable trace ID. Last resort is the
317
- // assistant-ui local message id.
318
- const runId = assistantMessageRunId(m) ||
319
- (typeof window !== "undefined" ? getActiveRun()?.runId : null) ||
320
- m.id ||
321
- "";
318
+ const runId = resolveAssistantRequestId(m, getActiveRun(), threadId);
319
+ if (!runId) {
320
+ setCopied("id-unavailable");
321
+ return;
322
+ }
322
323
  void writeClipboardText(runId).then((ok) => {
323
- if (!ok)
324
+ if (!ok) {
325
+ setCopied("id-failed");
324
326
  return;
327
+ }
325
328
  setCopied("id");
326
329
  setTimeout(() => {
327
330
  setCopied(null);
328
331
  setOpen(false);
329
332
  }, 1000);
330
333
  });
331
- }, [messageRuntime]);
334
+ }, [messageRuntime, threadId]);
332
335
  const handleForkChat = useCallback(() => {
333
336
  setOpen(false);
334
337
  actionsCtx?.onForkChat?.();
@@ -343,7 +346,13 @@ export function MessageActionsMenu({ showRevert, onRevert, } = {}) {
343
346
  }, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message" ? "Copied!" : "Copy Message"] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
344
347
  e.preventDefault();
345
348
  handleCopyRequestId();
346
- }, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id" ? "Copied!" : "Copy Request ID"] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), "Revert to here"] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: ["Sent ", timestamp.short] })] }))] })] }));
349
+ }, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id"
350
+ ? "Copied!"
351
+ : copied === "id-unavailable"
352
+ ? "Request ID unavailable"
353
+ : copied === "id-failed"
354
+ ? "Copy failed"
355
+ : "Copy Request ID"] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), "Revert to here"] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: ["Sent ", timestamp.short] })] }))] })] }));
347
356
  }
348
357
  // ─── UserMessage ──────────────────────────────────────────────────────────────
349
358
  export function UserMessage() {
@@ -481,29 +490,6 @@ function missingFinalResponseWarningText(content) {
481
490
  }
482
491
  return null;
483
492
  }
484
- export function completedAssistantToolNamesAfterLastText(content) {
485
- let lastTextIndex = -1;
486
- for (let index = content.length - 1; index >= 0; index -= 1) {
487
- if (content[index]?.type === "text") {
488
- lastTextIndex = index;
489
- break;
490
- }
491
- }
492
- const names = new Set();
493
- for (let index = lastTextIndex + 1; index < content.length; index += 1) {
494
- const part = content[index];
495
- if (part?.type !== "tool-call" ||
496
- part.activity === true ||
497
- part.result === undefined ||
498
- part.isError === true ||
499
- part.outcome === "unknown" ||
500
- isCallAgentToolCallShadowed(content, index)) {
501
- continue;
502
- }
503
- names.add(humanizeToolName(part.toolName));
504
- }
505
- return [...names];
506
- }
507
493
  export function latestUserMessageText(messages) {
508
494
  for (let index = messages.length - 1; index >= 0; index -= 1) {
509
495
  const message = messages[index];
@@ -781,11 +767,6 @@ export function InlineRunErrorNotice({ info, durationMs, onRetry, }) {
781
767
  : headline;
782
768
  return (_jsxs("div", { className: "my-1 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex cursor-pointer items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconAlertTriangle, { className: "size-3.5 shrink-0 text-amber-500" }), _jsx("span", { className: "truncate", children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), open && (_jsxs("div", { className: "mt-1 rounded-md border border-border/60 bg-background/70 p-2 text-[11px] leading-relaxed text-muted-foreground", children: [_jsx("p", { className: "whitespace-pre-wrap break-words", children: info.message }), info.errorCode && (_jsxs("div", { className: "mt-1 font-mono", children: ["code: ", info.errorCode] })), info.runId && _jsxs("div", { className: "font-mono", children: ["run: ", info.runId] }), info.details && (_jsx("pre", { className: "mt-1 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details })), onRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "mt-2 inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { className: "size-3" }), "Retry"] }))] }))] }));
783
769
  }
784
- function AssistantCompletionProgress({ messageId, toolNames, }) {
785
- if (toolNames.length === 0)
786
- return null;
787
- return (_jsx("div", { className: "my-1 w-full text-[13px] text-muted-foreground", role: "status", "aria-live": "polite", "data-testid": "assistant-completion-progress", children: _jsx(SmoothMarkdownText, { text: `The agent completed these actions: ${toolNames.join(", ")}`, streaming: true, resetKey: `assistant-completion-progress:${messageId}`, statusType: "running" }) }));
788
- }
789
770
  function MissingFinalResponseNotice({ messageId, text, animate, onRevealComplete, }) {
790
771
  return (_jsx("div", { className: "my-1 w-full text-muted-foreground", role: "status", "aria-live": "polite", "data-testid": "missing-final-response", children: _jsx(SmoothMarkdownText, { text: text, streaming: animate, resetKey: `missing-final-response:${messageId}`, statusType: animate ? "running" : "complete", onRevealComplete: animate ? onRevealComplete : undefined }) }));
791
772
  }
@@ -849,9 +830,6 @@ export function AssistantMessage() {
849
830
  return;
850
831
  setRevealedMissingFinalResponseKey(missingFinalResponseAnimationKey);
851
832
  }, [missingFinalResponseAnimationKey]);
852
- const completedToolNames = isLast && chatRunning && !hasCompletedCustomUi && Array.isArray(msg.content)
853
- ? completedAssistantToolNamesAfterLastText(msg.content)
854
- : [];
855
833
  const shouldHoldCompletionFooter = isLast &&
856
834
  ((missingFinalResponseCandidate && !showMissingFinalResponse) ||
857
835
  (animateMissingFinalResponse && !missingFinalResponseRevealed));
@@ -1004,15 +982,15 @@ export function AssistantMessage() {
1004
982
  case "tool-call":
1005
983
  if (shadowedToolCallIds.has(part.toolCallId))
1006
984
  return null;
1007
- return part.toolUI ? (_jsx(ToolActivityPresentation, { toolName: part.toolName, isRunning: part.status?.type === "running", isActiveTail: part.toolCallId === activeTailToolCallId, toolCallId: part.toolCallId, children: part.toolUI })) : (_jsx(ToolCallFallback, { ...part, isActiveTail: part.toolCallId === activeTailToolCallId }));
985
+ return part.toolUI ? (_jsx(ToolActivityPresentation, { toolName: part.toolName, isRunning: part.status?.type === "running", toolCallId: part.toolCallId, children: part.toolUI })) : (_jsx(ToolCallFallback, { ...part, isActiveTail: part.toolCallId === activeTailToolCallId }));
1008
986
  default:
1009
987
  return null;
1010
988
  }
1011
- } }) }), completedToolNames.length > 0 && !showInlineRunError && (_jsx(AssistantCompletionProgress, { messageId: msg.id, toolNames: completedToolNames })), showInlineRunError && messageRunError && (_jsx(InlineRunErrorNotice, { info: messageRunError, durationMs: capturedDurationMs ?? persistedDurationMs, onRetry: isLast && messageRunError.recoverable
989
+ } }) }), showInlineRunError && messageRunError && (_jsx(InlineRunErrorNotice, { info: messageRunError, durationMs: capturedDurationMs ?? persistedDurationMs, onRetry: isLast && messageRunError.recoverable
1012
990
  ? messageActions?.onRetryRunError
1013
991
  : undefined })), missingWarningText == null &&
1014
992
  missingFinalResponseNoticeText != null &&
1015
- !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent })), isComplete && (_jsx(McpConnectionSuggestion, { text: responseConnectionText, contextText: responseConnectionContext, variant: "response" }))] }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore }), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Regenerate response", className: `flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-accent hover:text-foreground ${messageFooterFadeClassName} disabled:cursor-not-allowed disabled:opacity-40`, children: _jsx(IconRefresh, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: "Regenerate response" })] }) })), _jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: "Restore to here?" }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Cancel" })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), "Restoring..."] })) : restoreState === "error" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: restoreError }), _jsx("button", { onClick: cancelRestore, className: "cursor-pointer rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Dismiss" })] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: thread.messages.findIndex((m) => m.id === msg.id) }))] }))] }));
993
+ !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent })), isComplete && (_jsx(McpConnectionSuggestion, { text: responseConnectionText, contextText: responseConnectionContext, variant: "response" }))] }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore, threadId: cpCtx?.threadId ?? "" }), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Regenerate response", className: `flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-accent hover:text-foreground ${messageFooterFadeClassName} disabled:cursor-not-allowed disabled:opacity-40`, children: _jsx(IconRefresh, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: "Regenerate response" })] }) })), _jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: "Restore to here?" }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Cancel" })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), "Restoring..."] })) : restoreState === "error" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: restoreError }), _jsx("button", { onClick: cancelRestore, className: "cursor-pointer rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Dismiss" })] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: thread.messages.findIndex((m) => m.id === msg.id) }))] }))] }));
1016
994
  }
1017
995
  // ─── RunningActivityStatus / ThinkingIndicator ────────────────────────────────
1018
996
  export function RunningActivityStatus({ label }) {
@@ -8,9 +8,8 @@ export declare const ChatRunningContext: React.Context<boolean>;
8
8
  export declare const ChatRunDurationContext: React.Context<number | null>;
9
9
  export declare const ASSISTANT_VISIBLE_TOOL_CALL_LIMIT = 3;
10
10
  /**
11
- * Keeps the visible tool stack spatially continuous as streaming calls change
12
- * which rows belong to the collapsed history bucket. The rendered data stays
13
- * untouched; this only animates the DOM's before/after positions.
11
+ * Keeps the tool-call stack layout-transparent. Tool-entry motion is disabled
12
+ * until it can stay stable while streaming calls are added and summarized.
14
13
  */
15
14
  export declare function ToolCallStackMotion({ children, className, }: {
16
15
  children: React.ReactNode;
@@ -55,10 +54,9 @@ export declare function toolCallHasPendingApproval(part: {
55
54
  } | null;
56
55
  }): boolean;
57
56
  export declare const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45000;
58
- export declare function ToolActivityPresentation({ toolName, isRunning, isActiveTail, toolCallId, suppressLongRunningHint, children, }: {
57
+ export declare function ToolActivityPresentation({ toolName, isRunning, toolCallId, suppressLongRunningHint, children, }: {
59
58
  toolName: string;
60
59
  isRunning: boolean;
61
- isActiveTail: boolean;
62
60
  toolCallId?: string;
63
61
  suppressLongRunningHint?: boolean;
64
62
  children: React.ReactNode;