@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
@@ -32,6 +32,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./com
32
32
  import { AgentComposerFrame, TiptapComposer, } from "./composer/index.js";
33
33
  import { useAgentDynamicSuggestionsResult, } from "./dynamic-suggestions.js";
34
34
  import { GuidedQuestionFlow, useGuidedQuestionFlow, } from "./guided-questions.js";
35
+ import { useT } from "./i18n.js";
35
36
  import { buildSignInReturnHref } from "./require-session.js";
36
37
  import { addMcpConnectionCompleteListener, consumeMcpConnectionResume, } from "./resources/mcp-connection-resume.js";
37
38
  import { McpConnectionSuggestion } from "./resources/McpConnectionSuggestion.js";
@@ -365,6 +366,7 @@ function getImageAttachmentSrc(attachment) {
365
366
  return imagePart && "image" in imagePart ? imagePart.image : null;
366
367
  }
367
368
  function ComposerAttachmentPreviewCard({ attachment, onRemove, }) {
369
+ const t = useT();
368
370
  const [imageSrc, setImageSrc] = useState(null);
369
371
  useEffect(() => {
370
372
  const nextSrc = getImageAttachmentSrc(attachment);
@@ -383,7 +385,9 @@ function ComposerAttachmentPreviewCard({ attachment, onRemove, }) {
383
385
  ? "h-20 w-20 rounded-xl shadow-[0_12px_30px_-18px_rgba(0,0,0,0.7)]"
384
386
  : "inline-flex max-w-[220px] items-center gap-2 rounded-lg px-2.5 py-2 text-xs"), children: [isImage ? (_jsxs(_Fragment, { children: [_jsx("img", { src: imageSrc, alt: attachment.name, className: "h-full w-full object-cover" }), _jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent px-2 py-1.5", children: _jsx("div", { className: "truncate text-[10px] font-medium text-white/95", children: attachment.name }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-background text-[10px] font-semibold uppercase tracking-[0.12em] text-muted-foreground", children: attachment.name.split(".").pop() || "file" }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium", children: attachment.name }), _jsx("div", { className: "text-[11px] text-muted-foreground", children: attachment.contentType || attachment.type })] })] })), _jsx("button", { type: "button", onClick: () => onRemove(attachment.id), className: cn("absolute flex h-6 w-6 items-center justify-center rounded-full border border-border/60 bg-background/95 text-muted-foreground shadow-sm transition hover:text-foreground", isImage
385
387
  ? "end-1.5 top-1.5 opacity-100 md:opacity-0 md:group-hover:opacity-100"
386
- : "end-1.5 top-1.5"), "aria-label": `Remove ${attachment.name}`, children: _jsx(IconX, { className: "h-3 w-3" }) })] }));
388
+ : "end-1.5 top-1.5"), "aria-label": t("agentChat.composer.removeAttachment", {
389
+ name: attachment.name,
390
+ }), children: _jsx(IconX, { className: "h-3 w-3" }) })] }));
387
391
  }
388
392
  function ComposerAttachmentPreviewStrip() {
389
393
  const attachments = useComposer((state) => state.attachments);
@@ -980,14 +984,38 @@ export function resolveAssistantChatRunningState({ forceStopped, isRuntimeRunnin
980
984
  showRunningInUI: !forceStopped && !hasTerminalRunError && (isRunning || isAutoResuming),
981
985
  };
982
986
  }
983
- export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, }) {
984
- if (runningActivityLabel)
987
+ export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, labels = {
988
+ thinking: "Thinking",
989
+ resuming: "Resuming",
990
+ stillWorking: "Still working",
991
+ }, }) {
992
+ if (runningActivityLabel) {
993
+ if (runningActivityLabel === "Thinking")
994
+ return labels.thinking;
995
+ if (runningActivityLabel === "Working") {
996
+ return labels.working ?? "Working";
997
+ }
998
+ if (runningActivityLabel === "Contacting model") {
999
+ return labels.contactingModel ?? "Contacting model";
1000
+ }
1001
+ const localizedActivityPatterns = [
1002
+ [/^Starting (.+)\.\.\.$/, labels.starting],
1003
+ [/^Preparing (.+)\.\.\.$/, labels.preparing],
1004
+ [/^Writing (.+)\.\.\.$/, labels.writing],
1005
+ [/^Still generating (.+)$/, labels.stillGenerating],
1006
+ ];
1007
+ for (const [pattern, translateActivity] of localizedActivityPatterns) {
1008
+ const match = runningActivityLabel.match(pattern);
1009
+ if (match?.[1] && translateActivity)
1010
+ return translateActivity(match[1]);
1011
+ }
985
1012
  return runningActivityLabel;
1013
+ }
986
1014
  if (isAutoResuming)
987
- return "Resuming";
1015
+ return labels.resuming;
988
1016
  if (isReconnecting && hasReconnectContent)
989
- return "Still working";
990
- return "Thinking";
1017
+ return labels.stillWorking;
1018
+ return labels.thinking;
991
1019
  }
992
1020
  export function resolveAssistantChatComposerPlaceholder(composerPlaceholder) {
993
1021
  return composerPlaceholder ?? DEFAULT_ASSISTANT_CHAT_COMPOSER_PLACEHOLDER;
@@ -1109,6 +1137,9 @@ function AssistantChatScrollerControls({ resumeFollowingRef, }) {
1109
1137
  }, [resumeFollowingRef, scrollToEnd]);
1110
1138
  return null;
1111
1139
  }
1140
+ export function shouldShowAssistantChatModelSelector(showModelSelector) {
1141
+ return showModelSelector !== false;
1142
+ }
1112
1143
  export const CHAT_STORAGE_PREFIX = "agent-chat:";
1113
1144
  const THREAD_SNAPSHOT_CACHE_PREFIX = `${CHAT_STORAGE_PREFIX}thread-snapshot:`;
1114
1145
  function threadSnapshotCacheKey(apiUrl, threadId) {
@@ -1344,7 +1375,8 @@ export function useAutoResumeStatus(tabId, forceStopped) {
1344
1375
  function approvalResolutionIdentity(approvalKey, toolCallId) {
1345
1376
  return `${toolCallId ?? ""}\u0000${approvalKey}`;
1346
1377
  }
1347
- const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, threadFooterSlot, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, onComposerTextChange, composerAreaClassName, composerPlaceholder, missingApiKeySetupLayout = "default", composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, isThreadStateLoading, onSlashCommand, execMode, onExecModeChange, approvalActions, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, modelListLoading, onModelChange, onEffortChange, imageModelMenu, onForkChat, onConnectProvider, onConnectLocalRuntime, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, agentChatSurface = "app", suppressInlineOpenApp = false, }, ref) {
1378
+ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, threadFooterSlot, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, onComposerTextChange, composerAreaClassName, composerPlaceholder, missingApiKeySetupLayout = "default", composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, showModelSelector = true, composerDisabled = false, composerDisabledPlaceholder, isNewThread, isThreadStateLoading, onSlashCommand, execMode, onExecModeChange, approvalActions, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, modelListLoading, onModelChange, onEffortChange, imageModelMenu, onForkChat, onConnectProvider, onConnectLocalRuntime, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, agentChatSurface = "app", suppressInlineOpenApp = false, }, ref) {
1379
+ const t = useT();
1348
1380
  const thread = useThread();
1349
1381
  const threadRuntime = useThreadRuntime();
1350
1382
  const composerRuntime = useComposerRuntime();
@@ -1441,10 +1473,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1441
1473
  void Promise.all(attachments.map((file) => composerRuntime.addAttachment(file))).catch((error) => {
1442
1474
  const msg = error instanceof Error
1443
1475
  ? error.message
1444
- : "Could not add the dropped file. Try a different format.";
1476
+ : t("agentChat.composer.droppedFileError");
1445
1477
  setComposerError(msg);
1446
1478
  });
1447
- }, [composerRuntime, setComposerError]);
1479
+ }, [composerRuntime, setComposerError, t]);
1448
1480
  // Patch the underlying assistant-ui MessageRepository so addOrUpdateMessage
1449
1481
  // can't throw "Parent message not found" mid-run. assistant-ui calls
1450
1482
  // `repository.clear()` from `runtime.import()` and from `resetHead(null)`,
@@ -1721,6 +1753,17 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1721
1753
  isAutoResuming,
1722
1754
  isReconnecting,
1723
1755
  hasReconnectContent: reconnectContent.length > 0,
1756
+ labels: {
1757
+ thinking: t("agentChat.status.thinking"),
1758
+ resuming: t("agentChat.status.resuming"),
1759
+ stillWorking: t("agentChat.status.stillWorking"),
1760
+ working: t("agentChat.status.working"),
1761
+ contactingModel: t("agentChat.status.contactingModel"),
1762
+ starting: (activity) => t("agentChat.status.starting", { activity }),
1763
+ preparing: (activity) => t("agentChat.status.preparing", { activity }),
1764
+ writing: (activity) => t("agentChat.status.writing", { activity }),
1765
+ stillGenerating: (activity) => t("agentChat.status.stillGenerating", { activity }),
1766
+ },
1724
1767
  });
1725
1768
  const reconnectActivityContent = useMemo(() => isReconnecting || reconnectFrozen
1726
1769
  ? reconnectActivityFallbackContent(runningActivityTool)
@@ -2375,10 +2418,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2375
2418
  }
2376
2419
  setRunErrorInfo({
2377
2420
  message: reconnectErrorCode === "run_timeout"
2378
- ? "The previous background agent run reached its time limit before finishing. The partial work was preserved; continue or retry to pick up from here."
2421
+ ? t("agentChat.recovery.backgroundTimeout")
2379
2422
  : reconnectErrorCode === "reconnect_stream_ended"
2380
- ? "The previous agent stream ended while the run was recovering. Continue or retry to reconnect to the run."
2381
- : "The previous agent run stopped producing visible progress during recovery, so it was stopped before it could keep looping.",
2423
+ ? t("agentChat.recovery.streamEnded")
2424
+ : t("agentChat.recovery.noProgress"),
2382
2425
  errorCode: reconnectErrorCode,
2383
2426
  recoverable: true,
2384
2427
  runId,
@@ -2453,7 +2496,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2453
2496
  };
2454
2497
  void streamReconnect();
2455
2498
  return true;
2456
- }, [apiUrl, refreshThreadFromServer, tabId, threadId, wasRecentlyStoppedRun]);
2499
+ }, [
2500
+ apiUrl,
2501
+ refreshThreadFromServer,
2502
+ t,
2503
+ tabId,
2504
+ threadId,
2505
+ wasRecentlyStoppedRun,
2506
+ ]);
2457
2507
  const reconnectActiveRunForThread = useCallback(async () => {
2458
2508
  if (!threadId)
2459
2509
  return false;
@@ -2489,7 +2539,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2489
2539
  clearActiveRunIfMatches(threadId, storedActiveRun.runId);
2490
2540
  window.dispatchEvent(new CustomEvent("agent-chat:run-error", {
2491
2541
  detail: {
2492
- message: "Couldn't reach the server to check whether the agent is still working. Send your message again to retry.",
2542
+ message: t("agentChat.recovery.statusCheckFailed"),
2493
2543
  errorCode: "run_status_unavailable",
2494
2544
  recoverable: true,
2495
2545
  ...(storedActiveRun.runId
@@ -2520,7 +2570,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2520
2570
  catch {
2521
2571
  return false;
2522
2572
  }
2523
- }, [apiUrl, refreshThreadFromServer, startReconnectToRun, tabId, threadId]);
2573
+ }, [
2574
+ apiUrl,
2575
+ refreshThreadFromServer,
2576
+ startReconnectToRun,
2577
+ t,
2578
+ tabId,
2579
+ threadId,
2580
+ ]);
2524
2581
  useEffect(() => {
2525
2582
  if (!threadId || !isNewThread)
2526
2583
  return;
@@ -3529,7 +3586,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3529
3586
  catch (err) {
3530
3587
  const msg = err instanceof Error
3531
3588
  ? err.message
3532
- : "Attachment could not be processed.";
3589
+ : t("agentChat.composer.attachmentError");
3533
3590
  setComposerError(msg);
3534
3591
  reportAgentChatSubmitResult(submitMessageId, false, "attachment-error");
3535
3592
  return;
@@ -3721,6 +3778,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3721
3778
  selectedEffort,
3722
3779
  selectedEngine,
3723
3780
  selectedModel,
3781
+ t,
3724
3782
  updateComposerContextItems,
3725
3783
  ]);
3726
3784
  const mcpResumeTimerRef = useRef(null);
@@ -4040,7 +4098,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4040
4098
  getApprovalResolution,
4041
4099
  onApprovalResolved: recordApprovalResolution,
4042
4100
  onApprove: (approvalKey) => {
4043
- void addToQueue("Approved. Go ahead and run the requested action.", undefined, undefined, undefined, undefined, "queued", undefined, false, false, false, true, // hideUserMessage: this is a protocol continuation, not a new prompt
4101
+ void addToQueue("Approved. Go ahead and run the requested action.", // i18n-ignore -- stable hidden agent instruction, not UI copy.
4102
+ undefined, undefined, undefined, undefined, "queued", undefined, false, false, false, true, // hideUserMessage: this is a protocol continuation, not a new prompt
4044
4103
  undefined, [approvalKey]);
4045
4104
  },
4046
4105
  ...(approvalActions?.onDeny ? { onDeny: approvalActions.onDeny } : {}),
@@ -4070,19 +4129,19 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4070
4129
  ? "centered"
4071
4130
  : compactMissingKeyEmptyState
4072
4131
  ? "compact-setup"
4073
- : undefined, className: cn("relative flex flex-1 flex-col h-full min-h-0 text-foreground", className), onDragEnter: handleChatDragEnter, onDragOver: handleChatDragOver, onDragLeave: handleChatDragLeave, onDropCapture: handleChatDropCapture, onDrop: handleChatDrop, children: [dropActive && (_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-50 flex items-center justify-center rounded-md border-2 border-dashed border-primary/70 bg-primary/5 backdrop-blur-[1px]", children: _jsx("span", { className: "rounded-md bg-background/90 px-3 py-1.5 text-xs font-medium text-foreground shadow-sm", children: "Drop to attach" }) })), showHeader && (_jsxs("div", { className: "flex h-11 shrink-0 items-center justify-between border-b border-border px-4", children: [_jsx("span", { className: "text-[13px] font-medium text-muted-foreground", children: "Agent" }), _jsx("div", { className: "flex items-center gap-1", children: onSwitchToCli && (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { onClick: onSwitchToCli, "aria-label": "Switch to CLI", className: "flex items-center gap-1 text-[12px] text-muted-foreground hover:text-foreground px-2 py-1 rounded-md hover:bg-accent", children: [_jsx(IconTerminal, { className: "h-3.5 w-3.5" }), "CLI"] }) }), _jsx(TooltipContent, { children: "Switch to CLI" })] }) })) })] })), _jsxs(MessageScrollerProvider, { autoScroll: true, children: [_jsx(AssistantChatScrollerControls, { resumeFollowingRef: resumeFollowingRef }), _jsxs(MessageScroller, { className: "agent-chat-scroll", children: [_jsx(MessageScrollerViewport, { children: authError ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full px-4 gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-muted", children: authSessionAvailable ? (_jsx(IconRefresh, { className: "h-5 w-5 text-muted-foreground" })) : (_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground" })) }), _jsxs("div", { className: "text-center max-w-[280px]", children: [_jsx("p", { className: "text-sm font-medium text-foreground mb-1", children: authSessionAvailable
4074
- ? "Chat session needs refresh"
4132
+ : undefined, className: cn("relative flex flex-1 flex-col h-full min-h-0 text-foreground", className), onDragEnter: handleChatDragEnter, onDragOver: handleChatDragOver, onDragLeave: handleChatDragLeave, onDropCapture: handleChatDropCapture, onDrop: handleChatDrop, children: [dropActive && (_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-50 flex items-center justify-center rounded-md border-2 border-dashed border-primary/70 bg-primary/5 backdrop-blur-[1px]", children: _jsx("span", { className: "rounded-md bg-background/90 px-3 py-1.5 text-xs font-medium text-foreground shadow-sm", children: t("agentChat.composer.dropToAttach") }) })), showHeader && (_jsxs("div", { className: "flex h-11 shrink-0 items-center justify-between border-b border-border px-4", children: [_jsx("span", { className: "text-[13px] font-medium text-muted-foreground", children: "Agent" }), _jsx("div", { className: "flex items-center gap-1", children: onSwitchToCli && (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { onClick: onSwitchToCli, "aria-label": t("agentChat.header.switchToCli"), className: "flex items-center gap-1 text-[12px] text-muted-foreground hover:text-foreground px-2 py-1 rounded-md hover:bg-accent", children: [_jsx(IconTerminal, { className: "h-3.5 w-3.5" }), "CLI"] }) }), _jsx(TooltipContent, { children: t("agentChat.header.switchToCli") })] }) })) })] })), _jsxs(MessageScrollerProvider, { autoScroll: true, children: [_jsx(AssistantChatScrollerControls, { resumeFollowingRef: resumeFollowingRef }), _jsxs(MessageScroller, { className: "agent-chat-scroll", children: [_jsx(MessageScrollerViewport, { children: authError ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full px-4 gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-muted", children: authSessionAvailable ? (_jsx(IconRefresh, { className: "h-5 w-5 text-muted-foreground" })) : (_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground" })) }), _jsxs("div", { className: "text-center max-w-[280px]", children: [_jsx("p", { className: "text-sm font-medium text-foreground mb-1", children: authSessionAvailable
4133
+ ? t("agentChat.auth.refreshTitle")
4075
4134
  : authError.sessionExpired
4076
- ? "Session expired"
4077
- : "Authentication required" }), _jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: authSessionAvailable
4078
- ? "You're signed in, but this chat connection needs to reconnect."
4135
+ ? t("agentChat.auth.expiredTitle")
4136
+ : t("agentChat.auth.requiredTitle") }), _jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: authSessionAvailable
4137
+ ? t("agentChat.auth.refreshDescription")
4079
4138
  : authError.sessionExpired
4080
- ? "Your session may have expired. Log out and log back in to reconnect."
4081
- : "You need to log in to use the agent." })] }), _jsxs("div", { className: "flex gap-2", children: [!authError.sessionExpired &&
4139
+ ? t("agentChat.auth.expiredDescription")
4140
+ : t("agentChat.auth.requiredDescription") })] }), _jsxs("div", { className: "flex gap-2", children: [!authError.sessionExpired &&
4082
4141
  !authSessionAvailable && (_jsx("button", { onClick: () => {
4083
4142
  window.location.href =
4084
4143
  buildSignInReturnHref();
4085
- }, className: "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md", children: "Log in" })), authError.sessionExpired &&
4144
+ }, className: "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md", children: t("agentChat.auth.logIn") })), authError.sessionExpired &&
4086
4145
  !authSessionAvailable && (_jsx("button", { onClick: async () => {
4087
4146
  try {
4088
4147
  await fetch(agentNativePath("/_agent-native/auth/logout"), {
@@ -4093,16 +4152,18 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4093
4152
  console.error("Failed to log out before reloading the chat", error);
4094
4153
  }
4095
4154
  window.location.reload();
4096
- }, className: "text-xs text-destructive hover:text-destructive/80 px-3 py-1.5 rounded-md border border-destructive/30 hover:bg-destructive/10", children: "Log out" })), _jsx("button", { onClick: () => {
4155
+ }, className: "text-xs text-destructive hover:text-destructive/80 px-3 py-1.5 rounded-md border border-destructive/30 hover:bg-destructive/10", children: t("agentChat.auth.logOut") })), _jsx("button", { onClick: () => {
4097
4156
  setAuthError(null);
4098
4157
  window.location.reload();
4099
4158
  }, className: authSessionAvailable
4100
4159
  ? "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md"
4101
- : "text-xs text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-accent", children: "Refresh chat" })] })] })) : isRestoring && centeredRestoringState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
4160
+ : "text-xs text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-accent", children: t("agentChat.auth.refreshChat") })] })] })) : isRestoring && centeredRestoringState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
4102
4161
  ? "sr-only"
4103
- : "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), "aria-busy": "true", children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ?? "Loading chat..." })] })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) : showEmptyState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
4162
+ : "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), "aria-busy": "true", children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ??
4163
+ t("agentChat.empty.loadingChat") })] })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) : showEmptyState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
4104
4164
  ? "sr-only"
4105
- : "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ?? "How can I help you?" }), emptyStateAddon, resolvedSuggestions &&
4165
+ : "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ??
4166
+ t("agentChat.empty.prompt") }), emptyStateAddon, resolvedSuggestions &&
4106
4167
  resolvedSuggestions.length > 0 ? (_jsx("div", { className: "flex w-full max-w-[280px] flex-col gap-1", children: resolvedSuggestions.map((suggestion) => (_jsx("button", { onClick: () => {
4107
4168
  if (engineSetupRequired) {
4108
4169
  requestMissingKeySetup();
@@ -4110,13 +4171,33 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4110
4171
  }
4111
4172
  void addToQueue(suggestion);
4112
4173
  }, className: "w-full px-2 py-1 text-center text-[13px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasRecentlyStoppedRun, children: _jsx(ThreadPrimitive.Messages
4113
- // assistant-ui indexes message parts through tap resources.
4114
- // Keep this key tied to that shape so clear/add transitions remount stale
4115
- // lookups without remounting on every streamed text update.
4116
- , { components: {
4174
+ // Deliberately NOT keyed on part structure. Doing that
4175
+ // remounted the whole transcript every time a tool call
4176
+ // started or a placeholder id was rewritten — a flash and a
4177
+ // lost scroll position in the middle of an answer. The
4178
+ // error boundary above is the mechanism for assistant-ui's
4179
+ // stale tap-resource errors: it catches them, clears, and
4180
+ // retries, and its retry signature includes the reset key so
4181
+ // a genuinely new structure always gets a fresh budget.
4182
+ // `assistant-ui-part-churn.spec.tsx` drives append, mutate,
4183
+ // rename and splice through both the import and streaming
4184
+ // paths and records that no such error occurs.
4185
+ , {
4186
+ // Deliberately NOT keyed on part structure. Doing that
4187
+ // remounted the whole transcript every time a tool call
4188
+ // started or a placeholder id was rewritten — a flash and a
4189
+ // lost scroll position in the middle of an answer. The
4190
+ // error boundary above is the mechanism for assistant-ui's
4191
+ // stale tap-resource errors: it catches them, clears, and
4192
+ // retries, and its retry signature includes the reset key so
4193
+ // a genuinely new structure always gets a fresh budget.
4194
+ // `assistant-ui-part-churn.spec.tsx` drives append, mutate,
4195
+ // rename and splice through both the import and streaming
4196
+ // paths and records that no such error occurs.
4197
+ components: {
4117
4198
  UserMessage: AssistantChatUserMessageItem,
4118
4199
  AssistantMessage: AssistantChatAssistantMessageItem,
4119
- } }, messageListResetKey) }) }), visibleLoopLimit && !showRunningInUI && (_jsx(MessageScrollerItem, { children: _jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
4200
+ } }) }) }), visibleLoopLimit && !showRunningInUI && (_jsx(MessageScrollerItem, { children: _jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
4120
4201
  setShowContinue(false);
4121
4202
  setLoopLimitInfo(null);
4122
4203
  addToQueue("Continue from where you left off.", undefined, undefined, undefined, undefined, "queued", "continue");
@@ -4132,10 +4213,12 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4132
4213
  visibleReconnectContent.length > 0 && (_jsx(MessageScrollerItem, { children: _jsx(ReconnectStreamMessage, { content: visibleReconnectContent, allowActivitySpinner: !reconnectFrozen }) })), showReconnectOverlay &&
4133
4214
  visibleReconnectContent.length === 0 &&
4134
4215
  reconnectContent.length === 0 &&
4135
- reconnectActivityContent.length > 0 && (_jsx(MessageScrollerItem, { children: _jsx(ReconnectStreamMessage, { content: reconnectActivityContent, allowActivitySpinner: !reconnectFrozen }) })), showGlobalRunningStatus && (_jsx(MessageScrollerItem, { children: _jsx(RunningActivityStatus, { label: runningStatusLabel }) })), visibleQueuedMessages.length > 0 && (_jsx(MessageScrollerItem, { children: _jsxs("div", { className: "flex items-center justify-end gap-1.5 pr-0.5 text-xs text-muted-foreground", children: [_jsx(IconClock, { className: "h-3 w-3" }), _jsxs("span", { children: [visibleQueuedMessages.length, " ", "queued"] })] }) })), visibleQueuedMessages.map((msg) => {
4216
+ reconnectActivityContent.length > 0 && (_jsx(MessageScrollerItem, { children: _jsx(ReconnectStreamMessage, { content: reconnectActivityContent, allowActivitySpinner: !reconnectFrozen }) })), showGlobalRunningStatus && (_jsx(MessageScrollerItem, { children: _jsx(RunningActivityStatus, { label: runningStatusLabel }) })), visibleQueuedMessages.length > 0 && (_jsx(MessageScrollerItem, { children: _jsxs("div", { className: "flex items-center justify-end gap-1.5 pr-0.5 text-xs text-muted-foreground", children: [_jsx(IconClock, { className: "h-3 w-3" }), _jsx("span", { children: t("agentChat.queue.count", {
4217
+ count: visibleQueuedMessages.length,
4218
+ }) })] }) })), visibleQueuedMessages.map((msg) => {
4136
4219
  const displayText = displayableUserMessageText(msg.text);
4137
4220
  const imageSources = queuedMessageImageSources(msg);
4138
- return (_jsx(MessageScrollerItem, { messageId: msg.id, children: _jsxs("div", { className: "group flex items-start justify-end gap-1.5", children: [isRunning && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => sendQueuedMessageNow(msg.id), "aria-label": "Send now", className: "mt-1.5 flex h-5 w-5 cursor-pointer items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100", children: _jsx(IconArrowUp, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: "Send now (stops the current response)" })] })), _jsx("button", { type: "button", onClick: () => applyLocalQueuedMessages((prev) => prev.filter((m) => m.id !== msg.id)), "aria-label": "Remove from queue", className: "mt-1.5 flex h-5 w-5 cursor-pointer items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) }), _jsxs("div", { className: "max-w-[85%] rounded-lg bg-accent/50 px-3 py-2 text-sm leading-relaxed text-foreground/60 whitespace-pre-wrap break-words", children: [displayText, imageSources.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5", children: imageSources.map((img, j) => (_jsx("img", { src: img, alt: "", className: "h-12 w-12 rounded object-cover border border-border/50" }, j))) }))] })] }) }, msg.id));
4221
+ return (_jsx(MessageScrollerItem, { messageId: msg.id, children: _jsxs("div", { className: "group flex items-start justify-end gap-1.5", children: [isRunning && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => sendQueuedMessageNow(msg.id), "aria-label": t("agentChat.queue.sendNow"), className: "mt-1.5 flex h-5 w-5 cursor-pointer items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100", children: _jsx(IconArrowUp, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.queue.sendNowHint") })] })), _jsx("button", { type: "button", onClick: () => applyLocalQueuedMessages((prev) => prev.filter((m) => m.id !== msg.id)), "aria-label": t("agentChat.queue.remove"), className: "mt-1.5 flex h-5 w-5 cursor-pointer items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 shadow-sm transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 group-hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) }), _jsxs("div", { className: "max-w-[85%] rounded-lg bg-accent/50 px-3 py-2 text-sm leading-relaxed text-foreground/60 whitespace-pre-wrap break-words", children: [displayText, imageSources.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5", children: imageSources.map((img, j) => (_jsx("img", { src: img, alt: "", className: "h-12 w-12 rounded object-cover border border-border/50" }, j))) }))] })] }) }, msg.id));
4139
4222
  }), resolvedThreadFooterSlot ? (_jsx(MessageScrollerItem, { children: _jsx("div", { className: "agent-thread-footer-slot", children: resolvedThreadFooterSlot }) })) : null] })) }), !authError && !isRestoring && !showEmptyState ? (_jsx(MessageScrollerButton, {})) : null] })] }, chatScrollResetKey), showComposerSlot ? composerSlot : null, isActiveComposer && (_jsx(McpConnectionSuggestion, { text: composerText })), showCenteredEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--centered-empty", children: resolvedThreadFooterSlot })) : null, guidedQuestions && guidedQuestions.length > 0 && (_jsx("div", { className: "shrink-0 px-3 pb-2", children: _jsx(GuidedQuestionFlow, { questions: guidedQuestions, onSubmit: handleGuidedQuestionsSubmit, onSkip: handleGuidedQuestionsSkip, ...(guidedQuestionsTitle
4140
4223
  ? { title: guidedQuestionsTitle }
4141
4224
  : {}), ...(guidedQuestionsDescription
@@ -4144,7 +4227,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4144
4227
  ? { skipLabel: guidedQuestionsSkipLabel }
4145
4228
  : {}), ...(guidedQuestionsSubmitLabel
4146
4229
  ? { submitLabel: guidedQuestionsSubmitLabel }
4147
- : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) })), composerError && (_jsxs("div", { role: "alert", className: "shrink-0 mx-3 mb-1.5 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), _jsx("span", { className: "flex-1 leading-snug", children: composerError }), _jsx("button", { type: "button", "aria-label": "Dismiss error", onClick: () => setComposerError(null), className: "shrink-0 opacity-70 hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) })] })), _jsxs("div", { className: "agent-composer-stack", "data-agent-composer-adjacent-ui": hasComposerAccessoryAboveStack
4230
+ : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) })), composerError && (_jsxs("div", { role: "alert", className: "shrink-0 mx-3 mb-1.5 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), _jsx("span", { className: "flex-1 leading-snug", children: composerError }), _jsx("button", { type: "button", "aria-label": t("agentChat.common.dismissError"), onClick: () => setComposerError(null), className: "shrink-0 opacity-70 hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) })] })), _jsxs("div", { className: "agent-composer-stack", "data-agent-composer-adjacent-ui": hasComposerAccessoryAboveStack
4148
4231
  ? "true"
4149
4232
  : undefined, children: [_jsx(SelectionAttachedPill, {}), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), showInlineMissingKeySetup ? (_jsx(BuilderSetupCard, { fullWidth: true, layout: "sidebar", onConnected: handleBuilderConnected })) : null, _jsxs(Popover, { open: showMissingKeySetup &&
4150
4233
  !showInlineMissingKeySetup &&
@@ -4152,24 +4235,26 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4152
4235
  !showMissingKeySetup &&
4153
4236
  "opacity-70"), rootClassName: cn(showMissingKeySetup &&
4154
4237
  "agent-composer-root--missing-key"), children: showMissingKeySetup &&
4155
- !showInlineMissingKeySetup ? (_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "agent-composer-missing-key-trigger", "aria-label": "Connect AI to start chatting", children: _jsxs("span", { className: "agent-composer-missing-key-content", children: [_jsxs("span", { className: "agent-composer-missing-key-copy", children: [_jsx("span", { className: "agent-composer-missing-key-title", children: missingApiKeySetupLayout ===
4238
+ !showInlineMissingKeySetup ? (_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "agent-composer-missing-key-trigger", "aria-label": t("agentChat.setup.connectToStart"), children: _jsxs("span", { className: "agent-composer-missing-key-content", children: [_jsxs("span", { className: "agent-composer-missing-key-copy", children: [_jsx("span", { className: "agent-composer-missing-key-title", children: missingApiKeySetupLayout ===
4156
4239
  "sidebar"
4157
- ? "Connect AI to chat"
4158
- : "Connect AI to start chatting" }), missingApiKeySetupLayout !==
4159
- "sidebar" ? (_jsx("span", { className: "agent-composer-missing-key-description", children: "Builder.io includes free credits, or use your own API key." })) : null] }), _jsx("span", { className: "agent-composer-missing-key-cta", children: "Connect AI" })] }) }) })) : (_jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, onTextChange: isActiveComposer
4240
+ ? t("agentChat.setup.connectToChat")
4241
+ : t("agentChat.setup.connectToStart") }), missingApiKeySetupLayout !==
4242
+ "sidebar" ? (_jsx("span", { className: "agent-composer-missing-key-description", children: t("agentChat.setup.builderCredits") })) : null] }), _jsx("span", { className: "agent-composer-missing-key-cta", children: t("agentChat.setup.connectAi") })] }) }) })) : (_jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, onTextChange: isActiveComposer
4160
4243
  ? handleComposerTextChange
4161
4244
  : undefined, disabled: isComposerDisabled ||
4162
4245
  showInlineMissingKeySetup, placeholder: showInlineMissingKeySetup
4163
- ? "Connect AI above to start chatting..."
4246
+ ? t("agentChat.setup.connectPlaceholder")
4164
4247
  : engineSetupRequired
4165
- ? "Connect AI to start chatting..."
4248
+ ? t("agentChat.setup.connectPlaceholder")
4166
4249
  : composerDisabled
4167
4250
  ? (composerDisabledPlaceholder ??
4168
- "Open Desktop to use this chat.")
4251
+ t("agentChat.composer.openDesktop"))
4169
4252
  : isRunning
4170
4253
  ? queuedMessages.length > 0
4171
- ? `${queuedMessages.length} queued — send a follow-up...`
4172
- : "Send a follow-up..."
4254
+ ? t("agentChat.queue.followUpWithCount", {
4255
+ count: queuedMessages.length,
4256
+ })
4257
+ : t("agentChat.queue.followUp")
4173
4258
  : resolveAssistantChatComposerPlaceholder(composerPlaceholder), onSubmit: isRunning ||
4174
4259
  composerContextItems.length > 0
4175
4260
  ? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0
@@ -4178,9 +4263,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4178
4263
  isRunning,
4179
4264
  requestedIntent: options?.intent,
4180
4265
  }), undefined, true)
4181
- : undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, modelListLoading: modelListLoading, onModelChange: onModelChange, onEffortChange: onEffortChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
4266
+ : undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
4267
+ ? onModelChange
4268
+ : undefined, onEffortChange: onEffortChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
4182
4269
  preserveQueuedMessages: true,
4183
- }), "aria-label": "Stop response", "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: "Stop response" })] })) : undefined })] })) }), showMissingKeySetup &&
4270
+ }), "aria-label": t("agentChat.composer.stopResponse"), "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.composer.stopResponse") })] })) : undefined })] })) }), showMissingKeySetup &&
4184
4271
  !showInlineMissingKeySetup ? (_jsx(PopoverContent, { side: missingApiKeySetupLayout === "sidebar"
4185
4272
  ? "top"
4186
4273
  : "bottom", align: "center", sideOffset: 8, collisionPadding: 12, "data-agent-native-composer-popover": "true", className: "z-[260] box-border w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,26rem),26rem)] rounded-lg border-border p-3 shadow-lg", children: _jsx(BuilderSetupContent, { onConnected: handleBuilderConnected, layout: missingApiKeySetupLayout }) })) : null] })] })] }) }) }) }) }) }) }) }) }) }) }));
@@ -44,6 +44,13 @@ export function HighlightedCodeBlock({ code, lang, containerClass, streaming = f
44
44
  const renderedHashRef = useRef(null);
45
45
  const debounceTimerRef = useRef(null);
46
46
  const cancelledRef = useRef(false);
47
+ // Whether this block has already put code on screen. Reserving space
48
+ // invisibly is right on FIRST paint — it avoids an unhighlighted→highlighted
49
+ // flash — but once code is visible, hiding it again to wait for Shiki blanks
50
+ // a code box the user was reading. That is what happened whenever the block
51
+ // lost its html (a re-highlight after the content changed): readable code
52
+ // went blank mid-answer.
53
+ const hasPaintedRef = useRef(false);
47
54
  useEffect(() => {
48
55
  cancelledRef.current = false;
49
56
  return () => {
@@ -108,11 +115,14 @@ export function HighlightedCodeBlock({ code, lang, containerClass, streaming = f
108
115
  }
109
116
  }, [code, lang, streaming, loadHighlighter]);
110
117
  if (html) {
118
+ hasPaintedRef.current = true;
111
119
  return (_jsx("div", { className: containerClass, dangerouslySetInnerHTML: { __html: html } }));
112
120
  }
113
121
  // Streaming: show plain growing text (previous html already kept above when
114
122
  // available). Non-streaming first paint: reserve space invisibly so we never
115
- // flash unhighlighted → highlighted.
116
- const showPlain = streaming || failed;
123
+ // flash unhighlighted → highlighted. Once painted, never hide again.
124
+ const showPlain = streaming || failed || hasPaintedRef.current;
125
+ if (showPlain)
126
+ hasPaintedRef.current = true;
117
127
  return (_jsx("div", { className: containerClass, "aria-busy": !showPlain && !failed, children: _jsx("pre", { children: _jsx("code", { className: lang ? `language-${lang}` : undefined, style: showPlain ? undefined : { visibility: "hidden" }, children: code }) }) }));
118
128
  }