@agent-native/core 0.154.5 → 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 (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
  3. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
  4. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
  5. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
  6. package/corpus/templates/analytics/AGENTS.md +3 -0
  7. package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
  8. package/corpus/templates/analytics/actions/bigquery.ts +1 -1
  9. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
  10. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  11. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
  12. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
  13. package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
  14. package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
  17. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  18. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  19. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  20. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
  21. package/corpus/templates/calendar/AGENTS.md +2 -0
  22. package/corpus/templates/calendar/DEVELOPING.md +4 -4
  23. package/corpus/templates/calendar/actions/get-settings.ts +4 -11
  24. package/corpus/templates/calendar/actions/update-availability.ts +15 -4
  25. package/corpus/templates/calendar/actions/update-settings.ts +15 -4
  26. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
  27. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
  29. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
  30. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
  31. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
  32. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
  33. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
  34. package/corpus/templates/calendar/app/i18n-data.ts +30 -0
  35. package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
  36. package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
  37. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
  38. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
  39. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
  40. package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
  41. package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
  42. package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
  43. package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
  44. package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
  45. package/corpus/templates/calendar/shared/api.ts +1 -0
  46. package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
  47. package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
  48. package/corpus/templates/calendar/shared/settings.ts +44 -0
  49. package/corpus/templates/clips/AGENTS.md +2 -0
  50. package/corpus/templates/clips/actions/add-comment.ts +5 -2
  51. package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
  52. package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
  53. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
  54. package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
  55. package/corpus/templates/clips/actions/update-comment.ts +9 -2
  56. package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
  57. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
  58. package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
  59. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
  60. package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
  61. package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
  62. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
  63. package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
  64. package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
  65. package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
  66. package/corpus/templates/clips/app/root.tsx +2 -1
  67. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
  68. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
  69. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
  70. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
  71. package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
  72. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
  73. package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
  75. package/corpus/templates/clips/desktop/src/app.tsx +116 -19
  76. package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
  77. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
  78. package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
  79. package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
  80. package/corpus/templates/clips/desktop/src/styles.css +117 -0
  81. package/corpus/templates/clips/server/db/index.ts +10 -0
  82. package/corpus/templates/clips/server/db/schema.ts +1 -0
  83. package/corpus/templates/clips/server/lib/recordings.ts +3 -1
  84. package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
  85. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  86. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
  87. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
  88. package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
  89. package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
  90. package/corpus/templates/content/actions/add-comment.ts +2 -1
  91. package/corpus/templates/content/actions/update-comment.ts +1 -1
  92. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
  93. package/corpus/templates/design/actions/generate-design.ts +10 -7
  94. package/corpus/templates/design/actions/present-design-variants.ts +35 -6
  95. package/corpus/templates/design/actions/run-design-audit.ts +33 -0
  96. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
  97. package/corpus/templates/design/shared/design-review.ts +1 -0
  98. package/corpus/templates/design/shared/html-integrity.ts +66 -0
  99. package/corpus/templates/dispatch/.env.example +5 -0
  100. package/corpus/templates/dispatch/AGENTS.md +24 -19
  101. package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
  102. package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
  103. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
  104. package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
  105. package/corpus/templates/factory/AGENTS.md +5 -1
  106. package/corpus/templates/factory/README.md +23 -0
  107. package/corpus/templates/factory/actions/view-screen.ts +17 -0
  108. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
  109. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
  110. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
  111. package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
  112. package/corpus/templates/factory/app/routes/factory.tsx +41 -0
  113. package/corpus/templates/factory/package.json +1 -0
  114. package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
  115. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
  116. package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
  117. package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
  118. package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
  119. package/corpus/templates/mail/vite.config.ts +7 -0
  120. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
  121. package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
  122. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  123. package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
  124. package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
  125. package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
  126. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
  127. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  128. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  129. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  130. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  131. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  132. package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
  133. package/corpus/templates/slides/app/global.css +7 -40
  134. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  135. package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
  136. package/dist/agent/index.d.ts +1 -1
  137. package/dist/agent/production-agent.d.ts +30 -0
  138. package/dist/agent/production-agent.js +105 -9
  139. package/dist/agent/thread-data-builder.js +17 -1
  140. package/dist/agent/types.d.ts +9 -0
  141. package/dist/cli/skills-content/help.js +6 -4
  142. package/dist/cli/skills.js +17 -2
  143. package/dist/cli/workspacify.d.ts +5 -0
  144. package/dist/cli/workspacify.js +65 -8
  145. package/dist/client/AgentPanel.d.ts +5 -0
  146. package/dist/client/AgentPanel.js +32 -13
  147. package/dist/client/AssistantChat.d.ts +36 -1
  148. package/dist/client/AssistantChat.js +175 -110
  149. package/dist/client/MultiTabAssistantChat.js +44 -27
  150. package/dist/client/active-run-state.d.ts +14 -1
  151. package/dist/client/active-run-state.js +0 -0
  152. package/dist/client/agent-chat-adapter.js +107 -5
  153. package/dist/client/analytics.js +9 -2
  154. package/dist/client/chat/markdown-renderer.js +4 -2
  155. package/dist/client/chat/message-components.d.ts +2 -2
  156. package/dist/client/chat/message-components.js +69 -35
  157. package/dist/client/chat/run-recovery.d.ts +4 -1
  158. package/dist/client/chat/run-recovery.js +62 -30
  159. package/dist/client/chat/tool-call-display.d.ts +12 -3
  160. package/dist/client/chat/tool-call-display.js +79 -40
  161. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  162. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  163. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  164. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  165. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  166. package/dist/client/chat-first/apps-rail.js +9 -4
  167. package/dist/client/chat-first/primary-nav.d.ts +2 -1
  168. package/dist/client/chat-first/primary-nav.js +3 -3
  169. package/dist/client/chat-first/types.d.ts +5 -1
  170. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  171. package/dist/client/components/ui/message-scroller.js +7 -3
  172. package/dist/client/composer/runtime-adapters.js +3 -2
  173. package/dist/client/context-xray/ContextMeter.js +5 -1
  174. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  175. package/dist/client/error-format.d.ts +4 -0
  176. package/dist/client/error-format.js +93 -5
  177. package/dist/client/i18n.js +83 -17
  178. package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
  179. package/dist/client/markdown/InlineMarkdown.js +65 -0
  180. package/dist/client/markdown/index.d.ts +1 -0
  181. package/dist/client/markdown/index.js +1 -0
  182. package/dist/client/observability/ThumbsFeedback.js +10 -8
  183. package/dist/client/review/ReviewThreadPanel.js +2 -1
  184. package/dist/client/run-stream-ownership.d.ts +39 -0
  185. package/dist/client/run-stream-ownership.js +65 -0
  186. package/dist/client/sharing/ShareButton.js +160 -54
  187. package/dist/coding-tools/run-code.js +2 -1
  188. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  189. package/dist/coding-tools/sandbox/background.js +8 -1
  190. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  191. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/collab/struct-routes.d.ts +1 -1
  195. package/dist/feature-flags/index.d.ts +1 -1
  196. package/dist/feature-flags/index.js +1 -1
  197. package/dist/feature-flags/store.d.ts +10 -0
  198. package/dist/feature-flags/store.js +112 -1
  199. package/dist/guards/no-env-credentials.js +4 -0
  200. package/dist/index.d.ts +2 -2
  201. package/dist/index.js +1 -1
  202. package/dist/integrations/adapters/slack.js +5 -1
  203. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  204. package/dist/localization/core-messages/ar-SA.js +541 -0
  205. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  206. package/dist/localization/core-messages/de-DE.js +513 -0
  207. package/dist/localization/core-messages/en-US.d.ts +513 -0
  208. package/dist/localization/core-messages/en-US.js +513 -0
  209. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  210. package/dist/localization/core-messages/es-ES.js +520 -0
  211. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  212. package/dist/localization/core-messages/fr-FR.js +520 -0
  213. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  214. package/dist/localization/core-messages/hi-IN.js +513 -0
  215. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  216. package/dist/localization/core-messages/ja-JP.js +506 -0
  217. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  218. package/dist/localization/core-messages/ko-KR.js +506 -0
  219. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  220. package/dist/localization/core-messages/pt-BR.js +520 -0
  221. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  222. package/dist/localization/core-messages/zh-CN.js +506 -0
  223. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  224. package/dist/localization/core-messages/zh-TW.js +506 -0
  225. package/dist/localization/core-messages.d.ts +524 -0
  226. package/dist/localization/core-messages.js +124 -0
  227. package/dist/localization/default-messages.d.ts +3 -0
  228. package/dist/localization/default-messages.js +3 -0
  229. package/dist/notifications/routes.d.ts +1 -1
  230. package/dist/observability/routes.d.ts +2 -2
  231. package/dist/progress/routes.d.ts +1 -1
  232. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  233. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  234. package/dist/resources/agents.js +40 -5
  235. package/dist/resources/metadata.d.ts +10 -0
  236. package/dist/resources/metadata.js +7 -3
  237. package/dist/review/actions/create-review-comment.js +1 -1
  238. package/dist/review/actions/reply-review-comment.js +1 -1
  239. package/dist/review/actions/resolve-review-thread.js +1 -1
  240. package/dist/secrets/routes.d.ts +6 -6
  241. package/dist/server/action-routes.js +32 -6
  242. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  243. package/dist/server/agent-chat-plugin.d.ts +10 -0
  244. package/dist/server/agent-chat-plugin.js +144 -41
  245. package/dist/server/agent-discovery.js +14 -3
  246. package/dist/server/agent-run-context.d.ts +7 -1
  247. package/dist/server/agent-run-context.js +9 -2
  248. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  249. package/dist/server/agent-teams.js +38 -9
  250. package/dist/server/core-routes-plugin.js +1 -1
  251. package/dist/server/identity-sso-migrations.d.ts +7 -0
  252. package/dist/server/identity-sso-migrations.js +36 -0
  253. package/dist/server/identity-sso-store.d.ts +3 -2
  254. package/dist/server/identity-sso-store.js +8 -3
  255. package/dist/server/index.d.ts +1 -1
  256. package/dist/server/realtime-token.d.ts +1 -1
  257. package/dist/server/release-migrations.js +4 -0
  258. package/dist/server/request-context.d.ts +7 -0
  259. package/dist/server/request-context.js +15 -0
  260. package/dist/tracking/providers.js +13 -3
  261. package/dist/workspace-files/tool.js +1 -1
  262. package/docs/content/agent-surfaces.mdx +30 -0
  263. package/package.json +3 -2
@@ -32,9 +32,11 @@ 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";
39
+ import { claimRunStream, createRunStreamToken, ownsRunStream, releaseRunStream, } from "./run-stream-ownership.js";
38
40
  import { AgentAutoContinueSignal, readSSEStreamRaw, settleInterruptedToolCalls, } from "./sse-event-processor.js";
39
41
  import { useAgentEngineConfigured } from "./use-agent-engine-configured.js";
40
42
  import { useDevMode } from "./use-dev-mode.js";
@@ -364,6 +366,7 @@ function getImageAttachmentSrc(attachment) {
364
366
  return imagePart && "image" in imagePart ? imagePart.image : null;
365
367
  }
366
368
  function ComposerAttachmentPreviewCard({ attachment, onRemove, }) {
369
+ const t = useT();
367
370
  const [imageSrc, setImageSrc] = useState(null);
368
371
  useEffect(() => {
369
372
  const nextSrc = getImageAttachmentSrc(attachment);
@@ -382,7 +385,9 @@ function ComposerAttachmentPreviewCard({ attachment, onRemove, }) {
382
385
  ? "h-20 w-20 rounded-xl shadow-[0_12px_30px_-18px_rgba(0,0,0,0.7)]"
383
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
384
387
  ? "end-1.5 top-1.5 opacity-100 md:opacity-0 md:group-hover:opacity-100"
385
- : "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" }) })] }));
386
391
  }
387
392
  function ComposerAttachmentPreviewStrip() {
388
393
  const attachments = useComposer((state) => state.attachments);
@@ -786,6 +791,27 @@ function trimReconnectTextAlreadyRendered(content, renderedAssistantText, option
786
791
  }
787
792
  return changed ? next : content;
788
793
  }
794
+ /**
795
+ * Whether the reconnect overlay — a SECOND fold of a run, rendered as a sibling
796
+ * of the message list — may appear.
797
+ *
798
+ * The adapter runtime and the reconnect reader both fold the same SSE events
799
+ * into their own accumulator. Whenever both are on screen the user sees the
800
+ * turn twice: duplicate tool cards (one spinning, one static) and the final
801
+ * message streaming in two places. Content-similarity dedupe cannot reliably
802
+ * hide the second copy, because the two readers disagree on tool-call identity
803
+ * (id-less activity cards get reader-local ids) and on how a turn is split
804
+ * across assistant messages.
805
+ *
806
+ * So ownership decides visibility, not similarity: if a runtime owns the turn,
807
+ * the overlay does not render. Keep this a pure function — it is the invariant
808
+ * the duplicate-render bug kept violating, and it must stay falsifiable.
809
+ */
810
+ export function shouldShowReconnectOverlay(state) {
811
+ if (state.isRuntimeRunning)
812
+ return false;
813
+ return state.isReconnecting || state.reconnectFrozen;
814
+ }
789
815
  export function dedupeReconnectContentAgainstMessages(content, messages, options) {
790
816
  if (content.length === 0)
791
817
  return content;
@@ -958,14 +984,38 @@ export function resolveAssistantChatRunningState({ forceStopped, isRuntimeRunnin
958
984
  showRunningInUI: !forceStopped && !hasTerminalRunError && (isRunning || isAutoResuming),
959
985
  };
960
986
  }
961
- export function resolveAssistantChatRunningStatusLabel({ runningActivityLabel, isAutoResuming, isReconnecting, hasReconnectContent, }) {
962
- 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
+ }
963
1012
  return runningActivityLabel;
1013
+ }
964
1014
  if (isAutoResuming)
965
- return "Resuming";
1015
+ return labels.resuming;
966
1016
  if (isReconnecting && hasReconnectContent)
967
- return "Still working";
968
- return "Thinking";
1017
+ return labels.stillWorking;
1018
+ return labels.thinking;
969
1019
  }
970
1020
  export function resolveAssistantChatComposerPlaceholder(composerPlaceholder) {
971
1021
  return composerPlaceholder ?? DEFAULT_ASSISTANT_CHAT_COMPOSER_PLACEHOLDER;
@@ -1087,6 +1137,9 @@ function AssistantChatScrollerControls({ resumeFollowingRef, }) {
1087
1137
  }, [resumeFollowingRef, scrollToEnd]);
1088
1138
  return null;
1089
1139
  }
1140
+ export function shouldShowAssistantChatModelSelector(showModelSelector) {
1141
+ return showModelSelector !== false;
1142
+ }
1090
1143
  export const CHAT_STORAGE_PREFIX = "agent-chat:";
1091
1144
  const THREAD_SNAPSHOT_CACHE_PREFIX = `${CHAT_STORAGE_PREFIX}thread-snapshot:`;
1092
1145
  function threadSnapshotCacheKey(apiUrl, threadId) {
@@ -1322,7 +1375,8 @@ export function useAutoResumeStatus(tabId, forceStopped) {
1322
1375
  function approvalResolutionIdentity(approvalKey, toolCallId) {
1323
1376
  return `${toolCallId ?? ""}\u0000${approvalKey}`;
1324
1377
  }
1325
- 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();
1326
1380
  const thread = useThread();
1327
1381
  const threadRuntime = useThreadRuntime();
1328
1382
  const composerRuntime = useComposerRuntime();
@@ -1419,10 +1473,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1419
1473
  void Promise.all(attachments.map((file) => composerRuntime.addAttachment(file))).catch((error) => {
1420
1474
  const msg = error instanceof Error
1421
1475
  ? error.message
1422
- : "Could not add the dropped file. Try a different format.";
1476
+ : t("agentChat.composer.droppedFileError");
1423
1477
  setComposerError(msg);
1424
1478
  });
1425
- }, [composerRuntime, setComposerError]);
1479
+ }, [composerRuntime, setComposerError, t]);
1426
1480
  // Patch the underlying assistant-ui MessageRepository so addOrUpdateMessage
1427
1481
  // can't throw "Parent message not found" mid-run. assistant-ui calls
1428
1482
  // `repository.clear()` from `runtime.import()` and from `resetHead(null)`,
@@ -1605,7 +1659,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1605
1659
  const [reconnectFrozen, setReconnectFrozen] = useState(false);
1606
1660
  // Adapter took over while reconnect still had visible tool cards — keep the
1607
1661
  // overlay until the adapter message catches up so we don't flash an empty gap.
1608
- const [adapterHandoffPending, setAdapterHandoffPending] = useState(false);
1609
1662
  const reconnectRunIdRef = useRef(null);
1610
1663
  const reconnectTurnIdRef = useRef(null);
1611
1664
  const reconnectTailOnlyRef = useRef(false);
@@ -1624,7 +1677,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1624
1677
  setIsReconnecting(false);
1625
1678
  setReconnectFrozen(false);
1626
1679
  setReconnectContent([]);
1627
- setAdapterHandoffPending(false);
1628
1680
  setPendingReconnectRecovery(null);
1629
1681
  resetRunningActivity();
1630
1682
  }, [resetRunningActivity]);
@@ -1701,15 +1753,21 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1701
1753
  isAutoResuming,
1702
1754
  isReconnecting,
1703
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
+ },
1704
1767
  });
1705
- const reconnectActivityContent = useMemo(() => isReconnecting || reconnectFrozen || adapterHandoffPending
1768
+ const reconnectActivityContent = useMemo(() => isReconnecting || reconnectFrozen
1706
1769
  ? reconnectActivityFallbackContent(runningActivityTool)
1707
- : [], [
1708
- adapterHandoffPending,
1709
- isReconnecting,
1710
- reconnectFrozen,
1711
- runningActivityTool,
1712
- ]);
1770
+ : [], [isReconnecting, reconnectFrozen, runningActivityTool]);
1713
1771
  const lastBroadcastRunningRef = useRef(isRunning);
1714
1772
  const tiptapRef = useRef(null);
1715
1773
  const focusComposerAfterConnectRef = useRef(false);
@@ -2010,6 +2068,13 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2010
2068
  if (isRuntimeRunningRef.current || isAutoResumingRef.current) {
2011
2069
  return false;
2012
2070
  }
2071
+ // The refs above lag a render and are per-component-instance, while
2072
+ // MultiTabAssistantChat mounts several instances against one run. The
2073
+ // claim is the actual mutual exclusion: module-scoped, synchronous, and
2074
+ // re-checked on every state write below.
2075
+ const ownershipToken = createRunStreamToken(`reconnect:${runId}`);
2076
+ if (!claimRunStream(threadId, runId, ownershipToken))
2077
+ return false;
2013
2078
  // SUPERSEDE THE PREVIOUS RECONNECT GENERATION. A turn that keeps failing
2014
2079
  // (e.g. repeated stale_run at "Contacting model") produces a new runId
2015
2080
  // every few seconds; each call here used to OVERWRITE the single-slot
@@ -2050,7 +2115,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2050
2115
  });
2051
2116
  setIsReconnecting(true);
2052
2117
  setReconnectFrozen(false);
2053
- setAdapterHandoffPending(false);
2054
2118
  setReconnectContent([]);
2055
2119
  window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
2056
2120
  detail: { isRunning: true, tabId: tabId || threadId },
@@ -2197,7 +2261,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2197
2261
  requestAnimationFrame(() => {
2198
2262
  rafPending = false;
2199
2263
  if (!reconnectOwnerMountedRef.current ||
2200
- reconnectRunIdRef.current !== runId) {
2264
+ reconnectRunIdRef.current !== runId ||
2265
+ !ownsRunStream(threadId, runId, ownershipToken)) {
2201
2266
  return;
2202
2267
  }
2203
2268
  setReconnectContent(latestSnapshot);
@@ -2205,9 +2270,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2205
2270
  };
2206
2271
  try {
2207
2272
  await readSSEStreamRaw(sseRes.body, content, toolCallCounter, tabId, scheduleUpdate, (seq, isProgress) => {
2273
+ // The adapter can preempt this reader mid-stream. Advancing
2274
+ // the cursor after that would move a run this reader no
2275
+ // longer represents.
2276
+ if (!ownsRunStream(threadId, runId, ownershipToken))
2277
+ return;
2208
2278
  markReconnectProgress();
2209
2279
  reconnectRetryCount = 0;
2210
- updateActiveRunSeq(seq, isProgress);
2280
+ updateActiveRunSeq(threadId, runId, seq, isProgress);
2211
2281
  }, { preparingActionState });
2212
2282
  if (reconnectAfterSeq === 0) {
2213
2283
  setReconnectContent([...content]);
@@ -2255,6 +2325,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2255
2325
  threadPollEngine?.stop();
2256
2326
  watchdog.stop();
2257
2327
  clearInterval(idleCheck);
2328
+ releaseRunStream(threadId, runId, ownershipToken);
2258
2329
  }
2259
2330
  // A newer reader, live adapter, stop action, or component unmount took
2260
2331
  // ownership while this async loop was unwinding. Do not let the stale
@@ -2347,10 +2418,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2347
2418
  }
2348
2419
  setRunErrorInfo({
2349
2420
  message: reconnectErrorCode === "run_timeout"
2350
- ? "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")
2351
2422
  : reconnectErrorCode === "reconnect_stream_ended"
2352
- ? "The previous agent stream ended while the run was recovering. Continue or retry to reconnect to the run."
2353
- : "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"),
2354
2425
  errorCode: reconnectErrorCode,
2355
2426
  recoverable: true,
2356
2427
  runId,
@@ -2425,7 +2496,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2425
2496
  };
2426
2497
  void streamReconnect();
2427
2498
  return true;
2428
- }, [apiUrl, refreshThreadFromServer, tabId, threadId, wasRecentlyStoppedRun]);
2499
+ }, [
2500
+ apiUrl,
2501
+ refreshThreadFromServer,
2502
+ t,
2503
+ tabId,
2504
+ threadId,
2505
+ wasRecentlyStoppedRun,
2506
+ ]);
2429
2507
  const reconnectActiveRunForThread = useCallback(async () => {
2430
2508
  if (!threadId)
2431
2509
  return false;
@@ -2461,7 +2539,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2461
2539
  clearActiveRunIfMatches(threadId, storedActiveRun.runId);
2462
2540
  window.dispatchEvent(new CustomEvent("agent-chat:run-error", {
2463
2541
  detail: {
2464
- 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"),
2465
2543
  errorCode: "run_status_unavailable",
2466
2544
  recoverable: true,
2467
2545
  ...(storedActiveRun.runId
@@ -2492,7 +2570,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2492
2570
  catch {
2493
2571
  return false;
2494
2572
  }
2495
- }, [apiUrl, refreshThreadFromServer, startReconnectToRun, tabId, threadId]);
2573
+ }, [
2574
+ apiUrl,
2575
+ refreshThreadFromServer,
2576
+ startReconnectToRun,
2577
+ t,
2578
+ tabId,
2579
+ threadId,
2580
+ ]);
2496
2581
  useEffect(() => {
2497
2582
  if (!threadId || !isNewThread)
2498
2583
  return;
@@ -3192,11 +3277,14 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3192
3277
  prevIsRuntimeRunningRef.current = isRuntimeRunning;
3193
3278
  if (isRuntimeRunning && !wasRunning) {
3194
3279
  // SINGLE-READER OWNERSHIP: the adapter runtime just took over (a new run
3195
- // started or an adopted run resumed). Abort the reconnect reader, but keep
3196
- // its visible content until the adapter message has tool/text parts so the
3197
- // UI does not flash an empty gap between readers.
3280
+ // started or an adopted run resumed), so it is now the only owner of this
3281
+ // turn's rendering. The overlay used to be kept alive here for up to
3282
+ // 2500ms so the UI would not flash a gap — but that deliberately put two
3283
+ // independent folds of the same run on screen at once, and the only thing
3284
+ // hiding the second was content-similarity guessing that fails whenever
3285
+ // the two readers disagree (id-less activity cards, a turn split across
3286
+ // several assistant messages). One owner, one surface: drop the overlay.
3198
3287
  if (reconnectRunIdRef.current !== null) {
3199
- const keepOverlay = reconnectContent.length > 0;
3200
3288
  reconnectRunIdRef.current = null;
3201
3289
  reconnectAbortRef.current?.abort();
3202
3290
  reconnectAbortRef.current = null;
@@ -3204,56 +3292,18 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3204
3292
  setReconnectFrozen(false);
3205
3293
  reconnectCanMaterializeRef.current = false;
3206
3294
  reconnectTailOnlyRef.current = false;
3207
- if (keepOverlay) {
3208
- setAdapterHandoffPending(true);
3209
- }
3210
- else {
3211
- setReconnectContent([]);
3212
- setAdapterHandoffPending(false);
3213
- }
3295
+ setReconnectContent([]);
3214
3296
  }
3215
3297
  else if (reconnectFrozen) {
3216
3298
  setReconnectFrozen(false);
3217
3299
  setReconnectContent([]);
3218
- setAdapterHandoffPending(false);
3219
3300
  reconnectCanMaterializeRef.current = false;
3220
3301
  }
3221
3302
  if (forceStopped) {
3222
3303
  setForceStopped(false);
3223
3304
  }
3224
3305
  }
3225
- }, [
3226
- isRuntimeRunning,
3227
- reconnectFrozen,
3228
- forceStopped,
3229
- reconnectContent.length,
3230
- ]);
3231
- // Release the deferred reconnect overlay once thread messages have caught
3232
- // up enough that dedupe would hide the overlay, or after a short timeout so
3233
- // a stuck handoff cannot leave duplicate tool cards forever.
3234
- useEffect(() => {
3235
- if (!adapterHandoffPending)
3236
- return;
3237
- if (!isRuntimeRunning) {
3238
- setReconnectContent([]);
3239
- setAdapterHandoffPending(false);
3240
- return;
3241
- }
3242
- const stillNeeded = dedupeReconnectContentAgainstMessages(reconnectContent, messages, {
3243
- suppressToolRepeats: true,
3244
- trimTailTextOverlap: true,
3245
- }).length > 0;
3246
- if (!stillNeeded) {
3247
- setReconnectContent([]);
3248
- setAdapterHandoffPending(false);
3249
- return;
3250
- }
3251
- const timer = window.setTimeout(() => {
3252
- setReconnectContent([]);
3253
- setAdapterHandoffPending(false);
3254
- }, 2500);
3255
- return () => window.clearTimeout(timer);
3256
- }, [adapterHandoffPending, isRuntimeRunning, messages, reconnectContent]);
3306
+ }, [isRuntimeRunning, reconnectFrozen, forceStopped]);
3257
3307
  // Same transition guard for isReconnecting: only clear forceStopped on
3258
3308
  // the false→true edge (a new reconnect starting on page load).
3259
3309
  const prevIsReconnectingRef = useRef(isReconnecting);
@@ -3270,7 +3320,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3270
3320
  if (!reconnectCanMaterializeRef.current) {
3271
3321
  setReconnectFrozen(false);
3272
3322
  setReconnectContent([]);
3273
- setAdapterHandoffPending(false);
3274
3323
  return;
3275
3324
  }
3276
3325
  try {
@@ -3316,7 +3365,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3316
3365
  threadRuntime.import(ensureMessageMetadata(repo));
3317
3366
  setReconnectFrozen(false);
3318
3367
  setReconnectContent([]);
3319
- setAdapterHandoffPending(false);
3320
3368
  reconnectCanMaterializeRef.current = false;
3321
3369
  reconnectTurnIdRef.current = null;
3322
3370
  }
@@ -3453,7 +3501,6 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3453
3501
  reconnectAbortRef.current = null;
3454
3502
  reconnectRunIdRef.current = null;
3455
3503
  setIsReconnecting(false);
3456
- setAdapterHandoffPending(false);
3457
3504
  const shouldFreezeReconnectContent = !reconnectTailOnlyRef.current &&
3458
3505
  reconnectCanMaterializeRef.current &&
3459
3506
  reconnectContent.length > 0;
@@ -3539,7 +3586,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3539
3586
  catch (err) {
3540
3587
  const msg = err instanceof Error
3541
3588
  ? err.message
3542
- : "Attachment could not be processed.";
3589
+ : t("agentChat.composer.attachmentError");
3543
3590
  setComposerError(msg);
3544
3591
  reportAgentChatSubmitResult(submitMessageId, false, "attachment-error");
3545
3592
  return;
@@ -3731,6 +3778,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3731
3778
  selectedEffort,
3732
3779
  selectedEngine,
3733
3780
  selectedModel,
3781
+ t,
3734
3782
  updateComposerContextItems,
3735
3783
  ]);
3736
3784
  const mcpResumeTimerRef = useRef(null);
@@ -3844,8 +3892,19 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3844
3892
  // an ahead-of-the-thread reconnect copy visible creates the familiar
3845
3893
  // two-card stack while the adapter catches up, so prefer one row and
3846
3894
  // let the live message advance in place.
3847
- suppressToolRepeats: adapterHandoffPending || isReconnecting || reconnectFrozen,
3848
- trimTailTextOverlap: adapterHandoffPending || reconnectTailOnlyRef.current,
3895
+ suppressToolRepeats: isReconnecting || reconnectFrozen,
3896
+ trimTailTextOverlap: reconnectTailOnlyRef.current,
3897
+ });
3898
+ // The reconnect overlay is a SECOND fold of the same run, rendered as a
3899
+ // sibling of the message list. It may only appear while no adapter runtime
3900
+ // owns the turn. Deriving it from `isRuntimeRunning` at render time — rather
3901
+ // than relying on an effect to clear the overlay's own flags afterwards —
3902
+ // is what makes two streaming copies structurally impossible instead of
3903
+ // merely unlikely; the effect below runs a frame too late to prevent it.
3904
+ const showReconnectOverlay = shouldShowReconnectOverlay({
3905
+ isRuntimeRunning,
3906
+ isReconnecting,
3907
+ reconnectFrozen,
3849
3908
  });
3850
3909
  const latestMessage = messages[messages.length - 1];
3851
3910
  const reconnectStatusContent = visibleReconnectContent.length > 0
@@ -4039,7 +4098,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4039
4098
  getApprovalResolution,
4040
4099
  onApprovalResolved: recordApprovalResolution,
4041
4100
  onApprove: (approvalKey) => {
4042
- void addToQueue("Approved. Go ahead and run the requested action.", undefined, undefined, undefined, undefined, "queued", undefined, false, false, false, false, undefined, [approvalKey]);
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
4103
+ undefined, [approvalKey]);
4043
4104
  },
4044
4105
  ...(approvalActions?.onDeny ? { onDeny: approvalActions.onDeny } : {}),
4045
4106
  ...(approvalActions?.onAlwaysAllow
@@ -4068,19 +4129,19 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4068
4129
  ? "centered"
4069
4130
  : compactMissingKeyEmptyState
4070
4131
  ? "compact-setup"
4071
- : 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
4072
- ? "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")
4073
4134
  : authError.sessionExpired
4074
- ? "Session expired"
4075
- : "Authentication required" }), _jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: authSessionAvailable
4076
- ? "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")
4077
4138
  : authError.sessionExpired
4078
- ? "Your session may have expired. Log out and log back in to reconnect."
4079
- : "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 &&
4080
4141
  !authSessionAvailable && (_jsx("button", { onClick: () => {
4081
4142
  window.location.href =
4082
4143
  buildSignInReturnHref();
4083
- }, 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 &&
4084
4145
  !authSessionAvailable && (_jsx("button", { onClick: async () => {
4085
4146
  try {
4086
4147
  await fetch(agentNativePath("/_agent-native/auth/logout"), {
@@ -4091,16 +4152,18 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4091
4152
  console.error("Failed to log out before reloading the chat", error);
4092
4153
  }
4093
4154
  window.location.reload();
4094
- }, 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: () => {
4095
4156
  setAuthError(null);
4096
4157
  window.location.reload();
4097
4158
  }, className: authSessionAvailable
4098
4159
  ? "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md"
4099
- : "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"
4100
4161
  ? "sr-only"
4101
- : "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"
4102
4164
  ? "sr-only"
4103
- : "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 &&
4104
4167
  resolvedSuggestions.length > 0 ? (_jsx("div", { className: "flex w-full max-w-[280px] flex-col gap-1", children: resolvedSuggestions.map((suggestion) => (_jsx("button", { onClick: () => {
4105
4168
  if (engineSetupRequired) {
4106
4169
  requestMissingKeySetup();
@@ -4126,18 +4189,16 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4126
4189
  setDismissedRunErrorKey(visibleRunErrorKey);
4127
4190
  }
4128
4191
  setRunErrorInfo(null);
4129
- } }) })), (isReconnecting ||
4130
- reconnectFrozen ||
4131
- adapterHandoffPending) &&
4132
- visibleReconnectContent.length > 0 && (_jsx(MessageScrollerItem, { children: _jsx(ReconnectStreamMessage, { content: visibleReconnectContent, allowActivitySpinner: !reconnectFrozen }) })), (isReconnecting ||
4133
- reconnectFrozen ||
4134
- adapterHandoffPending) &&
4192
+ } }) })), showReconnectOverlay &&
4193
+ visibleReconnectContent.length > 0 && (_jsx(MessageScrollerItem, { children: _jsx(ReconnectStreamMessage, { content: visibleReconnectContent, allowActivitySpinner: !reconnectFrozen }) })), showReconnectOverlay &&
4135
4194
  visibleReconnectContent.length === 0 &&
4136
4195
  reconnectContent.length === 0 &&
4137
- 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) => {
4196
+ 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", {
4197
+ count: visibleQueuedMessages.length,
4198
+ }) })] }) })), visibleQueuedMessages.map((msg) => {
4138
4199
  const displayText = displayableUserMessageText(msg.text);
4139
4200
  const imageSources = queuedMessageImageSources(msg);
4140
- 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));
4201
+ 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));
4141
4202
  }), 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
4142
4203
  ? { title: guidedQuestionsTitle }
4143
4204
  : {}), ...(guidedQuestionsDescription
@@ -4146,7 +4207,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4146
4207
  ? { skipLabel: guidedQuestionsSkipLabel }
4147
4208
  : {}), ...(guidedQuestionsSubmitLabel
4148
4209
  ? { submitLabel: guidedQuestionsSubmitLabel }
4149
- : {}), 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
4210
+ : {}), 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
4150
4211
  ? "true"
4151
4212
  : 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 &&
4152
4213
  !showInlineMissingKeySetup &&
@@ -4154,24 +4215,26 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4154
4215
  !showMissingKeySetup &&
4155
4216
  "opacity-70"), rootClassName: cn(showMissingKeySetup &&
4156
4217
  "agent-composer-root--missing-key"), children: showMissingKeySetup &&
4157
- !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 ===
4218
+ !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 ===
4158
4219
  "sidebar"
4159
- ? "Connect AI to chat"
4160
- : "Connect AI to start chatting" }), missingApiKeySetupLayout !==
4161
- "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
4220
+ ? t("agentChat.setup.connectToChat")
4221
+ : t("agentChat.setup.connectToStart") }), missingApiKeySetupLayout !==
4222
+ "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
4162
4223
  ? handleComposerTextChange
4163
4224
  : undefined, disabled: isComposerDisabled ||
4164
4225
  showInlineMissingKeySetup, placeholder: showInlineMissingKeySetup
4165
- ? "Connect AI above to start chatting..."
4226
+ ? t("agentChat.setup.connectPlaceholder")
4166
4227
  : engineSetupRequired
4167
- ? "Connect AI to start chatting..."
4228
+ ? t("agentChat.setup.connectPlaceholder")
4168
4229
  : composerDisabled
4169
4230
  ? (composerDisabledPlaceholder ??
4170
- "Open Desktop to use this chat.")
4231
+ t("agentChat.composer.openDesktop"))
4171
4232
  : isRunning
4172
4233
  ? queuedMessages.length > 0
4173
- ? `${queuedMessages.length} queued — send a follow-up...`
4174
- : "Send a follow-up..."
4234
+ ? t("agentChat.queue.followUpWithCount", {
4235
+ count: queuedMessages.length,
4236
+ })
4237
+ : t("agentChat.queue.followUp")
4175
4238
  : resolveAssistantChatComposerPlaceholder(composerPlaceholder), onSubmit: isRunning ||
4176
4239
  composerContextItems.length > 0
4177
4240
  ? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0
@@ -4180,9 +4243,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
4180
4243
  isRunning,
4181
4244
  requestedIntent: options?.intent,
4182
4245
  }), undefined, true)
4183
- : 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({
4246
+ : 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)
4247
+ ? onModelChange
4248
+ : 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({
4184
4249
  preserveQueuedMessages: true,
4185
- }), "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 &&
4250
+ }), "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 &&
4186
4251
  !showInlineMissingKeySetup ? (_jsx(PopoverContent, { side: missingApiKeySetupLayout === "sidebar"
4187
4252
  ? "top"
4188
4253
  : "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] })] })] }) }) }) }) }) }) }) }) }) }) }));