@agent-native/core 0.149.0 → 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 (134) 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 +139 -10
  35. package/corpus/templates/slides/actions/save-deck.ts +24 -1
  36. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +46 -0
  37. package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +194 -3
  38. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +10 -1
  39. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
  40. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +74 -63
  41. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
  42. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
  43. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
  44. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
  45. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -7
  46. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +45 -0
  47. package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
  48. package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
  49. package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
  50. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
  51. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
  52. package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
  53. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  54. package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
  55. package/corpus/templates/slides/app/lib/export-pdf-client.ts +84 -25
  56. package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +11 -0
  57. package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
  58. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
  59. package/corpus/templates/slides/app/pages/DeckEditor.tsx +16 -27
  60. package/corpus/templates/slides/app/pages/Index.tsx +256 -98
  61. package/corpus/templates/slides/changelog/2026-08-08-google-slides-export-now-tells-you-why-a-deck-fell-back-to-a.md +6 -0
  62. package/corpus/templates/slides/changelog/2026-08-09-delete-key-now-removes-a-selected-image.md +6 -0
  63. package/corpus/templates/slides/changelog/2026-08-09-editing-a-slide-in-an-imported-deck-no-longer-fails-to-save.md +6 -0
  64. package/corpus/templates/slides/changelog/2026-08-09-fixed-pdf-and-custom-size-powerpoint-imports-rendering-with-.md +6 -0
  65. package/corpus/templates/slides/changelog/2026-08-09-images-from-sites-without-cors-no-longer-export-blank.md +6 -0
  66. package/corpus/templates/slides/changelog/2026-08-09-imported-pdf-headings-no-longer-lose-the-space-between-color.md +6 -0
  67. package/corpus/templates/slides/changelog/2026-08-09-imported-slide-text-no-longer-renders-too-large-for-its-box.md +6 -0
  68. package/corpus/templates/slides/changelog/2026-08-09-large-decks-no-longer-render-slowly-or-glitch-in-the-editor.md +6 -0
  69. package/corpus/templates/slides/changelog/2026-08-09-revise-highlighted-slide-text-with-ai.md +6 -0
  70. package/corpus/templates/slides/changelog/2026-08-09-the-deck-editor-no-longer-stays-dimmed-after-the-window-narro.md +6 -0
  71. package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
  72. package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
  73. package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
  74. package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
  75. package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
  76. package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
  77. package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
  78. package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
  79. package/corpus/templates/slides/server/handlers/import/html-converter.ts +115 -22
  80. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +11 -2
  81. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  82. package/corpus/templates/slides/server/lib/fetch-remote-image.ts +192 -0
  83. package/corpus/templates/slides/server/lib/image-proxy-url.ts +192 -0
  84. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
  85. package/corpus/templates/slides/server/routes/api/image-proxy.get.ts +65 -0
  86. package/corpus/templates/slides/shared/api.ts +2 -1
  87. package/corpus/templates/slides/shared/deck-title.ts +135 -0
  88. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
  89. package/dist/agent/engine/ai-sdk-engine.js +1 -1
  90. package/dist/agent/production-agent.d.ts +21 -1
  91. package/dist/agent/production-agent.js +129 -10
  92. package/dist/agent/tool-approval-store.d.ts +18 -0
  93. package/dist/agent/tool-approval-store.js +122 -0
  94. package/dist/agent/types.d.ts +1 -1
  95. package/dist/cli/code-agent-executor.js +11 -2
  96. package/dist/client/AgentPanel.js +31 -6
  97. package/dist/client/AssistantChat.d.ts +3 -2
  98. package/dist/client/AssistantChat.js +7 -20
  99. package/dist/client/agent-chat/index.d.ts +1 -1
  100. package/dist/client/agent-chat-adapter.js +11 -0
  101. package/dist/client/agent-chat.d.ts +1 -1
  102. package/dist/client/chat/markdown-renderer.js +14 -2
  103. package/dist/client/chat/message-components.d.ts +6 -2
  104. package/dist/client/chat/message-components.js +25 -47
  105. package/dist/client/chat/tool-call-display.d.ts +3 -5
  106. package/dist/client/chat/tool-call-display.js +7 -212
  107. package/dist/client/chat-first/apps-rail.js +3 -3
  108. package/dist/client/chat-first/browser-pane.js +4 -1
  109. package/dist/client/chat-first/chat-history.d.ts +3 -1
  110. package/dist/client/chat-first/chat-history.js +2 -2
  111. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  112. package/dist/client/chat-first/surface-tabs.js +52 -41
  113. package/dist/client/chat-first/types.d.ts +3 -0
  114. package/dist/client/chat-first.d.ts +10 -0
  115. package/dist/client/chat-first.js +37 -2
  116. package/dist/client/components/ui/message-scroller.js +4 -1
  117. package/dist/client/i18n.js +3 -1
  118. package/dist/client/progress/RunsTray.js +14 -1
  119. package/dist/client/resources/McpConnectionSuggestion.js +9 -1
  120. package/dist/client/sse-event-processor.js +10 -0
  121. package/dist/client/use-chat-models.js +156 -80
  122. package/dist/integrations/webhook-handler.js +1 -1
  123. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  124. package/dist/secrets/routes.d.ts +6 -6
  125. package/dist/server/auth.js +262 -31
  126. package/dist/server/onboarding-html.js +56 -9
  127. package/dist/shared/reasoning-effort.d.ts +8 -8
  128. package/dist/shared/reasoning-effort.js +10 -10
  129. package/dist/shared/streaming-text-smoothing.js +41 -11
  130. package/dist/styles/agent-native.css +19 -32
  131. package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  132. package/docs/content/template-chat.mdx +1 -1
  133. package/package.json +2 -2
  134. package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -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;
@@ -20,207 +20,12 @@ import { isBuiltinDataWidgetActionRenderer, resolveBuiltinActionChatRenderer, re
20
20
  export const ChatRunningContext = React.createContext(false);
21
21
  export const ChatRunDurationContext = React.createContext(null);
22
22
  export const ASSISTANT_VISIBLE_TOOL_CALL_LIMIT = 3;
23
- const TOOL_CALL_ENTRY_DURATION_MS = 220;
24
- const TOOL_CALL_STACK_MOTION_DURATION_MS = 220;
25
- const TOOL_CALL_STACK_EASING = "cubic-bezier(0.23, 1, 0.32, 1)";
26
- function toolStackMotionKey(element, index) {
27
- const toolCallId = element.dataset.agentToolCallId;
28
- if (toolCallId)
29
- return `tool:${toolCallId}`;
30
- const summaryId = element.dataset.agentToolSummary;
31
- if (summaryId)
32
- return `summary:${summaryId}`;
33
- return `anonymous:${index}`;
34
- }
35
- function prefersReducedMotionForToolStack() {
36
- return (typeof window !== "undefined" &&
37
- typeof window.matchMedia === "function" &&
38
- window.matchMedia("(prefers-reduced-motion: reduce)").matches);
39
- }
40
- function startToolStackAnimation(element, keyframes) {
41
- if (typeof element.animate !== "function")
42
- return null;
43
- return element.animate(keyframes, {
44
- duration: TOOL_CALL_STACK_MOTION_DURATION_MS,
45
- easing: TOOL_CALL_STACK_EASING,
46
- fill: "both",
47
- });
48
- }
49
- function prepareToolStackExitClone(clone, snapshot) {
50
- clone.classList.remove("agent-tool-call--entering", "agent-tool-call--stack-entering");
51
- clone
52
- .querySelectorAll(".agent-tool-call--entering, .agent-tool-call--stack-entering")
53
- .forEach((element) => {
54
- element.classList.remove("agent-tool-call--entering", "agent-tool-call--stack-entering");
55
- });
56
- clone.classList.add("agent-tool-call-stack__exit");
57
- clone.setAttribute("aria-hidden", "true");
58
- clone.removeAttribute("data-agent-tool-call-id");
59
- clone.removeAttribute("data-agent-tool-summary");
60
- clone.removeAttribute("data-agent-tool-motion-token");
61
- clone.style.left = `${snapshot.left}px`;
62
- clone.style.top = `${snapshot.top}px`;
63
- clone.style.width = `${snapshot.width}px`;
64
- clone.style.height = `${snapshot.height}px`;
65
- clone.style.transform = "translate3d(0, 0, 0)";
66
- clone.style.transition = "none";
67
- clone.style.animation = "none";
68
- clone.style.opacity = "1";
69
- }
70
23
  /**
71
- * Keeps the visible tool stack spatially continuous as streaming calls change
72
- * which rows belong to the collapsed history bucket. The rendered data stays
73
- * untouched; this only animates the DOM's before/after positions.
24
+ * Keeps the tool-call stack layout-transparent. Tool-entry motion is disabled
25
+ * until it can stay stable while streaming calls are added and summarized.
74
26
  */
75
27
  export function ToolCallStackMotion({ children, className, }) {
76
- const stackRef = useRef(null);
77
- const previousRef = useRef(new Map());
78
- const previousStructureRef = useRef([]);
79
- const activeMotionsRef = useRef(new Map());
80
- const exitAnimationsRef = useRef(new Map());
81
- useLayoutEffect(() => {
82
- const stack = stackRef.current;
83
- if (!stack)
84
- return;
85
- const previous = previousRef.current;
86
- const visualPrevious = new Map(previous);
87
- for (const [key, activeMotion] of activeMotionsRef.current) {
88
- if (stack.contains(activeMotion.element)) {
89
- const previousSnapshot = previous.get(key);
90
- if (previousSnapshot) {
91
- const rect = activeMotion.element.getBoundingClientRect();
92
- visualPrevious.set(key, {
93
- ...previousSnapshot,
94
- top: rect.top,
95
- left: rect.left,
96
- });
97
- }
98
- }
99
- activeMotion.animation.cancel();
100
- }
101
- activeMotionsRef.current.clear();
102
- for (const [ghost, animation] of exitAnimationsRef.current) {
103
- animation.cancel();
104
- ghost.remove();
105
- }
106
- exitAnimationsRef.current.clear();
107
- const elements = Array.from(stack.querySelectorAll("[data-agent-tool-call-id], [data-agent-tool-summary]"));
108
- const structure = elements.map((element, index) => toolStackMotionKey(element, index));
109
- const previousStructure = previousStructureRef.current;
110
- const structureChanged = structure.length !== previousStructure.length ||
111
- structure.some((key, index) => key !== previousStructure[index]);
112
- previousStructureRef.current = structure;
113
- const current = new Map();
114
- for (const [index, element] of elements.entries()) {
115
- const rect = element.getBoundingClientRect();
116
- current.set(toolStackMotionKey(element, index), {
117
- top: rect.top,
118
- left: rect.left,
119
- width: rect.width,
120
- height: rect.height,
121
- clone: element.cloneNode(true),
122
- });
123
- }
124
- previousRef.current = current;
125
- // Entry motion changes the measured transform without changing the stack
126
- // structure. Do not turn that animation's cleanup into a second FLIP pass.
127
- if (previous.size === 0 ||
128
- !structureChanged ||
129
- prefersReducedMotionForToolStack())
130
- return;
131
- const summary = elements.find((element) => element.dataset.agentToolSummary !== undefined);
132
- const summaryRect = summary?.getBoundingClientRect();
133
- const elementsByKey = new Map(elements.map((element, index) => [
134
- toolStackMotionKey(element, index),
135
- element,
136
- ]));
137
- for (const [key, after] of current.entries()) {
138
- const before = visualPrevious.get(key);
139
- if (!before) {
140
- const node = elementsByKey.get(key);
141
- if (!node)
142
- continue;
143
- if (node.dataset.agentToolSummary !== undefined)
144
- continue;
145
- if (node.dataset.agentToolCallId !== undefined &&
146
- !node.classList.contains("agent-tool-call--entering")) {
147
- node.classList.add("agent-tool-call--stack-entering");
148
- window.setTimeout(() => {
149
- node.classList.remove("agent-tool-call--stack-entering");
150
- }, TOOL_CALL_ENTRY_DURATION_MS);
151
- }
152
- continue;
153
- }
154
- const dx = before.left - after.left;
155
- const dy = before.top - after.top;
156
- if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5)
157
- continue;
158
- const node = elementsByKey.get(key);
159
- if (!node)
160
- continue;
161
- if (node.dataset.agentToolSummary !== undefined)
162
- continue;
163
- const animation = startToolStackAnimation(node, [
164
- { transform: `translate3d(${dx}px, ${dy}px, 0)` },
165
- { transform: "translate3d(0, 0, 0)" },
166
- ]);
167
- if (!animation)
168
- continue;
169
- activeMotionsRef.current.set(key, { element: node, animation });
170
- const clearMotion = () => {
171
- if (activeMotionsRef.current.get(key)?.animation === animation) {
172
- activeMotionsRef.current.delete(key);
173
- }
174
- };
175
- animation.onfinish = clearMotion;
176
- animation.oncancel = clearMotion;
177
- }
178
- for (const [key, before] of visualPrevious.entries()) {
179
- if (current.has(key) || !key.startsWith("tool:"))
180
- continue;
181
- const ghost = before.clone;
182
- prepareToolStackExitClone(ghost, before);
183
- document.body?.appendChild(ghost);
184
- if (!ghost.isConnected)
185
- continue;
186
- const targetTop = summaryRect?.top ?? before.top - 8;
187
- const targetLeft = summaryRect?.left ?? before.left;
188
- const animation = startToolStackAnimation(ghost, [
189
- { transform: "translate3d(0, 0, 0)", opacity: 1 },
190
- {
191
- transform: `translate3d(${targetLeft - before.left}px, ${targetTop - before.top}px, 0)`,
192
- opacity: 0,
193
- },
194
- ]);
195
- if (!animation) {
196
- ghost.remove();
197
- continue;
198
- }
199
- exitAnimationsRef.current.set(ghost, animation);
200
- const clearExit = () => {
201
- if (exitAnimationsRef.current.get(ghost) === animation) {
202
- exitAnimationsRef.current.delete(ghost);
203
- }
204
- ghost.remove();
205
- };
206
- animation.onfinish = clearExit;
207
- animation.oncancel = clearExit;
208
- }
209
- });
210
- useEffect(() => {
211
- return () => {
212
- for (const { animation } of activeMotionsRef.current.values()) {
213
- animation.cancel();
214
- }
215
- activeMotionsRef.current.clear();
216
- for (const [ghost, animation] of exitAnimationsRef.current) {
217
- animation.cancel();
218
- ghost.remove();
219
- }
220
- exitAnimationsRef.current.clear();
221
- };
222
- }, []);
223
- return (_jsx("div", { ref: stackRef, className: cn("agent-tool-call-stack", className), children: children }));
28
+ return (_jsx("div", { className: cn("agent-tool-call-stack", className), children: children }));
224
29
  }
225
30
  export const ApprovalContext = React.createContext(null);
226
31
  /** Pending human-in-the-loop gate still waiting for Approve/Deny. */
@@ -231,18 +36,8 @@ export function toolCallHasPendingApproval(part) {
231
36
  approval.dismissed !== true);
232
37
  }
233
38
  export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
234
- export function ToolActivityPresentation({ toolName, isRunning, isActiveTail, toolCallId, suppressLongRunningHint = false, children, }) {
39
+ export function ToolActivityPresentation({ toolName, isRunning, toolCallId, suppressLongRunningHint = false, children, }) {
235
40
  const [showLongRunningHint, setShowLongRunningHint] = useState(false);
236
- // A batched update can first reveal a tool with its result already attached.
237
- // Presentation follows the active chat tail rather than execution state so
238
- // that newly revealed completed tools still get their entrance motion.
239
- const [animateEntry, setAnimateEntry] = useState(isActiveTail);
240
- useEffect(() => {
241
- if (!animateEntry)
242
- return;
243
- const timeout = window.setTimeout(() => setAnimateEntry(false), TOOL_CALL_ENTRY_DURATION_MS);
244
- return () => window.clearTimeout(timeout);
245
- }, [animateEntry]);
246
41
  useEffect(() => {
247
42
  if (!isRunning || suppressLongRunningHint) {
248
43
  setShowLongRunningHint(false);
@@ -254,7 +49,7 @@ export function ToolActivityPresentation({ toolName, isRunning, isActiveTail, to
254
49
  }, TOOL_LONG_RUNNING_HINT_DELAY_MS);
255
50
  return () => window.clearTimeout(timeout);
256
51
  }, [isRunning, suppressLongRunningHint, toolName]);
257
- return (_jsx("div", { className: cn("agent-tool-call", animateEntry && "agent-tool-call--entering"), "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: "Still working. Large updates can take a minute or two." }))] }) }));
52
+ return (_jsx("div", { className: "agent-tool-call", "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: "Still working. Large updates can take a minute or two." }))] }) }));
258
53
  }
259
54
  function stringifyToolValue(value, pretty = false) {
260
55
  if (typeof value === "string")
@@ -480,7 +275,7 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
480
275
  // These must be separate components so hook order in ToolCallDisplayGeneric
481
276
  // is always stable (no conditional hook calls).
482
277
  const toolKind = structuredMeta?.toolKind;
483
- const wrapToolDisplay = (children) => (_jsx(ToolActivityPresentation, { toolName: toolName, isRunning: isRunning, isActiveTail: showActiveTail, toolCallId: toolCallId, suppressLongRunningHint: toolName === "call-agent" || toolName.startsWith("agent:"), children: children }));
278
+ const wrapToolDisplay = (children) => (_jsx(ToolActivityPresentation, { toolName: toolName, isRunning: isRunning, toolCallId: toolCallId, suppressLongRunningHint: toolName === "call-agent" || toolName.startsWith("agent:"), children: children }));
484
279
  if (toolKind === "bash") {
485
280
  return wrapToolDisplay(_jsx(BashCell, { meta: structuredMeta, output: result, isRunning: isRunning }));
486
281
  }
@@ -642,7 +437,7 @@ function AgentCallCell({ agentName, activity, progress, responseText, isRunning,
642
437
  function AgentActivityToolCallRow({ tool, isActiveTail, }) {
643
438
  const isRunning = tool.status === "running";
644
439
  const ToolIcon = resolveToolIcon(tool.name);
645
- return (_jsx(ToolActivityPresentation, { toolName: tool.name, isRunning: isRunning, isActiveTail: isActiveTail, toolCallId: tool.id, suppressLongRunningHint: true, children: _jsxs("div", { className: "my-0.5 flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground", children: [_jsx("span", { className: "flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : (_jsx(ToolIcon, { className: "size-3.5" })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: humanizeToolName(tool.name) })] }) }));
440
+ return (_jsx(ToolActivityPresentation, { toolName: tool.name, isRunning: isRunning, toolCallId: tool.id, suppressLongRunningHint: true, children: _jsxs("div", { className: "my-0.5 flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground", children: [_jsx("span", { className: "flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : (_jsx(ToolIcon, { className: "size-3.5" })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: humanizeToolName(tool.name) })] }) }));
646
441
  }
647
442
  // ─── ToolCallFallback ──────────────────────────────────────────────────────────
648
443
  export function ToolCallFallback({ toolName, toolCallId, args, argsText, result, ...rest }) {
@@ -15,12 +15,12 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
15
15
  const active = activeAppId === app.id;
16
16
  const pinned = layout.pinnedIds.includes(app.id);
17
17
  const index = orderedApps.indexOf(app);
18
- return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-8 w-full min-w-0 items-center gap-1 rounded-md px-1 text-sm", active
18
+ return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-8 w-full min-w-0 items-center gap-1 rounded-md px-0 text-sm", active
19
19
  ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
20
20
  : "text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onDragStart: (event) => {
21
21
  event.dataTransfer.effectAllowed = "move";
22
22
  onDragStart(app.id);
23
- }, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex h-full min-w-0 flex-1 items-center gap-2 px-1 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
23
+ }, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex h-full min-w-0 flex-1 items-center gap-2 px-2 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
24
24
  if (!event.altKey)
25
25
  return;
26
26
  if (event.key === "ArrowUp") {
@@ -99,5 +99,5 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
99
99
  ? "bg-sidebar-accent text-sidebar-accent-foreground"
100
100
  : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
101
101
  }
102
- return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
102
+ return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-0 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-2 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
103
103
  });
@@ -17,6 +17,9 @@ export function ChatFirstBrowserPane({ url, title, status, statusMessage, onClos
17
17
  setHistoryIndex(0);
18
18
  setNavigationError(null);
19
19
  }, [url]);
20
+ const currentTarget = resolveChatFirstBrowserTarget({ url: currentUrl });
21
+ const isExternalOnly = currentTarget.status === "ready" &&
22
+ currentTarget.target.openExternally === true;
20
23
  function navigate(event) {
21
24
  event.preventDefault();
22
25
  const target = resolveChatFirstBrowserTarget({ url: draftUrl });
@@ -56,7 +59,7 @@ export function ChatFirstBrowserPane({ url, title, status, statusMessage, onClos
56
59
  }
57
60
  return (_jsxs("section", { "data-chat-first-browser-pane": true, className: "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background", "aria-label": copy("surface.browser.label"), children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-1.5 border-b border-border bg-card px-2", children: [_jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [_jsx("button", { type: "button", disabled: historyIndex <= 0, onClick: goBack, "aria-label": copy("browserBack"), title: copy("browserBack"), className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-35", children: _jsx(IconArrowLeft, { size: 15 }) }), _jsx("button", { type: "button", disabled: historyIndex >= history.length - 1, onClick: goForward, "aria-label": copy("browserForward"), title: copy("browserForward"), className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-35", children: _jsx(IconArrowRight, { size: 15 }) }), _jsx("button", { type: "button", onClick: () => setReloadKey((key) => key + 1), "aria-label": copy("browserReload"), title: copy("browserReload"), className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconRefresh, { size: 15 }) })] }), _jsxs("form", { className: "flex min-w-0 flex-1 items-center gap-1.5 rounded-md border border-input bg-background px-2", onSubmit: navigate, children: [_jsx(IconWorld, { size: 13, className: "shrink-0 text-muted-foreground", "aria-hidden": "true" }), _jsx("input", { value: draftUrl, onChange: (event) => setDraftUrl(event.target.value), "aria-label": copy("browserAddress"), spellCheck: false, className: "h-7 min-w-0 flex-1 bg-transparent text-xs text-foreground outline-none placeholder:text-muted-foreground" })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [title ? (_jsx("span", { className: "hidden max-w-28 truncate px-1 text-xs text-muted-foreground xl:block", title: title, children: title })) : null, _jsx("button", { type: "button", onClick: () => window.open(currentUrl, "_blank", "noopener,noreferrer"), "aria-label": copy("browserOpenExternal"), title: copy("browserOpenExternal"), className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconExternalLink, { size: 15 }) }), _jsx("button", { type: "button", onClick: onClose, "aria-label": copy("browserClose"), title: copy("browserClose"), className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(IconX, { size: 15 }) })] })] }), navigationError ? (_jsx("p", { className: "border-b border-destructive/30 bg-destructive/10 px-3 py-1.5 text-xs text-destructive", role: "alert", children: navigationError })) : null, _jsx("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: status === "starting" || status === "error" ? (_jsx("div", { className: "flex h-full items-center justify-center px-6 text-center", role: status === "error" ? "alert" : "status", "aria-live": "polite", children: _jsxs("div", { className: "max-w-xs space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: status === "starting"
58
61
  ? copy("browserPreviewStarting")
59
- : copy("browserPreviewError") }), statusMessage ? (_jsx("p", { className: "text-xs text-muted-foreground", children: statusMessage })) : null] }) })) : (renderEmbed({
62
+ : copy("browserPreviewError") }), statusMessage ? (_jsx("p", { className: "text-xs text-muted-foreground", children: statusMessage })) : null] }) })) : isExternalOnly ? (_jsx("div", { className: "flex h-full items-center justify-center p-6 text-center", children: _jsxs("div", { className: "max-w-sm space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: copy("browserPreviewError") }), _jsxs("a", { href: currentUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconExternalLink, { size: 14, "aria-hidden": "true" }), copy("browserOpenExternal")] })] }) })) : (renderEmbed({
60
63
  url: currentUrl,
61
64
  title: title || copy("browserPage"),
62
65
  key: `${currentUrl}:${reloadKey}`,
@@ -3,10 +3,12 @@ import { type ChatHistoryItem, type ChatHistoryListProps } from "../chat/ChatHis
3
3
  export interface ChatFirstChatHistoryProps extends Omit<ChatHistoryListProps, "items" | "sections" | "variant"> {
4
4
  items: ChatHistoryItem[];
5
5
  label?: ReactNode;
6
+ /** Optional contextual actions for the Chats section header. */
7
+ headerAction?: ReactNode;
6
8
  }
7
9
  /**
8
10
  * The single chat-list presentation used by the chat-first rail on every
9
11
  * host. Hosts still own thread fetching and actions, but row rhythm and the
10
12
  * Chats section treatment stay identical.
11
13
  */
12
- export declare function ChatFirstChatHistory({ items, label, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element | null;
14
+ export declare function ChatFirstChatHistory({ items, label, headerAction, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element | null;
@@ -5,8 +5,8 @@ import { ChatHistoryList, } from "../chat/ChatHistoryList.js";
5
5
  * host. Hosts still own thread fetching and actions, but row rhythm and the
6
6
  * Chats section treatment stay identical.
7
7
  */
8
- export function ChatFirstChatHistory({ items, label = "Chats", className, ...props }) {
8
+ export function ChatFirstChatHistory({ items, label = "Chats", headerAction, className, ...props }) {
9
9
  if (items.length === 0)
10
10
  return null;
11
- return (_jsxs("section", { "data-chat-first-chat-history": true, className: ["min-h-0 min-w-0", className].filter(Boolean).join(" "), children: [_jsx("p", { className: "mb-1 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: label }), _jsx(ChatHistoryList, { ...props, items: items, variant: "rail", className: "min-w-0" })] }));
11
+ return (_jsxs("section", { "data-chat-first-chat-history": true, className: ["min-h-0 min-w-0", className].filter(Boolean).join(" "), children: [_jsxs("div", { "data-chat-first-chat-history-header": true, className: "group mb-1 flex min-h-6 items-center justify-between gap-2 px-2", children: [_jsx("p", { className: "min-w-0 text-[11px] font-medium text-sidebar-foreground/50", children: label }), headerAction ? (_jsx("div", { className: "shrink-0 opacity-0 transition-opacity duration-100 group-hover:opacity-100 group-focus-within:opacity-100", children: headerAction })) : null] }), _jsx(ChatHistoryList, { ...props, items: items, variant: "rail", className: "min-w-0" })] }));
12
12
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import { type ChatFirstSurfaceTab } from "../chat-first.js";
3
3
  import type { ChatFirstSurfaceTabsProps } from "./types.js";
4
- export declare function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, copy, }: ChatFirstSurfaceTabsProps): import("react").JSX.Element;
4
+ export declare function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, apps, onOpenApp, renderAppIcon, copy, }: ChatFirstSurfaceTabsProps): import("react").JSX.Element;
5
5
  export declare function ChatFirstSurfaceContent({ tabs, activeTabId, renderTab, }: {
6
6
  tabs: readonly ChatFirstSurfaceTab[];
7
7
  activeTabId: string | null;