@agent-native/core 0.155.0 → 0.157.1

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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
  4. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  5. package/corpus/templates/assets/app/global.css +4 -0
  6. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  7. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +18 -15
  8. package/corpus/templates/brain/app/routes/sources.tsx +1 -2
  9. package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
  10. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
  11. package/corpus/templates/calendar/server/handlers/bookings.ts +219 -93
  12. package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
  13. package/corpus/templates/clips/actions/create-folder.ts +7 -1
  14. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  15. package/corpus/templates/clips/actions/create-recording.ts +5 -3
  16. package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
  17. package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
  18. package/corpus/templates/clips/actions/request-recording-access.ts +272 -0
  19. package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
  20. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
  21. package/corpus/templates/clips/app/components/player/share-dialog.tsx +177 -134
  22. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
  23. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
  24. package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
  25. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
  26. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +24 -17
  27. package/corpus/templates/clips/app/routes/share.$shareId.tsx +151 -20
  28. package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
  29. package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
  30. package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
  31. package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
  32. package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
  33. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
  34. package/corpus/templates/clips/desktop/src/app.tsx +24 -0
  35. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
  36. package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
  37. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
  38. package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
  39. package/corpus/templates/clips/desktop/src/styles.css +24 -9
  40. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
  41. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
  42. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
  43. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
  44. package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
  45. package/corpus/templates/clips/server/db/index.ts +1 -0
  46. package/corpus/templates/clips/server/db/schema.ts +1 -0
  47. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
  48. package/corpus/templates/clips/server/lib/emails.ts +23 -0
  49. package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
  50. package/corpus/templates/clips/server/lib/recordings.ts +2 -1
  51. package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
  52. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
  53. package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
  54. package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
  55. package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
  56. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
  58. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
  59. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +19 -0
  60. package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
  61. package/corpus/templates/clips/shared/recording-link.ts +4 -0
  62. package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
  63. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
  64. package/corpus/templates/factory/AGENTS.md +5 -1
  65. package/corpus/templates/factory/README.md +6 -2
  66. package/corpus/templates/factory/actions/view-screen.ts +15 -0
  67. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
  68. package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
  69. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  70. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
  71. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
  72. package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
  73. package/corpus/templates/factory/app/routes/agents.tsx +9 -0
  74. package/corpus/templates/mail/server/plugins/auth.ts +0 -2
  75. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
  76. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
  77. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  78. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  79. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  80. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  81. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
  82. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  83. package/corpus/templates/slides/app/context/DeckContext.tsx +108 -19
  84. package/corpus/templates/slides/app/global.css +7 -40
  85. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  86. package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
  87. package/corpus/templates/slides/app/pages/DeckEditor.tsx +143 -45
  88. package/dist/agent/index.d.ts +1 -1
  89. package/dist/agent/production-agent.d.ts +30 -0
  90. package/dist/agent/production-agent.js +100 -8
  91. package/dist/agent/types.d.ts +9 -0
  92. package/dist/client/AgentPanel.d.ts +13 -1
  93. package/dist/client/AgentPanel.js +22 -17
  94. package/dist/client/AssistantChat.d.ts +15 -1
  95. package/dist/client/AssistantChat.js +135 -48
  96. package/dist/client/HighlightedCodeBlock.js +12 -2
  97. package/dist/client/MultiTabAssistantChat.js +44 -27
  98. package/dist/client/agent-chat/index.d.ts +1 -0
  99. package/dist/client/agent-chat/index.js +1 -0
  100. package/dist/client/chat/markdown-renderer.js +20 -5
  101. package/dist/client/chat/message-components.d.ts +2 -2
  102. package/dist/client/chat/message-components.js +69 -35
  103. package/dist/client/chat/run-recovery.d.ts +4 -1
  104. package/dist/client/chat/run-recovery.js +62 -30
  105. package/dist/client/chat/tool-call-display.d.ts +12 -3
  106. package/dist/client/chat/tool-call-display.js +79 -40
  107. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  108. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  109. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  110. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  111. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  112. package/dist/client/chat-first/apps-rail.js +1 -1
  113. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  114. package/dist/client/components/ui/message-scroller.js +7 -3
  115. package/dist/client/composer/runtime-adapters.js +3 -2
  116. package/dist/client/context-xray/ContextMeter.js +5 -1
  117. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  118. package/dist/client/error-format.d.ts +4 -0
  119. package/dist/client/error-format.js +93 -5
  120. package/dist/client/i18n.js +83 -17
  121. package/dist/client/observability/ThumbsFeedback.js +10 -8
  122. package/dist/client/settings/AccountSettingsCard.js +48 -19
  123. package/dist/client/sharing/ShareButton.d.ts +1 -2
  124. package/dist/client/sharing/ShareButton.js +151 -81
  125. package/dist/client/sharing/ShareDialog.js +5 -4
  126. package/dist/client/use-chat-threads.js +10 -14
  127. package/dist/coding-tools/run-code.js +2 -1
  128. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  129. package/dist/coding-tools/sandbox/background.js +8 -1
  130. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  131. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  132. package/dist/collab/routes.d.ts +1 -1
  133. package/dist/collab/struct-routes.d.ts +1 -1
  134. package/dist/index.d.ts +1 -1
  135. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  136. package/dist/localization/core-messages/ar-SA.js +541 -0
  137. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  138. package/dist/localization/core-messages/de-DE.js +513 -0
  139. package/dist/localization/core-messages/en-US.d.ts +513 -0
  140. package/dist/localization/core-messages/en-US.js +513 -0
  141. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  142. package/dist/localization/core-messages/es-ES.js +520 -0
  143. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  144. package/dist/localization/core-messages/fr-FR.js +520 -0
  145. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  146. package/dist/localization/core-messages/hi-IN.js +513 -0
  147. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  148. package/dist/localization/core-messages/ja-JP.js +506 -0
  149. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  150. package/dist/localization/core-messages/ko-KR.js +506 -0
  151. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  152. package/dist/localization/core-messages/pt-BR.js +520 -0
  153. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  154. package/dist/localization/core-messages/zh-CN.js +506 -0
  155. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  156. package/dist/localization/core-messages/zh-TW.js +506 -0
  157. package/dist/localization/core-messages.d.ts +524 -0
  158. package/dist/localization/core-messages.js +124 -0
  159. package/dist/localization/default-messages.d.ts +3 -0
  160. package/dist/localization/default-messages.js +3 -0
  161. package/dist/notifications/routes.d.ts +1 -1
  162. package/dist/observability/routes.d.ts +5 -5
  163. package/dist/progress/routes.d.ts +1 -1
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  165. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  166. package/dist/resources/handlers.d.ts +1 -1
  167. package/dist/secrets/routes.d.ts +9 -9
  168. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  169. package/dist/server/agent-chat-plugin.d.ts +10 -0
  170. package/dist/server/agent-chat-plugin.js +144 -41
  171. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  172. package/dist/server/agent-run-context.d.ts +7 -1
  173. package/dist/server/agent-run-context.js +9 -2
  174. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  175. package/dist/server/agent-teams.js +38 -9
  176. package/dist/server/auth-marketing.d.ts +1 -0
  177. package/dist/server/auth-marketing.js +0 -2
  178. package/dist/server/auth.d.ts +1 -0
  179. package/dist/server/auth.js +18 -0
  180. package/dist/server/index.d.ts +1 -1
  181. package/dist/server/onboarding-html.d.ts +1 -0
  182. package/dist/server/onboarding-html.js +2 -88
  183. package/dist/server/realtime-token.d.ts +1 -1
  184. package/dist/server/request-context.d.ts +7 -0
  185. package/dist/server/request-context.js +15 -0
  186. package/dist/shared/markdown-block-split.d.ts +16 -0
  187. package/dist/shared/markdown-block-split.js +81 -5
  188. package/dist/styles/agent-native.css +13 -0
  189. package/dist/voice/voice-cleanup-prompt.js +1 -0
  190. package/docs/content/agent-surfaces.mdx +30 -0
  191. package/package.json +2 -2
  192. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
@@ -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";
@@ -7,8 +7,8 @@ import { IconLoader2, IconCheck, IconCopy, IconX, IconChevronDown, IconGitFork,
7
7
  import { useState, useEffect, useCallback, useRef } from "react";
8
8
  import { agentNativePath } from "../api-path.js";
9
9
  import { writeClipboardText } from "../clipboard.js";
10
- import { isProviderAuthenticationError } from "../error-format.js";
11
- import { useT } from "../i18n.js";
10
+ import { isProviderAuthenticationError, localizeKnownChatErrorText, } from "../error-format.js";
11
+ import { useFormatters, useT } from "../i18n.js";
12
12
  import { AgentProviderSetupForm } from "../settings/ProviderSetupForm.js";
13
13
  import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
14
14
  import { cn } from "../utils.js";
@@ -66,10 +66,11 @@ export function getRunErrorMetadata(message) {
66
66
  export function runErrorKey(info) {
67
67
  return `${info.runId ?? ""}:${info.errorCode ?? ""}:${info.message}`;
68
68
  }
69
- export function runErrorHeadline(info) {
70
- return info.recoverable === true
71
- ? "The agent stopped before finishing"
72
- : "The agent hit an error";
69
+ export function runErrorHeadline(info, labels = {
70
+ recoverable: "The agent stopped before finishing",
71
+ terminal: "The agent hit an error",
72
+ }) {
73
+ return info.recoverable === true ? labels.recoverable : labels.terminal;
73
74
  }
74
75
  export function getRequestModeMetadata(message) {
75
76
  const meta = message?.metadata;
@@ -123,21 +124,28 @@ function isConnectionRecoveryRunError(info) {
123
124
  // which opens the flow in an Electron BrowserWindow that shares the webview's
124
125
  // session. See packages/desktop-app/src/main/index.ts.
125
126
  export function BuilderConnectCta({ variant = "primary", onConnected, }) {
127
+ const t = useT();
126
128
  const { configured, orgName, connecting, error, start } = useBuilderConnectFlow({
127
129
  trackingSource: "assistant_chat_builder_cta",
128
130
  onConnected,
129
131
  });
130
132
  if (variant === "compact") {
131
133
  if (configured) {
132
- return (_jsxs("span", { className: "agent-builder-setup-card__builder-button inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground", children: [_jsx(IconCheck, { size: 11, className: "text-emerald-500" }), orgName ? `Connected to ${orgName}` : "Connected"] }));
134
+ return (_jsxs("span", { className: "agent-builder-setup-card__builder-button inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground", children: [_jsx(IconCheck, { size: 11, className: "text-emerald-500" }), orgName
135
+ ? t("agentChat.setup.connectedTo", { organization: orgName })
136
+ : t("agentChat.setup.connected")] }));
133
137
  }
134
- return (_jsxs("div", { className: "agent-builder-setup-card__builder-cta flex min-w-0 flex-col items-start gap-1 sm:items-end", children: [_jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "agent-builder-setup-card__builder-button inline-flex h-8 shrink-0 items-center gap-1 whitespace-nowrap rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-60", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Waiting\u2026"] })) : ("Connect Builder.io") }), error && (_jsx("p", { className: "max-w-[13rem] text-[10px] leading-snug text-destructive sm:text-right", children: error }))] }));
138
+ return (_jsxs("div", { className: "agent-builder-setup-card__builder-cta flex min-w-0 flex-col items-start gap-1 sm:items-end", children: [_jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "agent-builder-setup-card__builder-button inline-flex h-8 shrink-0 items-center gap-1 whitespace-nowrap rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-60", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), t("agentChat.common.waiting")] })) : (t("agentChat.setup.connectBuilder")) }), error && (_jsx("p", { className: "max-w-[13rem] text-[10px] leading-snug text-destructive sm:text-end", children: error }))] }));
135
139
  }
136
140
  const containerClass = "flex items-center gap-3 rounded-md border border-border px-3 py-3";
137
141
  if (configured) {
138
- return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5", children: orgName ? `Connected — ${orgName}` : "Connected" })] }), _jsxs("span", { className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-emerald-500/10 px-2 py-0.5 text-[10px] font-medium text-emerald-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })] }));
142
+ return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5", children: orgName
143
+ ? t("agentChat.setup.connectedOrganization", {
144
+ organization: orgName,
145
+ })
146
+ : t("agentChat.setup.connected") })] }), _jsxs("span", { className: "ms-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-emerald-500/10 px-2 py-0.5 text-[10px] font-medium text-emerald-500", children: [_jsx(IconCheck, { size: 10 }), t("agentChat.setup.connected")] })] }));
139
147
  }
140
- return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Connect Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 max-w-[220px]", children: "Free credits for LLM, hosting, and more \u2014 no API key needed" }), error && _jsx("p", { className: "mt-1 text-[10px] text-destructive", children: error })] }), _jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-foreground px-3 py-1.5 text-[11px] font-medium no-underline text-background hover:opacity-90 disabled:opacity-60 disabled:cursor-wait", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Waiting\u2026"] })) : ("Connect") })] }));
148
+ return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: t("agentChat.setup.connectBuilder") }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 max-w-[220px]", children: t("agentChat.setup.freeCredits") }), error && _jsx("p", { className: "mt-1 text-[10px] text-destructive", children: error })] }), _jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "ms-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-foreground px-3 py-1.5 text-[11px] font-medium no-underline text-background hover:opacity-90 disabled:opacity-60 disabled:cursor-wait", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), t("agentChat.common.waiting")] })) : (t("agentChat.common.connect")) })] }));
141
149
  }
142
150
  // ─── ApiKeyConnect ────────────────────────────────────────────────────────────
143
151
  export function ApiKeyConnect({ onConnected }) {
@@ -181,6 +189,7 @@ export function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout =
181
189
  }
182
190
  // ─── RunErrorRecoveryCard ─────────────────────────────────────────────────────
183
191
  export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, onProviderConnected, }) {
192
+ const t = useT();
184
193
  const [detailsOpen, setDetailsOpen] = useState(false);
185
194
  const [copyState, setCopyState] = useState("idle");
186
195
  const [forking, setForking] = useState(false);
@@ -201,7 +210,9 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
201
210
  builderReconnect.configured;
202
211
  const isQueryError = isProviderQueryRunError(info);
203
212
  const isConnectionRecoveryError = isConnectionRecoveryRunError(info);
204
- const copyLabel = info.runId || info.errorCode || info.details ? "Copy debug" : "Copy";
213
+ const copyLabel = info.runId || info.errorCode || info.details
214
+ ? t("agentChat.recovery.copyDebug")
215
+ : t("agentChat.common.copy");
205
216
  const copyDetails = useCallback(() => {
206
217
  const text = [
207
218
  info.message,
@@ -237,31 +248,40 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
237
248
  try {
238
249
  const result = await onFork();
239
250
  if (result === false) {
240
- setForkError("Could not fork this chat. Try starting a new chat.");
251
+ setForkError(t("agentChat.recovery.forkFailed"));
241
252
  }
242
253
  }
243
254
  catch {
244
- setForkError("Could not fork this chat. Try starting a new chat.");
255
+ setForkError(t("agentChat.recovery.forkFailed"));
245
256
  }
246
257
  finally {
247
258
  setForking(false);
248
259
  }
249
- }, [forking, onFork]);
260
+ }, [forking, onFork, t]);
250
261
  useEffect(() => {
251
262
  if (builderReconnectResolved) {
252
263
  onDismiss();
253
264
  }
254
265
  }, [builderReconnectResolved, onDismiss]);
255
- return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: info.message }), isProviderAuthError && (_jsx("div", { className: "mt-3 rounded-md border border-border/70 bg-background/60 p-2.5", children: _jsx(BuilderSetupContent, { layout: "sidebar", onConnected: handleProviderConnected }) })), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "The current Builder.io or model-provider credential was rejected. Reconnect Builder.io (free tier available), then retry this message." })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "If retry lands on the same error, start a new chat session and continue from what already changed." })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), "Details"] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
256
- ? "Connecting Builder.io"
257
- : "Reconnect Builder.io"] })), canRecover && (_jsx(_Fragment, { children: _jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Continue"] }) })), canRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError ? "Diagnose and retry" : "Retry"] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), "New chat"] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: "Fork this conversation into a separate chat thread.", "aria-label": "Fork this conversation into a separate chat thread", className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking ? "Forking..." : "Fork chat"] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ml-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copyState === "copied" ? (_jsx(IconCheck, { size: 13 })) : copyState === "failed" ? (_jsx(IconX, { size: 13 })) : (_jsx(IconCopy, { size: 13 })), _jsx("span", { "aria-live": "polite", children: copyState === "copied"
258
- ? "Copied"
266
+ return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info, {
267
+ recoverable: t("agentChat.error.stopped"),
268
+ terminal: t("agentChat.error.failed"),
269
+ }) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: localizeKnownChatErrorText(info.message, t) }), isProviderAuthError && (_jsx("div", { className: "mt-3 rounded-md border border-border/70 bg-background/60 p-2.5", children: _jsx(BuilderSetupContent, { layout: "sidebar", onConnected: handleProviderConnected }) })), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.credentialRejected") })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: t("agentChat.recovery.newChatHint") })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), t("agentChat.common.details")] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": t("agentChat.common.dismiss"), className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
270
+ ? t("agentChat.recovery.connectingBuilder")
271
+ : t("agentChat.recovery.reconnectBuilder")] })), canRecover && (_jsx(_Fragment, { children: _jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), t("agentChat.common.continue")] }) })), canRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError
272
+ ? t("agentChat.recovery.diagnoseRetry")
273
+ : t("agentChat.common.retry")] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), t("agentChat.tabs.newChat")] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: t("agentChat.recovery.forkDescription"), "aria-label": t("agentChat.recovery.forkDescription"), className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking
274
+ ? t("agentChat.recovery.forking")
275
+ : t("agentChat.message.forkChat")] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ms-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copyState === "copied" ? (_jsx(IconCheck, { size: 13 })) : copyState === "failed" ? (_jsx(IconX, { size: 13 })) : (_jsx(IconCopy, { size: 13 })), _jsx("span", { "aria-live": "polite", children: copyState === "copied"
276
+ ? t("agentChat.common.copied")
259
277
  : copyState === "failed"
260
- ? "Copy failed"
278
+ ? t("agentChat.recovery.copyFailed")
261
279
  : copyLabel })] })] }), shouldShowBuilderReconnect && builderReconnect.error && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: builderReconnect.error })), forkError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: forkError }))] }));
262
280
  }
263
281
  // ─── LoopLimitContinueCard ────────────────────────────────────────────────────
264
282
  export function LoopLimitContinueCard({ info, onContinue, }) {
283
+ const t = useT();
284
+ const { formatNumber } = useFormatters();
265
285
  const [settings, setSettings] = useState(null);
266
286
  const [value, setValue] = useState("");
267
287
  const [saving, setSaving] = useState(false);
@@ -294,9 +314,11 @@ export function LoopLimitContinueCard({ info, onContinue, }) {
294
314
  numericValue !== settings.maxIterations;
295
315
  const scopeLabel = settings?.scope === "org"
296
316
  ? settings.orgName
297
- ? `${settings.orgName} org`
298
- : "org"
299
- : "your account";
317
+ ? t("agentChat.limit.namedOrganization", {
318
+ organization: settings.orgName,
319
+ })
320
+ : t("agentChat.limit.organization")
321
+ : t("agentChat.limit.account");
300
322
  const saveLimit = useCallback(async () => {
301
323
  if (!settings?.canUpdate)
302
324
  return false;
@@ -311,7 +333,8 @@ export function LoopLimitContinueCard({ info, onContinue, }) {
311
333
  });
312
334
  const body = await res.json().catch(() => ({}));
313
335
  if (!res.ok) {
314
- throw new Error(body?.error ?? `Save failed (${res.status})`);
336
+ throw new Error(body?.error ??
337
+ t("agentChat.common.saveFailedStatus", { status: res.status }));
315
338
  }
316
339
  setSettings(body);
317
340
  setValue(String(body.maxIterations));
@@ -321,13 +344,13 @@ export function LoopLimitContinueCard({ info, onContinue, }) {
321
344
  return true;
322
345
  }
323
346
  catch (err) {
324
- setError(err instanceof Error ? err.message : "Save failed");
347
+ setError(err instanceof Error ? err.message : t("agentChat.common.saveFailed"));
325
348
  return false;
326
349
  }
327
350
  finally {
328
351
  setSaving(false);
329
352
  }
330
- }, [numericValue, settings?.canUpdate]);
353
+ }, [numericValue, settings?.canUpdate, t]);
331
354
  const handleContinue = useCallback(async () => {
332
355
  if (hasPendingChange) {
333
356
  const ok = await saveLimit();
@@ -343,12 +366,18 @@ export function LoopLimitContinueCard({ info, onContinue, }) {
343
366
  catch { }
344
367
  window.dispatchEvent(new CustomEvent("agent-panel:open-settings"));
345
368
  }, []);
346
- return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] px-3 py-3 shadow-sm", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-600 dark:text-amber-400", children: _jsx(IconGauge, { size: 14 }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: "Step limit reached" }), _jsxs("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: ["The agent used", " ", currentLimit
347
- ? `${currentLimit.toLocaleString()} steps`
348
- : "all available steps", ". Keep going in a fresh turn, or raise the ", scopeLabel, " limit first."] })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "min-w-[116px] flex-1 space-y-1", children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Max steps" }), _jsx("input", { type: "number", min: settings?.minMaxIterations ?? 1, max: settings?.maxMaxIterations ?? 1000, value: value, disabled: !settings?.canUpdate || saving, onChange: (e) => {
369
+ return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] px-3 py-3 shadow-sm", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-600 dark:text-amber-400", children: _jsx(IconGauge, { size: 14 }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: t("agentChat.limit.reached") }), _jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: currentLimit
370
+ ? t("agentChat.limit.descriptionWithCount", {
371
+ count: currentLimit,
372
+ formattedCount: formatNumber(currentLimit),
373
+ scope: scopeLabel,
374
+ })
375
+ : t("agentChat.limit.descriptionAll", { scope: scopeLabel }) })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "min-w-[116px] flex-1 space-y-1", children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: t("agentChat.limit.maxSteps") }), _jsx("input", { type: "number", min: settings?.minMaxIterations ?? 1, max: settings?.maxMaxIterations ?? 1000, value: value, disabled: !settings?.canUpdate || saving, onChange: (e) => {
349
376
  setValue(e.target.value);
350
377
  setError(null);
351
- }, className: "h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring disabled:opacity-60" })] }), _jsx("button", { type: "button", onClick: saveLimit, disabled: !hasPendingChange || saving, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50", children: saving ? (_jsx(IconLoader2, { size: 12, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: 12 })) : ("Save") }), _jsxs("button", { type: "button", onClick: openSettings, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: [_jsx(IconSettings, { size: 12 }), "Settings"] }), _jsxs("button", { type: "button", onClick: handleContinue, disabled: saving, className: "ml-auto inline-flex h-8 items-center gap-1 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:opacity-60", children: [hasPendingChange ? "Save and keep going" : "Keep going", _jsx(IconArrowRight, { size: 12 })] })] }), settings && !settings.canUpdate && (_jsx("p", { className: "mt-2 text-[11px] text-muted-foreground", children: "Only organization owners and admins can change this limit." })), error && _jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })] }));
378
+ }, className: "h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring disabled:opacity-60" })] }), _jsx("button", { type: "button", onClick: saveLimit, disabled: !hasPendingChange || saving, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50", children: saving ? (_jsx(IconLoader2, { size: 12, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: 12 })) : (t("agentChat.common.save")) }), _jsxs("button", { type: "button", onClick: openSettings, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: [_jsx(IconSettings, { size: 12 }), t("agentChat.common.settings")] }), _jsxs("button", { type: "button", onClick: handleContinue, disabled: saving, className: "ms-auto inline-flex h-8 items-center gap-1 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:opacity-60", children: [hasPendingChange
379
+ ? t("agentChat.limit.saveAndContinue")
380
+ : t("agentChat.limit.keepGoing"), _jsx(IconArrowRight, { size: 12 })] })] }), settings && !settings.canUpdate && (_jsx("p", { className: "mt-2 text-[11px] text-muted-foreground", children: t("agentChat.limit.ownerOnly") })), error && _jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })] }));
352
381
  }
353
382
  // ─── PlanModeCallout ──────────────────────────────────────────────────────────
354
383
  //
@@ -358,5 +387,8 @@ export function LoopLimitContinueCard({ info, onContinue, }) {
358
387
  // narrow sidebar chats and wide/centered page layouts, instead of floating
359
388
  // against the full pane width.
360
389
  export function PlanModeCallout({ canImplementPlan, onImplementPlan, onSwitchToAct, }) {
361
- return (_jsx("div", { className: "agent-plan-mode-callout shrink-0 px-3", children: _jsxs("div", { className: "ml-auto flex w-fit max-w-full items-center gap-2 rounded-full border border-border/70 bg-background/95 px-2 py-1.5 text-xs text-muted-foreground shadow-sm", children: [_jsx(IconClipboardList, { size: 13, className: "shrink-0" }), _jsx("span", { className: "min-w-0 truncate", children: canImplementPlan ? "Plan ready" : "Plan mode" }), canImplementPlan ? (_jsxs("button", { type: "button", onClick: onImplementPlan, className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-full bg-foreground px-2.5 text-[11px] font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 12 }), "Implement"] })) : (_jsxs("button", { type: "button", onClick: onSwitchToAct, className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-full border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent", "aria-label": "Switch to Act mode", children: ["Act", _jsx(IconArrowRight, { size: 12 })] }))] }) }));
390
+ const t = useT();
391
+ return (_jsx("div", { className: "agent-plan-mode-callout shrink-0 px-3", children: _jsxs("div", { className: "ms-auto flex w-fit max-w-full items-center gap-2 rounded-full border border-border/70 bg-background/95 px-2 py-1.5 text-xs text-muted-foreground shadow-sm", children: [_jsx(IconClipboardList, { size: 13, className: "shrink-0" }), _jsx("span", { className: "min-w-0 truncate", children: canImplementPlan
392
+ ? t("agentChat.plan.ready")
393
+ : t("agentChat.plan.mode") }), canImplementPlan ? (_jsxs("button", { type: "button", onClick: onImplementPlan, className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-full bg-foreground px-2.5 text-[11px] font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 12 }), t("agentChat.plan.implement")] })) : (_jsxs("button", { type: "button", onClick: onSwitchToAct, className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-full border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent", "aria-label": t("agentChat.plan.switchToAct"), children: [t("agentChat.plan.act"), _jsx(IconArrowRight, { size: 12 })] }))] }) }));
362
394
  }