@agent-native/core 0.155.0 → 0.157.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  2. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  3. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
  4. package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
  5. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +15 -1
  6. package/corpus/templates/clips/app/routes/share.$shareId.tsx +16 -2
  7. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  8. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  9. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  10. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  11. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  12. package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
  13. package/corpus/templates/slides/app/global.css +7 -40
  14. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  15. package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
  16. package/dist/agent/index.d.ts +1 -1
  17. package/dist/agent/production-agent.d.ts +30 -0
  18. package/dist/agent/production-agent.js +100 -8
  19. package/dist/agent/types.d.ts +9 -0
  20. package/dist/client/AgentPanel.js +12 -9
  21. package/dist/client/AssistantChat.d.ts +15 -1
  22. package/dist/client/AssistantChat.js +110 -43
  23. package/dist/client/MultiTabAssistantChat.js +44 -27
  24. package/dist/client/chat/markdown-renderer.js +4 -2
  25. package/dist/client/chat/message-components.d.ts +2 -2
  26. package/dist/client/chat/message-components.js +69 -35
  27. package/dist/client/chat/run-recovery.d.ts +4 -1
  28. package/dist/client/chat/run-recovery.js +62 -30
  29. package/dist/client/chat/tool-call-display.d.ts +12 -3
  30. package/dist/client/chat/tool-call-display.js +79 -40
  31. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  32. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  33. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  34. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  35. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  36. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  37. package/dist/client/components/ui/message-scroller.js +7 -3
  38. package/dist/client/composer/runtime-adapters.js +3 -2
  39. package/dist/client/context-xray/ContextMeter.js +5 -1
  40. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  41. package/dist/client/error-format.d.ts +4 -0
  42. package/dist/client/error-format.js +93 -5
  43. package/dist/client/i18n.js +83 -17
  44. package/dist/client/observability/ThumbsFeedback.js +10 -8
  45. package/dist/client/sharing/ShareButton.js +160 -54
  46. package/dist/coding-tools/run-code.js +2 -1
  47. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  48. package/dist/coding-tools/sandbox/background.js +8 -1
  49. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  50. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  51. package/dist/collab/routes.d.ts +1 -1
  52. package/dist/index.d.ts +1 -1
  53. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  54. package/dist/localization/core-messages/ar-SA.js +541 -0
  55. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  56. package/dist/localization/core-messages/de-DE.js +513 -0
  57. package/dist/localization/core-messages/en-US.d.ts +513 -0
  58. package/dist/localization/core-messages/en-US.js +513 -0
  59. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  60. package/dist/localization/core-messages/es-ES.js +520 -0
  61. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  62. package/dist/localization/core-messages/fr-FR.js +520 -0
  63. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  64. package/dist/localization/core-messages/hi-IN.js +513 -0
  65. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  66. package/dist/localization/core-messages/ja-JP.js +506 -0
  67. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  68. package/dist/localization/core-messages/ko-KR.js +506 -0
  69. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  70. package/dist/localization/core-messages/pt-BR.js +520 -0
  71. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  72. package/dist/localization/core-messages/zh-CN.js +506 -0
  73. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  74. package/dist/localization/core-messages/zh-TW.js +506 -0
  75. package/dist/localization/core-messages.d.ts +524 -0
  76. package/dist/localization/core-messages.js +124 -0
  77. package/dist/localization/default-messages.d.ts +2 -0
  78. package/dist/localization/default-messages.js +2 -0
  79. package/dist/notifications/routes.d.ts +1 -1
  80. package/dist/observability/routes.d.ts +5 -5
  81. package/dist/resources/handlers.d.ts +1 -1
  82. package/dist/secrets/routes.d.ts +9 -9
  83. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  84. package/dist/server/agent-chat-plugin.d.ts +10 -0
  85. package/dist/server/agent-chat-plugin.js +144 -41
  86. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  87. package/dist/server/agent-run-context.d.ts +7 -1
  88. package/dist/server/agent-run-context.js +9 -2
  89. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  90. package/dist/server/agent-teams.js +38 -9
  91. package/dist/server/index.d.ts +1 -1
  92. package/dist/server/request-context.d.ts +7 -0
  93. package/dist/server/request-context.js +15 -0
  94. package/docs/content/agent-surfaces.mdx +30 -0
  95. package/package.json +2 -2
@@ -12,6 +12,7 @@ import { fetchBuilderStatus, fetchEnvironmentStatus, } from "./client-status-req
12
12
  import { Popover, PopoverAnchor, PopoverContent, } from "./components/ui/popover.js";
13
13
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
14
14
  import { isTrustedFrameMessage } from "./frame.js";
15
+ import { DEFAULT_LOCALE, useOptionalLocale, useT } from "./i18n.js";
15
16
  import { RunStuckBanner } from "./RunStuckBanner.js";
16
17
  import { callAction } from "./use-action.js";
17
18
  import { useChangeVersion } from "./use-change-version.js";
@@ -118,20 +119,25 @@ function formatScopeType(type) {
118
119
  return type.replace(/[-_]+/g, " ");
119
120
  }
120
121
  // ─── History Popover ─────────────────────────────────────────────────────────
121
- function formatThreadTime(ts) {
122
+ function formatThreadTime(ts, locale, yesterdayLabel) {
122
123
  const d = new Date(ts);
123
124
  const now = new Date();
124
125
  const diffMs = now.getTime() - d.getTime();
125
126
  const diffDays = Math.floor(diffMs / 86400000);
126
127
  if (diffDays === 0)
127
- return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
128
+ return d.toLocaleTimeString(locale, {
129
+ hour: "numeric",
130
+ minute: "2-digit",
131
+ });
128
132
  if (diffDays === 1)
129
- return "Yesterday";
133
+ return yesterdayLabel;
130
134
  if (diffDays < 7)
131
- return d.toLocaleDateString([], { weekday: "short" });
132
- return d.toLocaleDateString([], { month: "short", day: "numeric" });
135
+ return d.toLocaleDateString(locale, { weekday: "short" });
136
+ return d.toLocaleDateString(locale, { month: "short", day: "numeric" });
133
137
  }
134
138
  function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads = false, isLoadingMoreThreads = false, loadError, onSelect, onClose, onLoadMore, onSearch, onTogglePin, onRename, }) {
139
+ const t = useT();
140
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
135
141
  const [search, setSearch] = useState("");
136
142
  const [searchResults, setSearchResults] = useState(null);
137
143
  const [isSearching, setIsSearching] = useState(false);
@@ -189,7 +195,7 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
189
195
  const unpinnedThreads = filtered.filter((t) => t.pinnedAt == null);
190
196
  const toHistoryItem = (thread) => {
191
197
  const isActive = thread.id === activeThreadId;
192
- const title = thread.title || thread.preview || "Chat";
198
+ const title = thread.title || thread.preview || t("agentChat.history.untitledChat");
193
199
  return {
194
200
  id: thread.id,
195
201
  title,
@@ -198,10 +204,10 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
198
204
  ? thread.preview
199
205
  : undefined,
200
206
  timestamp: isActive
201
- ? "Active"
207
+ ? t("agentChat.history.active")
202
208
  : openTabIds.has(thread.id)
203
- ? "Open"
204
- : formatThreadTime(thread.updatedAt),
209
+ ? t("agentChat.history.open")
210
+ : formatThreadTime(thread.updatedAt, locale, t("agentChat.history.yesterday")),
205
211
  pinned: thread.pinnedAt != null,
206
212
  };
207
213
  };
@@ -210,7 +216,7 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
210
216
  ? [
211
217
  {
212
218
  id: "pinned",
213
- label: "Pinned",
219
+ label: t("agentChat.history.pinned"),
214
220
  items: pinnedThreads.map(toHistoryItem),
215
221
  },
216
222
  ]
@@ -228,10 +234,13 @@ function HistoryPopover({ threads, openTabIds, activeThreadId, hasMoreThreads =
228
234
  const isPinned = threads.find((t) => t.id === id)?.pinnedAt != null;
229
235
  onTogglePin(id, !isPinned);
230
236
  }
231
- : undefined, onRename: onRename, searchValue: search, onSearchChange: setSearch, searchPlaceholder: "Search chats...", searchInputRef: inputRef, loading: isSearching, loadingLabel: "Searching...", error: loadError && !search.trim() ? loadError : undefined, emptyLabel: "No chats yet", emptySearchLabel: "No matching chats", footer: !search.trim() && hasMoreThreads ? (_jsx("button", { type: "button", onClick: () => onLoadMore?.(), disabled: isLoadingMoreThreads, className: "mx-1 mt-1 flex w-[calc(100%-0.5rem)] items-center justify-center rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-60", children: isLoadingMoreThreads ? "Loading..." : "Load older chats" })) : undefined }) })] }));
237
+ : undefined, onRename: onRename, searchValue: search, onSearchChange: setSearch, searchPlaceholder: t("agentChat.history.search"), searchInputRef: inputRef, loading: isSearching, loadingLabel: t("agentChat.history.searching"), error: loadError && !search.trim() ? loadError : undefined, emptyLabel: t("agentChat.history.empty"), emptySearchLabel: t("agentChat.history.noMatches"), footer: !search.trim() && hasMoreThreads ? (_jsx("button", { type: "button", onClick: () => onLoadMore?.(), disabled: isLoadingMoreThreads, className: "mx-1 mt-1 flex w-[calc(100%-0.5rem)] items-center justify-center rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-60", children: isLoadingMoreThreads
238
+ ? t("agentChat.common.loading")
239
+ : t("agentChat.history.loadOlder") })) : undefined }) })] }));
232
240
  }
233
241
  // ─── Help Popover ────────────────────────────────────────────────────────────
234
242
  function HelpPopover({ onClose }) {
243
+ const t = useT();
235
244
  useEffect(() => {
236
245
  const handler = (e) => {
237
246
  if (e.key === "Escape")
@@ -243,16 +252,16 @@ function HelpPopover({ onClose }) {
243
252
  const commands = [
244
253
  {
245
254
  name: "/clear",
246
- description: "Start a new chat (keeps current chat in history)",
255
+ description: t("agentChat.commands.clear"),
247
256
  },
248
- { name: "/new", description: "Same as /clear" },
249
- { name: "/history", description: "Browse all chats" },
250
- { name: "/plan", description: "Switch to read-only planning" },
251
- { name: "/act", description: "Switch back to acting" },
252
- { name: "/help", description: "Show this list of commands" },
253
- { name: "@", description: "Mention files, agents, or resources" },
257
+ { name: "/new", description: t("agentChat.commands.new") },
258
+ { name: "/history", description: t("agentChat.commands.history") },
259
+ { name: "/plan", description: t("agentChat.commands.plan") },
260
+ { name: "/act", description: t("agentChat.commands.act") },
261
+ { name: "/help", description: t("agentChat.commands.help") },
262
+ { name: "@", description: t("agentChat.commands.mention") },
254
263
  ];
255
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }), _jsxs("div", { className: "absolute end-2 top-0 z-50 w-72 rounded-lg border border-border bg-popover shadow-lg", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border", children: [_jsx("span", { className: "text-xs font-medium text-foreground", children: "Available Commands" }), _jsx("button", { onClick: onClose, "aria-label": "Close help", className: "flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: _jsx(IconX, { size: 12 }) })] }), _jsx("div", { className: "py-1", children: commands.map((cmd) => (_jsxs("div", { className: "px-3 py-1.5", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: cmd.name }), _jsx("div", { className: "text-[11px] text-muted-foreground", children: cmd.description })] }, cmd.name))) })] })] }));
264
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }), _jsxs("div", { className: "absolute end-2 top-0 z-50 w-72 rounded-lg border border-border bg-popover shadow-lg", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border", children: [_jsx("span", { className: "text-xs font-medium text-foreground", children: t("agentChat.commands.available") }), _jsx("button", { onClick: onClose, "aria-label": t("agentChat.commands.closeHelp"), className: "flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: _jsx(IconX, { size: 12 }) })] }), _jsx("div", { className: "py-1", children: commands.map((cmd) => (_jsxs("div", { className: "px-3 py-1.5", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: cmd.name }), _jsx("div", { className: "text-[11px] text-muted-foreground", children: cmd.description })] }, cmd.name))) })] })] }));
256
265
  }
257
266
  function readString(value) {
258
267
  return typeof value === "string" ? value.trim() : "";
@@ -376,6 +385,7 @@ function runToAgentTeamTabInfo(run) {
376
385
  };
377
386
  }
378
387
  export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderOverlay, contentHidden = false, apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, restoreActiveThread = true, browserTabId, threadUrlSync = false, scope = null, agentTeamPollMs = DEFAULT_AGENT_TEAM_POLL_MS, ...props }) {
388
+ const translate = useT();
379
389
  const { enabled: threadUrlSyncEnabled, paramName: threadUrlParamName, routeThreadId, getPath: getThreadPath, navigate: navigateThreadUrl, } = resolveThreadUrlSync(threadUrlSync);
380
390
  const threadRouteControlsActiveThread = threadUrlSyncEnabled &&
381
391
  threadUrlSync !== true &&
@@ -1661,7 +1671,9 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1661
1671
  const agentTeamStatus = chatTabStatusFromAgentTeamStatus(subAgentStatuses[id]);
1662
1672
  return {
1663
1673
  id,
1664
- label: t?.title || t?.preview?.slice(0, 30) || "New chat",
1674
+ label: t?.title ||
1675
+ t?.preview?.slice(0, 30) ||
1676
+ translate("agentChat.tabs.newChat"),
1665
1677
  status: agentTeamStatus ??
1666
1678
  (runningThreads.has(id)
1667
1679
  ? "running"
@@ -1677,7 +1689,10 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1677
1689
  if (!tabs.some((t) => t.id === id)) {
1678
1690
  tabs.push({
1679
1691
  id,
1680
- label: subAgentNames[id] || (parentMap[id] ? "Sub-agent..." : "New chat"),
1692
+ label: subAgentNames[id] ||
1693
+ (parentMap[id]
1694
+ ? translate("agentChat.tabs.subAgent")
1695
+ : translate("agentChat.tabs.newChat")),
1681
1696
  status: chatTabStatusFromAgentTeamStatus(subAgentStatuses[id]) ??
1682
1697
  "running",
1683
1698
  parentThreadId: parentMap[id],
@@ -1726,7 +1741,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1726
1741
  activeTab?.parentThreadId === tab.id);
1727
1742
  return (_jsxs("div", { ref: isActive ? activeTabRefCb : undefined, className: cn("agent-tab relative flex items-center rounded-md text-[11px] font-medium shrink-0 max-w-[130px]", isActive
1728
1743
  ? "bg-accent text-foreground ring-1 ring-inset ring-border/60 shadow-sm"
1729
- : "text-muted-foreground hover:text-foreground hover:bg-accent/50"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2.5 py-1.5 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": "Close tab", onClick: (e) => {
1744
+ : "text-muted-foreground hover:text-foreground hover:bg-accent/50"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2.5 py-1.5 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": translate("agentChat.tabs.closeTab"), onClick: (e) => {
1730
1745
  e.stopPropagation();
1731
1746
  e.preventDefault();
1732
1747
  closeTab(tab.id);
@@ -1740,13 +1755,13 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1740
1755
  borderRadius: "0 6px 6px 0",
1741
1756
  background: "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
1742
1757
  }, children: _jsx(IconX, { size: 12 }) })] }, tab.id));
1743
- }) }), _jsx(TooltipProvider, { delayDuration: 200, children: _jsxs("div", { className: "flex items-center gap-px shrink-0 ms-auto", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: addTab, "aria-label": "New chat", className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 12 }) }) }), _jsx(TooltipContent, { children: "New chat" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => setShowHistory(!showHistory), "aria-label": "All chats", className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", showHistory && "bg-accent text-foreground"), children: _jsx(IconHistory, { size: 12 }) }) }), _jsx(TooltipContent, { children: "All chats" })] })] }) })] }), hasSubTabs && (_jsx("div", { className: "flex items-center px-1 py-0.5 border-b border-border shrink-0 gap-0.5 bg-muted/30", children: _jsxs("div", { className: "agent-tabs-scroll flex items-center gap-0.5 min-w-0 overflow-x-auto flex-1", children: [_jsx("button", { onClick: () => switchThread(focusParentId), className: cn("flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium cursor-pointer", activeThreadId === focusParentId
1758
+ }) }), _jsx(TooltipProvider, { delayDuration: 200, children: _jsxs("div", { className: "flex items-center gap-px shrink-0 ms-auto", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: addTab, "aria-label": translate("agentChat.tabs.newChat"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 12 }) }) }), _jsx(TooltipContent, { children: translate("agentChat.tabs.newChat") })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => setShowHistory(!showHistory), "aria-label": translate("agentChat.tabs.allChats"), className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground/60 hover:text-foreground hover:bg-accent/50", showHistory && "bg-accent text-foreground"), children: _jsx(IconHistory, { size: 12 }) }) }), _jsx(TooltipContent, { children: translate("agentChat.tabs.allChats") })] })] }) })] }), hasSubTabs && (_jsx("div", { className: "flex items-center px-1 py-0.5 border-b border-border shrink-0 gap-0.5 bg-muted/30", children: _jsxs("div", { className: "agent-tabs-scroll flex items-center gap-0.5 min-w-0 overflow-x-auto flex-1", children: [_jsx("button", { onClick: () => switchThread(focusParentId), className: cn("flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium cursor-pointer", activeThreadId === focusParentId
1744
1759
  ? "bg-accent text-foreground"
1745
- : "text-muted-foreground hover:bg-accent hover:text-foreground"), children: "Main" }), childTabs.map((tab) => (_jsxs("div", { ref: tab.id === activeThreadId
1760
+ : "text-muted-foreground hover:bg-accent hover:text-foreground"), children: translate("agentChat.tabs.main") }), childTabs.map((tab) => (_jsxs("div", { ref: tab.id === activeThreadId
1746
1761
  ? activeTabRefCb
1747
1762
  : undefined, className: cn("agent-tab relative flex shrink-0 items-center rounded-md text-[10px] font-medium max-w-[130px]", tab.id === activeThreadId
1748
1763
  ? "bg-accent text-foreground"
1749
- : "text-muted-foreground hover:bg-accent hover:text-foreground"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2 py-1 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.subAgentName || tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1 h-1 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": "Close tab", onClick: (e) => {
1764
+ : "text-muted-foreground hover:bg-accent hover:text-foreground"), children: [_jsxs("button", { type: "button", onClick: () => switchThread(tab.id), className: "flex items-center gap-1 px-2 py-1 min-w-0 flex-1 text-start", children: [_jsx("span", { className: "truncate pe-1", children: tab.subAgentName || tab.label }), tab.status === "running" && (_jsx("span", { className: "w-1 h-1 rounded-full bg-muted-foreground/50 shrink-0 animate-pulse" }))] }), _jsx("button", { type: "button", "aria-label": translate("agentChat.tabs.closeTab"), onClick: (e) => {
1750
1765
  e.stopPropagation();
1751
1766
  e.preventDefault();
1752
1767
  closeTab(tab.id);
@@ -1772,7 +1787,9 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1772
1787
  display: contentHidden || tabId !== activeThreadId ? "none" : "flex",
1773
1788
  }, children: [_jsx(RunStuckBanner, { threadId: tabId, enabled: tabId === activeThreadId, apiUrl: apiUrl, autoRetry: true, autoRetryOwnerId: browserTabId, hasInFlightWork: () => chatRefs.current.get(tabId)?.hasInFlightWork() ?? false, isAwaitingResponse: () => chatRefs.current.get(tabId)?.isRunning() ?? false, onRetry: () => {
1774
1789
  const handle = chatRefs.current.get(tabId);
1775
- handle?.sendRecoveryMessage("Continue from where you left off and finish my last request. Do not repeat completed work.", "continue");
1790
+ handle?.sendRecoveryMessage(
1791
+ // i18n-ignore -- stable hidden agent instruction.
1792
+ "Continue from where you left off and finish my last request. Do not repeat completed work.", "continue");
1776
1793
  } }), _jsx(AssistantChat, { ...props, dynamicSuggestions: tabDynamicSuggestions, ref: (handle) => {
1777
1794
  if (handle) {
1778
1795
  chatRefs.current.set(tabId, handle);
@@ -1788,7 +1805,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1788
1805
  // the in-flight team chunk. Disable the composer and show a
1789
1806
  // hint so users know to send via the orchestrator chat instead.
1790
1807
  composerDisabled: Boolean(parentMap[tabId]), composerDisabledPlaceholder: parentMap[tabId]
1791
- ? "Send messages to the orchestrator chat — this sub-agent runs automatically"
1808
+ ? translate("agentChat.composer.subAgentReadOnly")
1792
1809
  : undefined })] }, tabId));
1793
1810
  })] })] }));
1794
1811
  }
@@ -7,8 +7,9 @@ import { IconPlus, IconExternalLink } from "@tabler/icons-react";
7
7
  import React, { useState, useEffect, useCallback, useMemo, useRef, } from "react";
8
8
  import { splitMarkdownBlocks } from "../../shared/markdown-block-split.js";
9
9
  import { initialSmoothStreamingGraphemeCount, SMOOTH_STREAMING_COMMIT_INTERVAL_MS, smoothStreamingPunctuationDelayMs, smoothStreamingRevealCount, splitStreamingTextGraphemes, } from "../../shared/streaming-text-smoothing.js";
10
- import { NEW_CHAT_ACTION_HREF } from "../error-format.js";
10
+ import { localizeKnownChatErrorText, NEW_CHAT_ACTION_HREF, } from "../error-format.js";
11
11
  import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
12
+ import { useT } from "../i18n.js";
12
13
  import { IframeEmbed, parseEmbedBody } from "../IframeEmbed.js";
13
14
  import { cn } from "../utils.js";
14
15
  import { LEGACY_CHART_SHORTHAND_LANG, LegacyChartShorthandChart, LegacyChartShorthandFallback, parseLegacyChartShorthand, wrapLegacyChartShorthandLines, } from "./legacy-chart-shorthand.js";
@@ -485,11 +486,12 @@ export function SmoothMarkdownText({ text, streaming, resetKey, statusType = "co
485
486
  }
486
487
  // ─── MarkdownText ──────────────────────────────────────────────────────────────
487
488
  export function MarkdownText() {
489
+ const t = useT();
488
490
  const textPart = useMessagePartText();
489
491
  const messageRuntime = useMessageRuntime();
490
492
  const message = messageRuntime.getState();
491
493
  const textStreaming = React.useContext(TextStreamingContext);
492
494
  const isLastAssistantMessage = message.role === "assistant" && message.isLast;
493
495
  const statusType = textPart.status?.type ?? message.status?.type ?? "complete";
494
- return (_jsx(SmoothMarkdownText, { text: textPart.text, streaming: textStreaming && isLastAssistantMessage, resetKey: message.id, statusType: statusType }));
496
+ return (_jsx(SmoothMarkdownText, { text: localizeKnownChatErrorText(textPart.text, t), streaming: textStreaming && isLastAssistantMessage, resetKey: message.id, statusType: statusType }));
495
497
  }
@@ -9,7 +9,7 @@ interface FormattedMessageTimestamp {
9
9
  short: string;
10
10
  full: string;
11
11
  }
12
- export declare function formatMessageTimestamp(value: unknown): FormattedMessageTimestamp | null;
12
+ export declare function formatMessageTimestamp(value: unknown, locale?: string, yesterdayLabel?: string): FormattedMessageTimestamp | null;
13
13
  export declare function MessageTimestamp({ timestamp, className, }: {
14
14
  timestamp: FormattedMessageTimestamp;
15
15
  className?: string;
@@ -184,6 +184,6 @@ export declare function AssistantMessage(): React.JSX.Element | null;
184
184
  export declare function RunningActivityStatus({ label }: {
185
185
  label: string;
186
186
  }): React.JSX.Element;
187
- export declare function ThinkingIndicator({ label, }?: {
187
+ export declare function ThinkingIndicator({ label }?: {
188
188
  label?: string;
189
189
  }): React.JSX.Element;
@@ -14,6 +14,8 @@ import { writeClipboardText } from "../clipboard.js";
14
14
  import { Dialog, DialogClose, DialogContent, DialogTitle, } from "../components/ui/dialog.js";
15
15
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../components/ui/dropdown-menu.js";
16
16
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
17
+ import { localizeKnownChatErrorText } from "../error-format.js";
18
+ import { DEFAULT_LOCALE, useFormatters, useOptionalLocale, useT, } from "../i18n.js";
17
19
  import { ThumbsFeedback } from "../observability/ThumbsFeedback.js";
18
20
  import { McpConnectionSuggestion } from "../resources/McpConnectionSuggestion.js";
19
21
  import { isCallAgentToolCallShadowed, isToolCallActive, shadowedCallAgentToolCallIds, } from "../tool-display.js";
@@ -21,7 +23,7 @@ import { cn } from "../utils.js";
21
23
  import { MarkdownText, renderMarkdownToClipboardHtml, SmoothMarkdownText, } from "./markdown-renderer.js";
22
24
  import { getAssistantRunDurationMs } from "./repo-helpers.js";
23
25
  import { getRunErrorMetadata, runErrorHeadline, runErrorKey, } from "./run-recovery.js";
24
- import { ToolCallFallback, ToolActivityPresentation, ToolCallStackMotion, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunningRunIdContext, ChatRunningTurnIdContext, ChatRunDurationContext, formatWorkedDuration, ReasoningCell, WorkedForSummary, toolCallHasPendingApproval, } from "./tool-call-display.js";
26
+ import { ToolCallFallback, ToolActivityPresentation, ToolCallStackMotion, FilesChangedSummary, ASSISTANT_VISIBLE_TOOL_CALL_LIMIT, ChatRunningContext, ChatRunningRunIdContext, ChatRunningTurnIdContext, ChatRunDurationContext, ReasoningCell, useLocalizedWorkedDuration, WorkedForSummary, toolCallHasPendingApproval, } from "./tool-call-display.js";
25
27
  export { toolCallHasPendingApproval };
26
28
  // ─── Pending selection context key ───────────────────────────────────────────
27
29
  // Mirrored from AssistantChat to avoid a cross-import on a private constant.
@@ -29,7 +31,7 @@ const PENDING_SELECTION_KEY = "pending-selection-context";
29
31
  // ─── displayableUserMessageText ───────────────────────────────────────────────
30
32
  export function displayableUserMessageText(text) {
31
33
  return text
32
- .replace(/<context\b[^>]*>[\s\S]*?<\/context>\n?/gi, "")
34
+ .replace(/<context\b[^>]*>[\s\S]*?<\/context>\n?/gi, "") // i18n-ignore -- parsing regex, not UI copy.
33
35
  .replace(/<context\b[^>]*>[\s\S]*$/gi, "")
34
36
  .replace(/<\/context>/gi, "")
35
37
  .trim();
@@ -56,14 +58,14 @@ function isSameCalendarDay(a, b) {
56
58
  a.getMonth() === b.getMonth() &&
57
59
  a.getDate() === b.getDate());
58
60
  }
59
- export function formatMessageTimestamp(value) {
61
+ export function formatMessageTimestamp(value, locale, yesterdayLabel = "Yesterday") {
60
62
  const date = coerceMessageDate(value);
61
63
  if (!date)
62
64
  return null;
63
65
  const now = new Date();
64
66
  const yesterday = new Date(now);
65
67
  yesterday.setDate(now.getDate() - 1);
66
- const time = new Intl.DateTimeFormat(undefined, {
68
+ const time = new Intl.DateTimeFormat(locale, {
67
69
  hour: "numeric",
68
70
  minute: "2-digit",
69
71
  }).format(date);
@@ -72,16 +74,16 @@ export function formatMessageTimestamp(value) {
72
74
  short = time;
73
75
  }
74
76
  else if (isSameCalendarDay(date, yesterday)) {
75
- short = `Yesterday ${time}`;
77
+ short = `${yesterdayLabel} ${time}`;
76
78
  }
77
79
  else if (date.getFullYear() === now.getFullYear()) {
78
- short = `${new Intl.DateTimeFormat(undefined, {
80
+ short = `${new Intl.DateTimeFormat(locale, {
79
81
  month: "short",
80
82
  day: "numeric",
81
83
  }).format(date)}, ${time}`;
82
84
  }
83
85
  else {
84
- short = `${new Intl.DateTimeFormat(undefined, {
86
+ short = `${new Intl.DateTimeFormat(locale, {
85
87
  month: "short",
86
88
  day: "numeric",
87
89
  year: "numeric",
@@ -89,7 +91,7 @@ export function formatMessageTimestamp(value) {
89
91
  }
90
92
  return {
91
93
  short,
92
- full: new Intl.DateTimeFormat(undefined, {
94
+ full: new Intl.DateTimeFormat(locale, {
93
95
  weekday: "short",
94
96
  month: "short",
95
97
  day: "numeric",
@@ -104,6 +106,8 @@ export function MessageTimestamp({ timestamp, className, }) {
104
106
  }
105
107
  // ─── SelectionAttachedPill ────────────────────────────────────────────────────
106
108
  export function SelectionAttachedPill() {
109
+ const t = useT();
110
+ const { formatNumber } = useFormatters();
107
111
  const [length, setLength] = useState(null);
108
112
  useEffect(() => {
109
113
  let cancelled = false;
@@ -140,7 +144,10 @@ export function SelectionAttachedPill() {
140
144
  }, []);
141
145
  if (length === null || length === 0)
142
146
  return null;
143
- return (_jsx("div", { className: "agent-selection-attached-pill shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }), _jsxs("span", { children: [length.toLocaleString(), " chars of selection attached"] }), _jsx("button", { type: "button", "aria-label": "Clear selection context", onClick: () => {
147
+ return (_jsx("div", { className: "agent-selection-attached-pill shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }), _jsx("span", { children: t("agentChat.selection.attached", {
148
+ count: length,
149
+ formattedCount: formatNumber(length),
150
+ }) }), _jsx("button", { type: "button", "aria-label": t("agentChat.selection.clear"), onClick: () => {
144
151
  setLength(null);
145
152
  // Dispatch clear event; AssistantChat owns the DELETE call.
146
153
  window.dispatchEvent(new CustomEvent("agent-panel:selection-clear-requested"));
@@ -193,7 +200,8 @@ export function resolveAssistantRequestId(message, activeRun, threadId) {
193
200
  }
194
201
  // ─── MessageBranchPicker ──────────────────────────────────────────────────────
195
202
  export function MessageBranchPicker() {
196
- 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" }) }) })] }));
203
+ const t = useT();
204
+ 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": t("agentChat.message.previousBranch"), 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": t("agentChat.message.nextBranch"), 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" }) }) })] }));
197
205
  }
198
206
  // ─── Mention rendering ────────────────────────────────────────────────────────
199
207
  const mentionIconProps = {
@@ -299,23 +307,27 @@ function UserMessageAttachments() {
299
307
  }) }));
300
308
  }
301
309
  export function ChatImageAttachmentPreview({ src, alt, }) {
310
+ const t = useT();
302
311
  const [open, setOpen] = useState(false);
303
- return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": `Preview ${alt}`, title: alt, onClick: () => setOpen(true), className: "h-16 w-16 cursor-zoom-in overflow-hidden rounded-lg border border-border/70 bg-muted/50 p-0 transition-[border-color,box-shadow] hover:border-foreground/40 hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx("img", { src: src, alt: alt, className: "h-full w-full object-cover" }) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { hideClose: true, "aria-describedby": undefined, className: "fixed inset-0 left-0 top-0 flex h-screen max-h-none w-screen max-w-none translate-x-0 translate-y-0 items-center justify-center gap-0 overflow-hidden rounded-none border-0 bg-foreground/90 p-0 text-background shadow-none backdrop-blur-sm dark:bg-background/95 dark:text-foreground", onOpenAutoFocus: (event) => event.preventDefault(), children: [_jsx(DialogTitle, { className: "sr-only", children: alt || "Image preview" }), _jsx("div", { className: "flex h-full w-full items-center justify-center overflow-auto p-6", onClick: (event) => {
312
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": t("agentChat.composer.previewAttachment", { name: alt }), title: alt, onClick: () => setOpen(true), className: "h-16 w-16 cursor-zoom-in overflow-hidden rounded-lg border border-border/70 bg-muted/50 p-0 transition-[border-color,box-shadow] hover:border-foreground/40 hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx("img", { src: src, alt: alt, className: "h-full w-full object-cover" }) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { hideClose: true, "aria-describedby": undefined, className: "fixed inset-0 left-0 top-0 flex h-screen max-h-none w-screen max-w-none translate-x-0 translate-y-0 items-center justify-center gap-0 overflow-hidden rounded-none border-0 bg-foreground/90 p-0 text-background shadow-none backdrop-blur-sm dark:bg-background/95 dark:text-foreground", onOpenAutoFocus: (event) => event.preventDefault(), children: [_jsx(DialogTitle, { className: "sr-only", children: alt || t("agentChat.composer.imagePreview") }), _jsx("div", { className: "flex h-full w-full items-center justify-center overflow-auto p-6", onClick: (event) => {
304
313
  if (event.target === event.currentTarget)
305
314
  setOpen(false);
306
- }, children: _jsx("img", { src: src, alt: alt, draggable: false, className: "max-h-[90vh] max-w-[92vw] rounded-lg object-contain shadow-2xl" }) }), _jsx(DialogClose, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Close image preview", className: "absolute end-4 top-4 inline-flex size-9 items-center justify-center rounded-full border border-background/25 bg-background/10 text-background transition-colors hover:bg-background/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring dark:border-foreground/25 dark:bg-foreground/10 dark:text-foreground dark:hover:bg-foreground/20", children: _jsx(IconX, { className: "size-4" }) }) })] }) })] }));
315
+ }, children: _jsx("img", { src: src, alt: alt, draggable: false, className: "max-h-[90vh] max-w-[92vw] rounded-lg object-contain shadow-2xl" }) }), _jsx(DialogClose, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.composer.closePreview"), className: "absolute end-4 top-4 inline-flex size-9 items-center justify-center rounded-full border border-background/25 bg-background/10 text-background transition-colors hover:bg-background/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring dark:border-foreground/25 dark:bg-foreground/10 dark:text-foreground dark:hover:bg-foreground/20", children: _jsx(IconX, { className: "size-4" }) }) })] }) })] }));
307
316
  }
308
317
  // ─── UserMessageEditComposer ──────────────────────────────────────────────────
309
318
  function UserMessageEditComposer() {
310
- 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" }) })] })] }));
319
+ const t = useT();
320
+ 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: t("agentChat.common.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: t("agentChat.common.save") }) })] })] }));
311
321
  }
312
322
  // ─── MessageActionsMenu ────────────────────────────────────────────────────────
313
323
  export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}) {
324
+ const t = useT();
325
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
314
326
  const [open, setOpen] = useState(false);
315
327
  const [copied, setCopied] = useState(null);
316
328
  const messageRuntime = useMessageRuntime();
317
329
  const actionsCtx = React.useContext(MessageActionsContext);
318
- const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt);
330
+ const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt, locale, t("agentChat.history.yesterday"));
319
331
  const handleCopyMessage = useCallback(() => {
320
332
  const m = messageRuntime.getState();
321
333
  const text = m.content
@@ -362,28 +374,32 @@ export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}
362
374
  setOpen(false);
363
375
  onRevert?.();
364
376
  }, [onRevert]);
365
- return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Message actions", className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), "Fork Chat"] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
377
+ return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": t("agentChat.message.actions"), className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), t("agentChat.message.forkChat")] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
366
378
  e.preventDefault();
367
379
  handleCopyMessage();
368
- }, 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) => {
380
+ }, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message"
381
+ ? t("agentChat.common.copied")
382
+ : t("agentChat.message.copyMessage")] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
369
383
  e.preventDefault();
370
384
  handleCopyRequestId();
371
385
  }, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id"
372
- ? "Copied!"
386
+ ? t("agentChat.common.copied")
373
387
  : copied === "id-unavailable"
374
- ? "Request ID unavailable"
388
+ ? t("agentChat.message.requestIdUnavailable")
375
389
  : copied === "id-failed"
376
- ? "Copy failed"
377
- : "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] })] }))] })] }));
390
+ ? t("agentChat.recovery.copyFailed")
391
+ : t("agentChat.message.copyRequestId")] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), t("agentChat.message.revertHere")] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: t("agentChat.message.sentAt", { time: timestamp.short }) })] }))] })] }));
378
392
  }
379
393
  // ─── UserMessage ──────────────────────────────────────────────────────────────
380
394
  export function UserMessage() {
395
+ const t = useT();
396
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
381
397
  const [expanded, setExpanded] = useState(false);
382
398
  const [isExpandable, setIsExpandable] = useState(false);
383
399
  const contentRef = useRef(null);
384
400
  const messageRuntime = useMessageRuntime();
385
401
  const message = messageRuntime.getState();
386
- const timestamp = formatMessageTimestamp(message.createdAt);
402
+ const timestamp = formatMessageTimestamp(message.createdAt, locale, t("agentChat.history.yesterday"));
387
403
  const isEditing = useComposer((state) => state.isEditing);
388
404
  const chatRunning = React.useContext(ChatRunningContext);
389
405
  const hidden = isHiddenUserMessage(message);
@@ -429,7 +445,9 @@ export function UserMessage() {
429
445
  e.clipboardData.setData("text/plain", div.textContent || "");
430
446
  }, children: [_jsx("div", { ref: contentRef, className: cn("whitespace-pre-wrap break-words", !expanded && isExpandable && "max-h-[200px] overflow-hidden"), children: _jsx(MessagePrimitive.Parts, { components: {
431
447
  Text: UserMessageText,
432
- } }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" })), !chatRunning && hasDisplayableText && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Edit, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "Edit message", className: "absolute -left-8 top-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/0 transition-colors duration-150 group-hover:text-muted-foreground/70 group-hover:hover:bg-accent group-hover:hover:text-foreground", children: _jsx(IconPencil, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "left", className: "text-xs", children: "Edit message" })] }) }))] })), hasDisplayableText && isExpandable && (_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", expanded && "rotate-180") }), expanded ? "Collapse" : "Expand"] })), _jsxs("div", { className: "mt-1 flex items-center justify-end gap-1", children: [_jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] })] }) }));
448
+ } }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" })), !chatRunning && hasDisplayableText && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Edit, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.edit"), className: "absolute -left-8 top-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/0 transition-colors duration-150 group-hover:text-muted-foreground/70 group-hover:hover:bg-accent group-hover:hover:text-foreground", children: _jsx(IconPencil, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "left", className: "text-xs", children: t("agentChat.message.edit") })] }) }))] })), hasDisplayableText && isExpandable && (_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", expanded && "rotate-180") }), expanded
449
+ ? t("agentChat.common.collapse")
450
+ : t("agentChat.common.expand")] })), _jsxs("div", { className: "mt-1 flex items-center justify-end gap-1", children: [_jsx(MessageBranchPicker, {}), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: messageFooterFadeClassName }))] })] }) }));
433
451
  }
434
452
  // ─── AssistantMessage ─────────────────────────────────────────────────────────
435
453
  function assistantMessageHasRenderableContent(message) {
@@ -790,17 +808,27 @@ export function shouldShowInlineRunError({ runError, bannerRunErrorKey, }) {
790
808
  return runErrorKey(runError) !== bannerRunErrorKey;
791
809
  }
792
810
  export function InlineRunErrorNotice({ info, durationMs, onRetry, }) {
811
+ const t = useT();
812
+ const formatDuration = useLocalizedWorkedDuration();
793
813
  const [open, setOpen] = useState(false);
794
- const headline = runErrorHeadline(info);
814
+ const headline = runErrorHeadline(info, {
815
+ recoverable: t("agentChat.error.stopped"),
816
+ terminal: t("agentChat.error.failed"),
817
+ });
795
818
  const label = durationMs != null && durationMs >= 1000
796
- ? `${headline} after ${formatWorkedDuration(durationMs)}`
819
+ ? t("agentChat.error.afterDuration", {
820
+ headline,
821
+ duration: formatDuration(durationMs),
822
+ })
797
823
  : headline;
798
- 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"] }))] }))] }));
824
+ 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: localizeKnownChatErrorText(info.message, t) }), 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" }), t("agentChat.common.retry")] }))] }))] }));
799
825
  }
800
826
  function MissingFinalResponseNotice({ messageId, text, animate, onRevealComplete, }) {
801
827
  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 }) }));
802
828
  }
803
829
  export function AssistantMessage() {
830
+ const t = useT();
831
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
804
832
  const [restoreState, setRestoreState] = useState("idle");
805
833
  const [restoreError, setRestoreError] = useState(null);
806
834
  const messageRuntime = useMessageRuntime();
@@ -811,7 +839,7 @@ export function AssistantMessage() {
811
839
  const lastRunDurationMs = React.useContext(ChatRunDurationContext);
812
840
  const msg = messageRuntime.getState();
813
841
  const persistedDurationMs = getAssistantRunDurationMs(msg);
814
- const timestamp = formatMessageTimestamp(msg.createdAt);
842
+ const timestamp = formatMessageTimestamp(msg.createdAt, locale, t("agentChat.history.yesterday"));
815
843
  const isLast = msg.isLast;
816
844
  const wasLiveRef = useRef(false);
817
845
  const messageRunId = assistantMessageRunId(msg);
@@ -848,12 +876,12 @@ export function AssistantMessage() {
848
876
  const showMissingFinalResponse = useSettledFlag(missingFinalResponseCandidate, isLast ? MISSING_FINAL_RESPONSE_SETTLE_MS : 0);
849
877
  const shouldShowUserStoppedNotice = isUserStoppedRun && isLast && responseConnectionText.trim().length === 0;
850
878
  const missingFinalResponseNoticeText = shouldShowUserStoppedNotice
851
- ? "Stopped"
879
+ ? t("agentChat.error.stopped")
852
880
  : isUserStoppedRun
853
881
  ? null
854
882
  : (missingWarningText ??
855
883
  (showMissingFinalResponse
856
- ? "The agent stopped without sending a final message. Ask the agent to continue or retry."
884
+ ? t("agentChat.message.missingFinal")
857
885
  : null));
858
886
  const animateMissingFinalResponse = Boolean(isLast && missingFinalResponseNoticeText && wasLiveRef.current);
859
887
  const missingFinalResponseAnimationKey = animateMissingFinalResponse
@@ -950,7 +978,7 @@ export function AssistantMessage() {
950
978
  if (restoreState !== "confirming" || !cpCtx)
951
979
  return;
952
980
  if (!messageRunId) {
953
- setRestoreError("This message has no run to restore to.");
981
+ setRestoreError(t("agentChat.message.noRestoreRun"));
954
982
  setRestoreState("error");
955
983
  return;
956
984
  }
@@ -968,14 +996,18 @@ export function AssistantMessage() {
968
996
  const payload = (await restoreRes.json().catch(() => null));
969
997
  setRestoreError(typeof payload?.error === "string"
970
998
  ? payload.error
971
- : `Restore failed (${restoreRes.status}).`);
999
+ : t("agentChat.message.restoreFailed", {
1000
+ status: restoreRes.status,
1001
+ }));
972
1002
  setRestoreState("error");
973
1003
  }
974
1004
  catch (err) {
975
- setRestoreError(err instanceof Error ? err.message : "Restore request failed.");
1005
+ setRestoreError(err instanceof Error
1006
+ ? err.message
1007
+ : t("agentChat.message.restoreRequestFailed"));
976
1008
  setRestoreState("error");
977
1009
  }
978
- }, [restoreState, cpCtx, messageRunId]);
1010
+ }, [restoreState, cpCtx, messageRunId, t]);
979
1011
  const cancelRestore = useCallback(() => {
980
1012
  setRestoreError(null);
981
1013
  setRestoreState("idle");
@@ -1050,12 +1082,14 @@ export function AssistantMessage() {
1050
1082
  ? messageActions?.onRetryRunError
1051
1083
  : undefined })), missingWarningText == null &&
1052
1084
  missingFinalResponseNoticeText != null &&
1053
- !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), 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: msg.index }))] }))] }));
1085
+ !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), 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": t("agentChat.message.regenerate"), 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: t("agentChat.message.regenerate") })] }) })), _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: t("agentChat.message.restoreQuestion") }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.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" }), t("agentChat.message.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: t("agentChat.common.dismiss") })] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: msg.index }))] }))] }));
1054
1086
  }
1055
1087
  // ─── RunningActivityStatus / ThinkingIndicator ────────────────────────────────
1056
1088
  export function RunningActivityStatus({ label }) {
1057
1089
  return (_jsx("div", { className: "agent-running-activity", children: _jsx(ThinkingIndicator, { label: label }) }));
1058
1090
  }
1059
- export function ThinkingIndicator({ label = "Thinking", } = {}) {
1060
- return (_jsx("div", { className: "agent-thinking-indicator", role: "status", "aria-live": "polite", "aria-label": label, children: _jsx("span", { className: "agent-thinking-indicator__text", children: label }) }));
1091
+ export function ThinkingIndicator({ label } = {}) {
1092
+ const t = useT();
1093
+ const resolvedLabel = label ?? t("agentChat.status.thinking");
1094
+ return (_jsx("div", { className: "agent-thinking-indicator", role: "status", "aria-live": "polite", "aria-label": resolvedLabel, children: _jsx("span", { className: "agent-thinking-indicator__text", children: resolvedLabel }) }));
1061
1095
  }
@@ -16,7 +16,10 @@ export declare function getRunErrorMetadata(message: unknown): RunErrorInfo | nu
16
16
  * the same run is never announced twice.
17
17
  */
18
18
  export declare function runErrorKey(info: RunErrorInfo): string;
19
- export declare function runErrorHeadline(info: RunErrorInfo): string;
19
+ export declare function runErrorHeadline(info: RunErrorInfo, labels?: {
20
+ recoverable: string;
21
+ terminal: string;
22
+ }): string;
20
23
  export declare function getRequestModeMetadata(message: unknown): "act" | "plan" | null;
21
24
  export declare function BuilderConnectCta({ variant, onConnected, }: {
22
25
  variant?: "primary" | "compact";