@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
@@ -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
  }
@@ -72,8 +72,11 @@ export type ToolDetailPayload = {
72
72
  copyText: string;
73
73
  lang: string;
74
74
  };
75
- export declare function toolInputPayload(toolName: string, args: Record<string, unknown>): ToolDetailPayload | null;
76
- export declare function toolResultPayload(result: string | undefined): ToolDetailPayload | null;
75
+ export declare function toolInputPayload(toolName: string, args: Record<string, unknown>, labels?: {
76
+ input: string;
77
+ inputWithLabel: (label: string) => string;
78
+ }): ToolDetailPayload | null;
79
+ export declare function toolResultPayload(result: string | undefined, title?: string): ToolDetailPayload | null;
77
80
  export declare function AnimatedCollapse({ open, children, }: {
78
81
  open: boolean;
79
82
  children: React.ReactNode;
@@ -138,7 +141,13 @@ export declare function ReasoningCell({ text, isStreaming, defaultOpen, autoColl
138
141
  */
139
142
  durationMs?: number | null;
140
143
  }): React.JSX.Element | null;
141
- export declare function formatWorkedDuration(ms: number): string;
144
+ export declare function formatWorkedDuration(ms: number, options?: {
145
+ locale?: string;
146
+ hour?: string;
147
+ minute?: string;
148
+ second?: string;
149
+ }): string;
150
+ export declare function useLocalizedWorkedDuration(): (ms: number) => string;
142
151
  export declare function WorkedForSummary({ durationMs, defaultOpen, autoCollapse, children, }: {
143
152
  durationMs?: number | null;
144
153
  /** Keep completed work visible when the turn contains interactive UI. */
@@ -6,7 +6,7 @@ import { writeClipboardText } from "../clipboard.js";
6
6
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
7
7
  import { ConnectBuilderCard } from "../ConnectBuilderCard.js";
8
8
  import { FileStorageSetupCard } from "../FileStorageSetupCard.js";
9
- import { useT } from "../i18n.js";
9
+ import { useOptionalLocale, useT } from "../i18n.js";
10
10
  import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
11
11
  import { findMcpIntegrationForToolName } from "../resources/mcp-integration-catalog.js";
12
12
  import { McpIntegrationLogo } from "../resources/McpIntegrationLogo.js";
@@ -41,6 +41,7 @@ export function toolCallHasPendingApproval(part) {
41
41
  }
42
42
  export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
43
43
  export function ToolActivityPresentation({ toolName, isRunning, toolCallId, suppressLongRunningHint = false, children, }) {
44
+ const t = useT();
44
45
  const [showLongRunningHint, setShowLongRunningHint] = useState(false);
45
46
  useEffect(() => {
46
47
  if (!isRunning || suppressLongRunningHint) {
@@ -53,7 +54,7 @@ export function ToolActivityPresentation({ toolName, isRunning, toolCallId, supp
53
54
  }, TOOL_LONG_RUNNING_HINT_DELAY_MS);
54
55
  return () => window.clearTimeout(timeout);
55
56
  }, [isRunning, suppressLongRunningHint, toolName]);
56
- return (_jsx("div", { className: "agent-tool-call", "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: "Still working. Large updates can take a minute or two." }))] }) }));
57
+ return (_jsx("div", { className: "agent-tool-call", "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: t("agentChat.tool.longRunning") }))] }) }));
57
58
  }
58
59
  function stringifyToolValue(value, pretty = false) {
59
60
  if (typeof value === "string")
@@ -109,7 +110,10 @@ function formatToolTextValue(value, key, toolName) {
109
110
  }
110
111
  return { text: stringifyToolValue(value, true), lang: "json" };
111
112
  }
112
- export function toolInputPayload(toolName, args) {
113
+ export function toolInputPayload(toolName, args, labels = {
114
+ input: "Input",
115
+ inputWithLabel: (label) => `Input - ${label}`,
116
+ }) {
113
117
  const entries = Object.entries(args);
114
118
  if (entries.length === 0)
115
119
  return null;
@@ -120,7 +124,7 @@ export function toolInputPayload(toolName, args) {
120
124
  const keyLabel = normalizedKey === "sql" || normalizedKey.endsWith("sql") ? "SQL" : key;
121
125
  return {
122
126
  section: "input",
123
- title: `Input - ${keyLabel}`,
127
+ title: labels.inputWithLabel(keyLabel),
124
128
  text: formatted.text,
125
129
  copyText: typeof value === "string" ? value : stringifyToolValue(value, true),
126
130
  lang: formatted.lang,
@@ -128,19 +132,19 @@ export function toolInputPayload(toolName, args) {
128
132
  }
129
133
  return {
130
134
  section: "input",
131
- title: "Input",
135
+ title: labels.input,
132
136
  text: JSON.stringify(args, null, 2),
133
137
  copyText: JSON.stringify(args, null, 2),
134
138
  lang: "json",
135
139
  };
136
140
  }
137
- export function toolResultPayload(result) {
141
+ export function toolResultPayload(result, title = "Result") {
138
142
  if (result === undefined)
139
143
  return null;
140
144
  const formatted = formatToolTextValue(result);
141
145
  return {
142
146
  section: "result",
143
- title: "Result",
147
+ title,
144
148
  text: formatted.text,
145
149
  copyText: result,
146
150
  lang: formatted.lang,
@@ -195,6 +199,7 @@ function SimpleCodeViewer({ text, lang, className, maxHeightClass = "max-h-56",
195
199
  return (_jsxs("div", { className: cn("agent-tool-code overflow-auto rounded-md bg-muted/70 font-mono text-[11px] leading-relaxed text-foreground", maxHeightClass, className), children: [lang !== "text" && (_jsx("div", { className: "sticky top-0 z-[1] flex items-center justify-between border-b border-border/40 bg-muted/90 px-2.5 py-1", children: _jsx("span", { className: "font-mono text-[10px] uppercase tracking-wide text-muted-foreground/80", children: lang }) })), _jsx(HighlightedCodeBlock, { code: text, lang: lang })] }));
196
200
  }
197
201
  function ToolOutputPopover({ open, onOpenChange, title, payload, children, }) {
202
+ const t = useT();
198
203
  const [copied, setCopied] = useState(false);
199
204
  const copyResetRef = useRef(null);
200
205
  useEffect(() => {
@@ -216,7 +221,7 @@ function ToolOutputPopover({ open, onOpenChange, title, payload, children, }) {
216
221
  // Clipboard failures should not interrupt chat rendering.
217
222
  }
218
223
  }, [payload.copyText]);
219
- return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsxs(PopoverContent, { align: "start", side: "bottom", sideOffset: 6, collisionPadding: 12, className: "flex max-h-[min(calc(100vh-2rem),var(--radix-popover-content-available-height,75vh))] w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,760px),760px)] flex-col gap-0 overflow-hidden p-0", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? "Copied" : "Copy"] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden p-3", children: _jsx(SimpleCodeViewer, { text: payload.text, lang: payload.lang, maxHeightClass: "max-h-[min(70vh,calc(var(--radix-popover-content-available-height,75vh)-4.5rem))]" }) })] })] }));
224
+ return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsxs(PopoverContent, { align: "start", side: "bottom", sideOffset: 6, collisionPadding: 12, className: "flex max-h-[min(calc(100vh-2rem),var(--radix-popover-content-available-height,75vh))] w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,760px),760px)] flex-col gap-0 overflow-hidden p-0", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? t("agentChat.common.copied") : t("agentChat.common.copy")] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden p-3", children: _jsx(SimpleCodeViewer, { text: payload.text, lang: payload.lang, maxHeightClass: "max-h-[min(70vh,calc(var(--radix-popover-content-available-height,75vh)-4.5rem))]" }) })] })] }));
220
225
  }
221
226
  // ─── Collapsible height animation ─────────────────────────────────────────────
222
227
  export function AnimatedCollapse({ open, children, }) {
@@ -241,6 +246,7 @@ export function AnimatedCollapse({ open, children, }) {
241
246
  * the prompt locally (the action stays un-run).
242
247
  */
243
248
  function ApprovalAffordance({ toolName, toolCallId, approval, }) {
249
+ const t = useT();
244
250
  const ctx = React.useContext(ApprovalContext);
245
251
  const [localResolution, setLocalResolution] = useState(null);
246
252
  const retainedResolution = ctx?.getApprovalResolution?.(approval.approvalKey, toolCallId) ?? null;
@@ -250,27 +256,27 @@ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
250
256
  // Once resolved, collapse to a quiet note so a repository refresh cannot
251
257
  // restore the action buttons while the continuation is running.
252
258
  if (resolution === "approved") {
253
- return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Approved. Re-running ", toolName, "..."] }));
259
+ return (_jsx("div", { className: "mt-1.5 text-xs text-muted-foreground", children: t("agentChat.approval.approved", { tool: toolName }) }));
254
260
  }
255
261
  // Deny defaults to local-only (the action simply stays un-run). When the
256
262
  // host also provided `onDeny` (e.g. a Code session resolving its own
257
263
  // pending approval), it fires alongside the local state.
258
264
  if (resolution === "denied") {
259
- return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Denied. ", toolName, " did not run."] }));
265
+ return (_jsx("div", { className: "mt-1.5 text-xs text-muted-foreground", children: t("agentChat.approval.denied", { tool: toolName }) }));
260
266
  }
261
- return (_jsxs("div", { className: "mt-1.5 flex min-w-0 flex-wrap items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "min-w-0 flex-1 text-xs text-muted-foreground", children: ["Approve to run ", toolName, "?"] }), ctx && (_jsxs("button", { type: "button", onClick: () => {
267
+ return (_jsxs("div", { className: "mt-1.5 flex min-w-0 flex-wrap items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 text-xs text-muted-foreground", children: t("agentChat.approval.question", { tool: toolName }) }), ctx && (_jsxs("button", { type: "button", onClick: () => {
262
268
  setLocalResolution("approved");
263
269
  ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
264
270
  ctx.onApprove(approval.approvalKey);
265
- }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Approve"] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
271
+ }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.approve")] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
266
272
  setLocalResolution("approved");
267
273
  ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
268
274
  ctx.onAlwaysAllow?.(approval.approvalKey);
269
- }, title: "Approve and always allow this exact command", className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), "Always allow"] })), _jsxs("button", { type: "button", onClick: () => {
275
+ }, title: t("agentChat.approval.alwaysAllowHint"), className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.alwaysAllow")] })), _jsxs("button", { type: "button", onClick: () => {
270
276
  setLocalResolution("denied");
271
277
  ctx?.onApprovalResolved?.(approval.approvalKey, "denied", toolCallId);
272
278
  ctx?.onDeny?.(approval.approvalKey);
273
- }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), "Deny"] })] }));
279
+ }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), t("agentChat.approval.deny")] })] }));
274
280
  }
275
281
  // ─── ToolCallDisplay ──────────────────────────────────────────────────────────
276
282
  export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, structuredMeta, activity, approval, repeatCount, isLatestRunning = isRunning, isActiveTail, }) {
@@ -303,6 +309,7 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
303
309
  return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: effectiveIsRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
304
310
  }
305
311
  function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
312
+ const t = useT();
306
313
  const suppressInlineOpenApp = React.useContext(SuppressInlineOpenAppContext);
307
314
  const isRawCallAgent = toolName === "call-agent";
308
315
  const isAgentCall = toolName.startsWith("agent:") || isRawCallAgent;
@@ -362,7 +369,7 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
362
369
  if (parsed.taskId && parsed.threadId) {
363
370
  return (_jsx(AgentTaskCard, { taskId: parsed.taskId, threadId: parsed.threadId, description: parsed.description ||
364
371
  args?.task ||
365
- "Sub-agent task", onOpen: (tid) => {
372
+ t("agentChat.tool.subAgentTask"), onOpen: (tid) => {
366
373
  window.dispatchEvent(new CustomEvent("agent-task-open", {
367
374
  detail: {
368
375
  threadId: tid,
@@ -398,30 +405,38 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
398
405
  if (NativeToolRenderer) {
399
406
  return (_jsx(ActionChatUiSurface, { context: nativeToolContext, isBuiltinDataWidget: isBuiltinDataWidgetActionRenderer(nativeToolContext), children: _jsx(NativeToolRenderer, { context: nativeToolContext }) }));
400
407
  }
401
- const inputPayload = hasArgs ? toolInputPayload(toolName, args) : null;
402
- const resultPayload = toolResultPayload(result);
408
+ const inputPayload = hasArgs
409
+ ? toolInputPayload(toolName, args, {
410
+ input: t("agentChat.tool.input"),
411
+ inputWithLabel: (label) => t("agentChat.tool.inputWithLabel", { label }),
412
+ })
413
+ : null;
414
+ const resultPayload = toolResultPayload(result, t("agentChat.tool.result"));
403
415
  const displayName = isAgentCall
404
416
  ? isRunning
405
- ? `Asking ${agentName}...`
417
+ ? t("agentChat.tool.askingAgent", { agent: agentName })
406
418
  : isAgentError
407
- ? `Error asking ${agentName}`
408
- : `Asked ${agentName}`
419
+ ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
420
+ : t("agentChat.tool.askedAgent", { agent: agentName })
409
421
  : humanizeToolName(toolName);
410
422
  const canExpand = isAgentCall
411
423
  ? hasStreamText
412
424
  : hasArgs || result !== undefined;
413
425
  const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
414
426
  const ToolIcon = resolveToolIcon(toolName);
415
- const outputTitle = `Raw ${toolName} tool call output`;
427
+ const outputTitle = t("agentChat.tool.rawOutput", { tool: toolName });
416
428
  if (isAgentCall) {
417
- return (_jsx(AgentCallCell, { agentName: agentName ?? "agent", activity: agentActivity, progress: agentProgress, responseText: agentStreamText, isRunning: isRunning, isError: isAgentError, durationMs: typeof structuredMeta?.agentDurationMs === "number"
429
+ return (_jsx(AgentCallCell, { agentName: agentName ?? t("agentChat.common.agent"), activity: agentActivity, progress: agentProgress, responseText: agentStreamText, isRunning: isRunning, isError: isAgentError, durationMs: typeof structuredMeta?.agentDurationMs === "number"
418
430
  ? structuredMeta.agentDurationMs
419
431
  : agentActivity?.durationMs }));
420
432
  }
421
- return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && !(suppressInlineOpenApp && toolName === "open_app") && (_jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" })), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
433
+ return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && !(suppressInlineOpenApp && toolName === "open_app") && (_jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" })), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: t("agentChat.tool.repeated", { count: repeatCount }), children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": t("agentChat.tool.viewOutput", {
434
+ tool: toolName,
435
+ }), className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: t("agentChat.tool.interrupted") })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
422
436
  }
423
437
  function AgentCallCell({ agentName, activity, progress, responseText, isRunning, isError, durationMs, }) {
424
438
  const t = useT();
439
+ const formatDuration = useLocalizedWorkedDuration();
425
440
  const [open, setOpen] = useState(true);
426
441
  const toolCount = activity?.toolCalls?.length ?? 0;
427
442
  // Response segments are ordered against the tool calls that preceded them, so
@@ -434,10 +449,10 @@ function AgentCallCell({ agentName, activity, progress, responseText, isRunning,
434
449
  const work = activity?.reasoning?.length || toolCount || inlineSegments.length;
435
450
  const workItemCount = Math.max(activity?.reasoning?.length ?? 0, toolCount, inlineSegments.length);
436
451
  const label = isRunning
437
- ? t("agentPanel.delegatedAgent.asking", { name: agentName })
452
+ ? t("agentChat.tool.askingAgent", { agent: agentName })
438
453
  : isError
439
- ? t("agentPanel.delegatedAgent.error", { name: agentName })
440
- : t("agentPanel.delegatedAgent.asked", { name: agentName });
454
+ ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
455
+ : t("agentChat.tool.askedAgent", { agent: agentName });
441
456
  const workContent = work ? (_jsx("div", { className: "space-y-1 ps-5", children: Array.from({ length: workItemCount }, (_, index) => {
442
457
  const reasoningText = activity?.reasoning?.[index];
443
458
  const segment = inlineSegments[index];
@@ -452,8 +467,8 @@ function AgentCallCell({ agentName, activity, progress, responseText, isRunning,
452
467
  const progressText = isRunning && !activity && progress && progressState
453
468
  ? [
454
469
  progressState.charAt(0).toUpperCase() + progressState.slice(1),
455
- t("agentPanel.delegatedAgent.elapsed", {
456
- duration: formatWorkedDuration(progress.elapsedSeconds * 1000),
470
+ t("agentChat.tool.elapsed", {
471
+ duration: formatDuration(progress.elapsedSeconds * 1000),
457
472
  }),
458
473
  progress.detail,
459
474
  ]
@@ -603,6 +618,8 @@ function isReconnectToolSummaryPart(content, index, startIndex) {
603
618
  */
604
619
  const WorkSummaryContentContext = React.createContext(false);
605
620
  export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoCollapse = false, collapseWhenReplaced = false, durationMs, }) {
621
+ const t = useT();
622
+ const formatDuration = useLocalizedWorkedDuration();
606
623
  const embeddedInWorkSummary = React.useContext(WorkSummaryContentContext);
607
624
  const [open, setOpen] = useState(defaultOpen ?? true);
608
625
  const wasStreamingRef = useRef(isStreaming);
@@ -630,35 +647,54 @@ export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoColl
630
647
  return (_jsx("div", { className: "pb-1 pl-5 text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }));
631
648
  }
632
649
  const label = isStreaming
633
- ? "Thinking"
650
+ ? t("agentChat.status.thinking")
634
651
  : durationMs != null
635
- ? `Thought for ${formatWorkedDuration(durationMs)}`
636
- : "Thought";
652
+ ? t("agentChat.tool.thoughtFor", {
653
+ duration: formatDuration(durationMs),
654
+ })
655
+ : t("agentChat.tool.thought");
637
656
  // Only clamp to a scroll-free "tail" view while actively streaming and
638
657
  // expanded — once the run finishes the full text is shown, unclamped.
639
658
  const showTail = isStreaming && open;
640
659
  return (_jsxs("div", { className: "my-0.5 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") }), isStreaming ? (_jsx("span", { className: "agent-thinking-indicator__text", children: label })) : (_jsx("span", { children: label }))] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: cn("pl-5 pb-1", showTail && "reasoning-cell-tail"), children: _jsx("div", { className: "text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }) }) })] }));
641
660
  }
642
661
  // ─── Worked-for duration helpers ──────────────────────────────────────────────
643
- export function formatWorkedDuration(ms) {
662
+ export function formatWorkedDuration(ms, options = {}) {
663
+ const number = new Intl.NumberFormat(options.locale ?? "en-US");
664
+ const hour = options.hour ?? "h";
665
+ const minute = options.minute ?? "m";
666
+ const second = options.second ?? "s";
667
+ const part = (value, unit) => `${number.format(value)}${unit}`;
644
668
  const totalSeconds = Math.max(0, Math.round(ms / 1000));
645
669
  if (totalSeconds < 60) {
646
- return totalSeconds <= 1 ? "1s" : `${totalSeconds}s`;
670
+ return part(Math.max(1, totalSeconds), second);
647
671
  }
648
672
  const minutes = Math.floor(totalSeconds / 60);
649
673
  const seconds = totalSeconds % 60;
650
674
  if (minutes < 60) {
651
675
  if (seconds === 0)
652
- return `${minutes}m`;
653
- return `${minutes}m ${seconds}s`;
676
+ return part(minutes, minute);
677
+ return `${part(minutes, minute)} ${part(seconds, second)}`;
654
678
  }
655
679
  const hours = Math.floor(minutes / 60);
656
680
  const remMinutes = minutes % 60;
657
681
  if (remMinutes === 0)
658
- return `${hours}h`;
659
- return `${hours}h ${remMinutes}m`;
682
+ return part(hours, hour);
683
+ return `${part(hours, hour)} ${part(remMinutes, minute)}`;
684
+ }
685
+ export function useLocalizedWorkedDuration() {
686
+ const t = useT();
687
+ const locale = useOptionalLocale()?.locale ?? "en-US";
688
+ return useCallback((ms) => formatWorkedDuration(ms, {
689
+ locale,
690
+ hour: t("agentChat.duration.hourShort"),
691
+ minute: t("agentChat.duration.minuteShort"),
692
+ second: t("agentChat.duration.secondShort"),
693
+ }), [locale, t]);
660
694
  }
661
695
  export function WorkedForSummary({ durationMs, defaultOpen = false, autoCollapse = false, children, }) {
696
+ const t = useT();
697
+ const formatDuration = useLocalizedWorkedDuration();
662
698
  // Ordinary completed work starts closed so a remount never flashes details
663
699
  // while auto-collapse settles. Interactive UI opts into an open summary.
664
700
  const [open, setOpen] = useState(defaultOpen);
@@ -671,13 +707,16 @@ export function WorkedForSummary({ durationMs, defaultOpen = false, autoCollapse
671
707
  }
672
708
  }, [autoCollapse, defaultOpen]);
673
709
  const label = durationMs != null && durationMs >= 1000
674
- ? `Worked for ${formatWorkedDuration(durationMs)}`
675
- : "Worked";
710
+ ? t("agentChat.tool.workedFor", {
711
+ duration: formatDuration(durationMs),
712
+ })
713
+ : t("agentChat.tool.worked");
676
714
  return (_jsxs("div", { className: "my-1 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx("span", { children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), _jsx(AnimatedCollapse, { open: open, children: _jsx(WorkSummaryContentContext.Provider, { value: true, children: _jsx("div", { className: "pt-1", children: children }) }) })] }));
677
715
  }
678
716
  export function RanToolsSummary({ toolCount, motionKey = "summary", children, }) {
717
+ const t = useT();
679
718
  const [open, setOpen] = useState(false);
680
- const label = `Ran ${toolCount} ${toolCount === 1 ? "tool" : "tools"}`;
719
+ const label = t("agentChat.tool.ranTools", { count: toolCount });
681
720
  return (_jsxs("div", { className: "agent-tool-summary my-1 w-full", "data-agent-tool-summary": motionKey, children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx("span", { className: "agent-tool-summary__label", children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: "pt-1", children: children }) })] }));
682
721
  }
683
722
  // ─── Re-export for AssistantMessage ───────────────────────────────────────────