@agent-native/core 0.115.4 → 0.117.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 (487) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/corpus/README.md +3 -3
  3. package/corpus/core/CHANGELOG.md +43 -0
  4. package/corpus/core/agent-native.eject.json +1 -0
  5. package/corpus/core/docs/AGENTS.md +1 -1
  6. package/corpus/core/docs/content/agent-mentions.mdx +2 -2
  7. package/corpus/core/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
  8. package/corpus/core/docs/content/agent-teams.mdx +4 -4
  9. package/corpus/core/docs/content/dispatch.mdx +10 -10
  10. package/corpus/core/docs/content/drop-in-agent.mdx +4 -4
  11. package/corpus/core/docs/content/external-agents.mdx +1 -1
  12. package/corpus/core/docs/content/frames.mdx +1 -1
  13. package/corpus/core/docs/content/integrations.mdx +13 -2
  14. package/corpus/core/docs/content/key-concepts.mdx +2 -2
  15. package/corpus/core/docs/content/mcp-clients.mdx +7 -1
  16. package/corpus/core/docs/content/multi-app-workspace.mdx +3 -3
  17. package/corpus/core/docs/content/real-time-collaboration.mdx +33 -15
  18. package/corpus/core/docs/content/recurring-jobs.mdx +7 -7
  19. package/corpus/core/docs/content/skills-guide.mdx +4 -4
  20. package/corpus/core/docs/content/template-brain.mdx +1 -1
  21. package/corpus/core/docs/content/toolkit-collaboration.mdx +1 -1
  22. package/corpus/core/docs/content/using-your-agent.mdx +1 -1
  23. package/corpus/core/docs/content/what-is-agent-native.mdx +4 -4
  24. package/corpus/core/docs/content/workspace-management.mdx +4 -4
  25. package/corpus/core/package.json +4 -1
  26. package/corpus/core/src/agent/production-agent.ts +13 -3
  27. package/corpus/core/src/cli/doctor.ts +6 -1
  28. package/corpus/core/src/client/AgentPanel.tsx +79 -41
  29. package/corpus/core/src/client/AssistantChat.tsx +71 -25
  30. package/corpus/core/src/client/FeedbackButton.tsx +33 -7
  31. package/corpus/core/src/client/MultiTabAssistantChat.tsx +6 -8
  32. package/corpus/core/src/client/agent-chat-adapter.ts +11 -2
  33. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +22 -3
  34. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +34 -19
  35. package/corpus/core/src/client/analytics.ts +7 -0
  36. package/corpus/core/src/client/chat/message-components.tsx +43 -16
  37. package/corpus/core/src/client/chat/tool-call-display.tsx +30 -18
  38. package/corpus/core/src/client/chat/tool-render-registry.tsx +2 -0
  39. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  40. package/corpus/core/src/client/resources/ResourceTree.tsx +7 -10
  41. package/corpus/core/src/client/resources/ResourcesPanel.tsx +33 -70
  42. package/corpus/core/src/client/resources/mcp-connection-resume.ts +107 -0
  43. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +65 -12
  44. package/corpus/core/src/client/resources/mcp-integration-logos.ts +4 -0
  45. package/corpus/core/src/client/sse-event-processor.ts +50 -14
  46. package/corpus/core/src/client/use-db-sync.ts +370 -21
  47. package/corpus/core/src/collab/client.ts +26 -2
  48. package/corpus/core/src/connections/catalog.ts +7 -1
  49. package/corpus/core/src/db/ddl-guard.ts +49 -10
  50. package/corpus/core/src/deploy/build.ts +25 -2
  51. package/corpus/core/src/eject/remote-mcp-presets.ts +37 -5
  52. package/corpus/core/src/guards/explicit-collab-access.ts +229 -0
  53. package/corpus/core/src/guards/index.ts +1 -0
  54. package/corpus/core/src/localization/default-messages.ts +54 -5
  55. package/corpus/core/src/mcp/connect-route.ts +34 -51
  56. package/corpus/core/src/provider-api/index.ts +1 -1
  57. package/corpus/core/src/realtime-protocol.ts +92 -0
  58. package/corpus/core/src/server/collab-plugin.ts +137 -18
  59. package/corpus/core/src/server/core-routes-plugin.ts +7 -0
  60. package/corpus/core/src/server/index.ts +2 -0
  61. package/corpus/core/src/server/poll-events.ts +29 -8
  62. package/corpus/core/src/server/poll.ts +1123 -865
  63. package/corpus/core/src/server/realtime-token.ts +106 -0
  64. package/corpus/core/src/server/sentry-config.ts +40 -0
  65. package/corpus/core/src/server/short-lived-token.ts +156 -0
  66. package/corpus/core/src/server/sse.ts +15 -0
  67. package/corpus/core/src/server/ssr-handler.ts +9 -3
  68. package/corpus/core/src/styles/agent-native.css +40 -0
  69. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  70. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  71. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  72. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  73. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  74. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  75. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  76. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  77. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  78. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  79. package/corpus/templates/analytics/AGENTS.md +7 -2
  80. package/corpus/templates/analytics/actions/export-dashboard-panel-to-google-sheet.ts +144 -0
  81. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -88
  82. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +34 -37
  83. package/corpus/templates/analytics/app/global.css +0 -4
  84. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -7
  85. package/corpus/templates/analytics/app/i18n-data.ts +107 -0
  86. package/corpus/templates/analytics/app/lib/data-source-status.ts +9 -0
  87. package/corpus/templates/analytics/app/lib/sql-query.ts +1 -2
  88. package/corpus/templates/analytics/app/pages/Ask.tsx +2 -7
  89. package/corpus/templates/analytics/app/pages/DataSources.tsx +48 -0
  90. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +59 -0
  91. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -0
  92. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -1
  93. package/corpus/templates/analytics/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-22-daily-dashboard-email-query-batches.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-22-export-table-panels-directly-to-google-sheets.md +6 -0
  96. package/corpus/templates/analytics/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  97. package/corpus/templates/analytics/changelog/2026-07-22-recent-chats-can-expand-in-the-sidebar.md +6 -0
  98. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +8 -8
  99. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +31 -0
  100. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +3 -5
  101. package/corpus/templates/analytics/server/lib/dashboard-report.ts +12 -0
  102. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +3 -5
  103. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +150 -21
  104. package/corpus/templates/analytics/server/lib/google-sheets-export.ts +188 -0
  105. package/corpus/templates/analytics/server/lib/provider-api.ts +7 -2
  106. package/corpus/templates/analytics/server/plugins/collab.ts +6 -3
  107. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  108. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  109. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +35 -13
  110. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  111. package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
  112. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +54 -62
  113. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -6
  114. package/corpus/templates/assets/app/routes/_index.tsx +8 -5
  115. package/corpus/templates/assets/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  116. package/corpus/templates/assets/changelog/2026-07-22-recent-chats-expand-from-a-compact-list.md +6 -0
  117. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  118. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  119. package/corpus/templates/brain/actions/rotate-source-ingest-token.ts +77 -0
  120. package/corpus/templates/brain/app/components/layout/Layout.tsx +5 -1
  121. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -51
  122. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +2 -9
  123. package/corpus/templates/brain/app/i18n-data.ts +120 -0
  124. package/corpus/templates/brain/app/routes/sources.tsx +156 -5
  125. package/corpus/templates/brain/changelog/2026-07-22-chat-runs-always-end-with-an-answer.md +6 -0
  126. package/corpus/templates/brain/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  127. package/corpus/templates/brain/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  128. package/corpus/templates/brain/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  129. package/corpus/templates/brain/changelog/2026-07-22-long-chat-titles-stay-inside-the-sidebar.md +6 -0
  130. package/corpus/templates/brain/changelog/2026-07-22-new-clips-and-webhook-sources-now-show-their-secure-connecti.md +6 -0
  131. package/corpus/templates/brain/jobs/emit-netlify-brain-queue-cron.ts +125 -0
  132. package/corpus/templates/brain/netlify.toml +5 -1
  133. package/corpus/templates/brain/scripts/configure-prod-clips-source.ts +50 -0
  134. package/corpus/templates/brain/server/lib/capture-sanitization.ts +64 -6
  135. package/corpus/templates/brain/server/plugins/agent-chat.ts +4 -0
  136. package/corpus/templates/brain/server/plugins/brain-jobs.ts +1 -0
  137. package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +55 -0
  138. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  139. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  140. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  141. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  142. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -0
  143. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  144. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  145. package/corpus/templates/chat/app/routes/_index.tsx +8 -5
  146. package/corpus/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  147. package/corpus/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  148. package/corpus/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  149. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  150. package/corpus/templates/clips/AGENTS.md +9 -1
  151. package/corpus/templates/clips/actions/export-to-brain.ts +82 -6
  152. package/corpus/templates/clips/actions/request-transcript.ts +25 -14
  153. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +12 -0
  154. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  155. package/corpus/templates/clips/changelog/2026-07-22-clips-transcripts-now-retry-brain-export.md +5 -0
  156. package/corpus/templates/clips/changelog/2026-07-22-german-umlauts-and-other-non-ascii-characters-now-paste-corr.md +6 -0
  157. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +26 -3
  158. package/corpus/templates/clips/jobs/emit-netlify-brain-export-cron.ts +118 -0
  159. package/corpus/templates/clips/netlify.toml +4 -1
  160. package/corpus/templates/clips/scripts/configure-prod-brain-export.ts +92 -0
  161. package/corpus/templates/clips/server/jobs/brain-export.ts +182 -0
  162. package/corpus/templates/clips/server/lib/brain-export-state.ts +42 -0
  163. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +5 -1
  164. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  165. package/corpus/templates/clips/server/register-secrets.ts +42 -0
  166. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +10 -1
  167. package/corpus/templates/clips/server/routes/api/clips/brain-export/run.post.ts +45 -0
  168. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  169. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  170. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +566 -299
  171. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +14 -0
  172. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -1
  173. package/corpus/templates/content/app/i18n-data.ts +90 -0
  174. package/corpus/templates/content/server/plugins/collab.ts +1 -1
  175. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  176. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  177. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +2 -0
  178. package/corpus/templates/crm/actions/run.ts +2 -6
  179. package/corpus/templates/crm/actions/update-crm-record.ts +4 -2
  180. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +6 -0
  181. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +6 -0
  182. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +6 -0
  183. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +6 -0
  184. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  185. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +4 -1
  186. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  187. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  188. package/corpus/templates/design/server/plugins/collab.ts +11 -8
  189. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  190. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  191. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  192. package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -0
  193. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +1 -1
  194. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +2 -9
  195. package/corpus/templates/forms/app/root.tsx +1 -6
  196. package/corpus/templates/forms/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  197. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  198. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  199. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  200. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  201. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  202. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  203. package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -1
  204. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +57 -72
  205. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +2 -9
  206. package/corpus/templates/plan/app/lib/create-plan-routing.ts +2 -2
  207. package/corpus/templates/plan/app/pages/PlanChatPage.tsx +0 -1
  208. package/corpus/templates/plan/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  209. package/corpus/templates/plan/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  210. package/corpus/templates/plan/changelog/2026-07-22-recent-chats-and-sidebar-controls-are-more-compact.md +6 -0
  211. package/corpus/templates/plan/server/plugins/collab.ts +5 -2
  212. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  213. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +1 -1
  214. package/corpus/templates/slides/server/plugins/collab.ts +11 -8
  215. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  216. package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
  217. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +5 -22
  218. package/corpus/templates/tasks/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  219. package/corpus/toolkit/CHANGELOG.md +7 -0
  220. package/corpus/toolkit/README.md +12 -3
  221. package/corpus/toolkit/package.json +1 -1
  222. package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +89 -0
  223. package/corpus/toolkit/src/chat-history/index.ts +5 -0
  224. package/corpus/toolkit/src/chat-history.css +59 -0
  225. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +106 -36
  226. package/corpus/toolkit/src/composer/VoiceButton.tsx +49 -0
  227. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +2 -1
  228. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +5 -3
  229. package/corpus/toolkit/src/styles.css +35 -0
  230. package/dist/agent/production-agent.d.ts.map +1 -1
  231. package/dist/agent/production-agent.js +13 -3
  232. package/dist/agent/production-agent.js.map +1 -1
  233. package/dist/cli/doctor.d.ts +1 -1
  234. package/dist/cli/doctor.d.ts.map +1 -1
  235. package/dist/cli/doctor.js +5 -2
  236. package/dist/cli/doctor.js.map +1 -1
  237. package/dist/client/AgentPanel.d.ts +1 -0
  238. package/dist/client/AgentPanel.d.ts.map +1 -1
  239. package/dist/client/AgentPanel.js +38 -18
  240. package/dist/client/AgentPanel.js.map +1 -1
  241. package/dist/client/AssistantChat.d.ts.map +1 -1
  242. package/dist/client/AssistantChat.js +55 -20
  243. package/dist/client/AssistantChat.js.map +1 -1
  244. package/dist/client/FeedbackButton.d.ts +4 -2
  245. package/dist/client/FeedbackButton.d.ts.map +1 -1
  246. package/dist/client/FeedbackButton.js +18 -2
  247. package/dist/client/FeedbackButton.js.map +1 -1
  248. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  249. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  250. package/dist/client/MultiTabAssistantChat.js +2 -3
  251. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  252. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  253. package/dist/client/agent-chat-adapter.js +11 -3
  254. package/dist/client/agent-chat-adapter.js.map +1 -1
  255. package/dist/client/agent-page/AgentTabFrame.d.ts +3 -1
  256. package/dist/client/agent-page/AgentTabFrame.d.ts.map +1 -1
  257. package/dist/client/agent-page/AgentTabFrame.js +3 -2
  258. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  259. package/dist/client/agent-page/AgentTabsPage.d.ts +2 -0
  260. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  261. package/dist/client/agent-page/AgentTabsPage.js +26 -15
  262. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  263. package/dist/client/analytics.d.ts +10 -0
  264. package/dist/client/analytics.d.ts.map +1 -1
  265. package/dist/client/analytics.js.map +1 -1
  266. package/dist/client/chat/message-components.d.ts +6 -1
  267. package/dist/client/chat/message-components.d.ts.map +1 -1
  268. package/dist/client/chat/message-components.js +17 -11
  269. package/dist/client/chat/message-components.js.map +1 -1
  270. package/dist/client/chat/tool-call-display.d.ts +11 -1
  271. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  272. package/dist/client/chat/tool-call-display.js +16 -17
  273. package/dist/client/chat/tool-call-display.js.map +1 -1
  274. package/dist/client/chat/tool-render-registry.d.ts +2 -0
  275. package/dist/client/chat/tool-render-registry.d.ts.map +1 -1
  276. package/dist/client/chat/tool-render-registry.js.map +1 -1
  277. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  278. package/dist/client/resources/McpConnectionSuggestion.js +15 -1
  279. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  280. package/dist/client/resources/ResourceTree.d.ts.map +1 -1
  281. package/dist/client/resources/ResourceTree.js +3 -3
  282. package/dist/client/resources/ResourceTree.js.map +1 -1
  283. package/dist/client/resources/ResourcesPanel.d.ts +1 -0
  284. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  285. package/dist/client/resources/ResourcesPanel.js +34 -20
  286. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  287. package/dist/client/resources/mcp-connection-resume.d.ts +11 -0
  288. package/dist/client/resources/mcp-connection-resume.d.ts.map +1 -0
  289. package/dist/client/resources/mcp-connection-resume.js +95 -0
  290. package/dist/client/resources/mcp-connection-resume.js.map +1 -0
  291. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  292. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  293. package/dist/client/resources/mcp-integration-catalog.js +63 -8
  294. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  295. package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -1
  296. package/dist/client/resources/mcp-integration-logos.js +4 -0
  297. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  298. package/dist/client/sse-event-processor.d.ts +5 -0
  299. package/dist/client/sse-event-processor.d.ts.map +1 -1
  300. package/dist/client/sse-event-processor.js +39 -13
  301. package/dist/client/sse-event-processor.js.map +1 -1
  302. package/dist/client/use-db-sync.d.ts +4 -3
  303. package/dist/client/use-db-sync.d.ts.map +1 -1
  304. package/dist/client/use-db-sync.js +332 -21
  305. package/dist/client/use-db-sync.js.map +1 -1
  306. package/dist/collab/client.d.ts.map +1 -1
  307. package/dist/collab/client.js +26 -2
  308. package/dist/collab/client.js.map +1 -1
  309. package/dist/collab/routes.d.ts +1 -1
  310. package/dist/connections/catalog.d.ts +1 -1
  311. package/dist/connections/catalog.d.ts.map +1 -1
  312. package/dist/connections/catalog.js +7 -1
  313. package/dist/connections/catalog.js.map +1 -1
  314. package/dist/db/ddl-guard.d.ts +7 -2
  315. package/dist/db/ddl-guard.d.ts.map +1 -1
  316. package/dist/db/ddl-guard.js +21 -9
  317. package/dist/db/ddl-guard.js.map +1 -1
  318. package/dist/deploy/build.d.ts.map +1 -1
  319. package/dist/deploy/build.js +25 -2
  320. package/dist/deploy/build.js.map +1 -1
  321. package/dist/eject/remote-mcp-presets.d.ts.map +1 -1
  322. package/dist/eject/remote-mcp-presets.js +33 -3
  323. package/dist/eject/remote-mcp-presets.js.map +1 -1
  324. package/dist/guards/explicit-collab-access.d.ts +8 -0
  325. package/dist/guards/explicit-collab-access.d.ts.map +1 -0
  326. package/dist/guards/explicit-collab-access.js +214 -0
  327. package/dist/guards/explicit-collab-access.js.map +1 -0
  328. package/dist/guards/index.d.ts +1 -0
  329. package/dist/guards/index.d.ts.map +1 -1
  330. package/dist/guards/index.js +1 -0
  331. package/dist/guards/index.js.map +1 -1
  332. package/dist/localization/default-messages.d.ts +43 -0
  333. package/dist/localization/default-messages.d.ts.map +1 -1
  334. package/dist/localization/default-messages.js +48 -5
  335. package/dist/localization/default-messages.js.map +1 -1
  336. package/dist/mcp/connect-route.d.ts.map +1 -1
  337. package/dist/mcp/connect-route.js +34 -48
  338. package/dist/mcp/connect-route.js.map +1 -1
  339. package/dist/observability/routes.d.ts +5 -5
  340. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  341. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  342. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  343. package/dist/provider-api/index.js +1 -1
  344. package/dist/provider-api/index.js.map +1 -1
  345. package/dist/realtime-protocol.d.ts +40 -0
  346. package/dist/realtime-protocol.d.ts.map +1 -0
  347. package/dist/realtime-protocol.js +69 -0
  348. package/dist/realtime-protocol.js.map +1 -0
  349. package/dist/resources/handlers.d.ts +1 -1
  350. package/dist/secrets/routes.d.ts +3 -3
  351. package/dist/server/collab-plugin.d.ts +34 -3
  352. package/dist/server/collab-plugin.d.ts.map +1 -1
  353. package/dist/server/collab-plugin.js +65 -12
  354. package/dist/server/collab-plugin.js.map +1 -1
  355. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  356. package/dist/server/core-routes-plugin.js +3 -0
  357. package/dist/server/core-routes-plugin.js.map +1 -1
  358. package/dist/server/index.d.ts +1 -1
  359. package/dist/server/index.d.ts.map +1 -1
  360. package/dist/server/index.js.map +1 -1
  361. package/dist/server/poll-events.d.ts +7 -1
  362. package/dist/server/poll-events.d.ts.map +1 -1
  363. package/dist/server/poll-events.js +24 -7
  364. package/dist/server/poll-events.js.map +1 -1
  365. package/dist/server/poll.d.ts +223 -42
  366. package/dist/server/poll.d.ts.map +1 -1
  367. package/dist/server/poll.js +849 -737
  368. package/dist/server/poll.js.map +1 -1
  369. package/dist/server/realtime-token.d.ts +34 -0
  370. package/dist/server/realtime-token.d.ts.map +1 -0
  371. package/dist/server/realtime-token.js +85 -0
  372. package/dist/server/realtime-token.js.map +1 -0
  373. package/dist/server/sentry-config.d.ts +12 -0
  374. package/dist/server/sentry-config.d.ts.map +1 -1
  375. package/dist/server/sentry-config.js +34 -0
  376. package/dist/server/sentry-config.js.map +1 -1
  377. package/dist/server/short-lived-token.d.ts +46 -0
  378. package/dist/server/short-lived-token.d.ts.map +1 -1
  379. package/dist/server/short-lived-token.js +104 -0
  380. package/dist/server/short-lived-token.js.map +1 -1
  381. package/dist/server/sse.d.ts +1 -1
  382. package/dist/server/sse.d.ts.map +1 -1
  383. package/dist/server/sse.js +4 -0
  384. package/dist/server/sse.js.map +1 -1
  385. package/dist/server/ssr-handler.d.ts.map +1 -1
  386. package/dist/server/ssr-handler.js +5 -3
  387. package/dist/server/ssr-handler.js.map +1 -1
  388. package/dist/server/transcribe-voice.d.ts +1 -1
  389. package/dist/styles/agent-native.css +40 -0
  390. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  391. package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  392. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -0
  393. package/dist/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  394. package/dist/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  395. package/dist/templates/chat/app/routes/_index.tsx +8 -5
  396. package/dist/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  397. package/dist/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  398. package/dist/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  399. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  400. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  401. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  402. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  403. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  404. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  405. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  406. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  407. package/docs/AGENTS.md +1 -1
  408. package/docs/content/agent-mentions.mdx +2 -2
  409. package/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
  410. package/docs/content/agent-teams.mdx +4 -4
  411. package/docs/content/dispatch.mdx +10 -10
  412. package/docs/content/drop-in-agent.mdx +4 -4
  413. package/docs/content/external-agents.mdx +1 -1
  414. package/docs/content/frames.mdx +1 -1
  415. package/docs/content/integrations.mdx +13 -2
  416. package/docs/content/key-concepts.mdx +2 -2
  417. package/docs/content/mcp-clients.mdx +7 -1
  418. package/docs/content/multi-app-workspace.mdx +3 -3
  419. package/docs/content/real-time-collaboration.mdx +33 -15
  420. package/docs/content/recurring-jobs.mdx +7 -7
  421. package/docs/content/skills-guide.mdx +4 -4
  422. package/docs/content/template-brain.mdx +1 -1
  423. package/docs/content/toolkit-collaboration.mdx +1 -1
  424. package/docs/content/using-your-agent.mdx +1 -1
  425. package/docs/content/what-is-agent-native.mdx +4 -4
  426. package/docs/content/workspace-management.mdx +4 -4
  427. package/package.json +6 -3
  428. package/src/agent/production-agent.ts +13 -3
  429. package/src/cli/doctor.ts +6 -1
  430. package/src/client/AgentPanel.tsx +79 -41
  431. package/src/client/AssistantChat.tsx +71 -25
  432. package/src/client/FeedbackButton.tsx +33 -7
  433. package/src/client/MultiTabAssistantChat.tsx +6 -8
  434. package/src/client/agent-chat-adapter.ts +11 -2
  435. package/src/client/agent-page/AgentTabFrame.tsx +22 -3
  436. package/src/client/agent-page/AgentTabsPage.tsx +34 -19
  437. package/src/client/analytics.ts +7 -0
  438. package/src/client/chat/message-components.tsx +43 -16
  439. package/src/client/chat/tool-call-display.tsx +30 -18
  440. package/src/client/chat/tool-render-registry.tsx +2 -0
  441. package/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  442. package/src/client/resources/ResourceTree.tsx +7 -10
  443. package/src/client/resources/ResourcesPanel.tsx +33 -70
  444. package/src/client/resources/mcp-connection-resume.ts +107 -0
  445. package/src/client/resources/mcp-integration-catalog.ts +65 -12
  446. package/src/client/resources/mcp-integration-logos.ts +4 -0
  447. package/src/client/sse-event-processor.ts +50 -14
  448. package/src/client/use-db-sync.ts +370 -21
  449. package/src/collab/client.ts +26 -2
  450. package/src/connections/catalog.ts +7 -1
  451. package/src/db/ddl-guard.ts +49 -10
  452. package/src/deploy/build.ts +25 -2
  453. package/src/eject/remote-mcp-presets.ts +37 -5
  454. package/src/guards/explicit-collab-access.ts +229 -0
  455. package/src/guards/index.ts +1 -0
  456. package/src/localization/default-messages.ts +54 -5
  457. package/src/mcp/connect-route.ts +34 -51
  458. package/src/provider-api/index.ts +1 -1
  459. package/src/realtime-protocol.ts +92 -0
  460. package/src/server/collab-plugin.ts +137 -18
  461. package/src/server/core-routes-plugin.ts +7 -0
  462. package/src/server/index.ts +2 -0
  463. package/src/server/poll-events.ts +29 -8
  464. package/src/server/poll.ts +1123 -865
  465. package/src/server/realtime-token.ts +106 -0
  466. package/src/server/sentry-config.ts +40 -0
  467. package/src/server/short-lived-token.ts +156 -0
  468. package/src/server/sse.ts +15 -0
  469. package/src/server/ssr-handler.ts +9 -3
  470. package/src/styles/agent-native.css +40 -0
  471. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  472. package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  473. package/src/templates/chat/app/components/layout/Layout.tsx +1 -0
  474. package/src/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  475. package/src/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  476. package/src/templates/chat/app/routes/_index.tsx +8 -5
  477. package/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  478. package/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  479. package/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  480. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  481. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  482. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  483. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  484. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  485. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  486. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  487. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
@@ -0,0 +1,89 @@
1
+ import { IconDots, IconPlus } from "@tabler/icons-react";
2
+ import { useEffect, useMemo, useState } from "react";
3
+
4
+ import {
5
+ ChatHistoryList,
6
+ type ChatHistoryItem,
7
+ type ChatHistoryListProps,
8
+ } from "./ChatHistoryList.js";
9
+
10
+ export interface ChatHistoryRailLabels {
11
+ newChat: string;
12
+ showMore: string;
13
+ showLess: string;
14
+ }
15
+
16
+ export interface ChatHistoryRailProps extends Omit<
17
+ ChatHistoryListProps,
18
+ "footer" | "items" | "sections" | "variant"
19
+ > {
20
+ items: ChatHistoryItem[];
21
+ onNewChat: () => void;
22
+ railLabels: ChatHistoryRailLabels;
23
+ previewCount?: number;
24
+ expandedCount?: number;
25
+ }
26
+
27
+ /**
28
+ * Compact recent-chat rail for app sidebars. Hosts own thread persistence,
29
+ * sorting, routing, and mutations; the rail only owns progressive disclosure.
30
+ */
31
+ export function ChatHistoryRail({
32
+ items,
33
+ onNewChat,
34
+ railLabels,
35
+ previewCount = 5,
36
+ expandedCount = 15,
37
+ className,
38
+ emptyLabel,
39
+ ...listProps
40
+ }: ChatHistoryRailProps) {
41
+ const [expanded, setExpanded] = useState(false);
42
+ const collapsedLimit = Math.max(1, previewCount);
43
+ const expandedLimit = Math.max(collapsedLimit, expandedCount);
44
+ const canExpand = items.length > collapsedLimit;
45
+ const visibleItems = useMemo(
46
+ () => items.slice(0, expanded ? expandedLimit : collapsedLimit),
47
+ [collapsedLimit, expanded, expandedLimit, items],
48
+ );
49
+
50
+ useEffect(() => {
51
+ if (!canExpand) setExpanded(false);
52
+ }, [canExpand]);
53
+
54
+ const footer = (
55
+ <div className="an-chat-history-rail__footer">
56
+ <button
57
+ type="button"
58
+ className="an-chat-history-rail__new-chat"
59
+ onClick={onNewChat}
60
+ >
61
+ <IconPlus size={13} strokeWidth={1.8} aria-hidden="true" />
62
+ <span>{railLabels.newChat}</span>
63
+ </button>
64
+ {canExpand && (
65
+ <button
66
+ type="button"
67
+ className="an-chat-history-rail__disclosure"
68
+ onClick={() => setExpanded((current) => !current)}
69
+ aria-expanded={expanded}
70
+ aria-label={expanded ? railLabels.showLess : railLabels.showMore}
71
+ title={expanded ? railLabels.showLess : railLabels.showMore}
72
+ >
73
+ <IconDots size={14} strokeWidth={1.8} aria-hidden="true" />
74
+ </button>
75
+ )}
76
+ </div>
77
+ );
78
+
79
+ return (
80
+ <ChatHistoryList
81
+ {...listProps}
82
+ items={visibleItems}
83
+ footer={footer}
84
+ emptyLabel={emptyLabel ?? null}
85
+ variant="rail"
86
+ className={["an-chat-history-rail", className].filter(Boolean).join(" ")}
87
+ />
88
+ );
89
+ }
@@ -5,3 +5,8 @@ export {
5
5
  type ChatHistoryListProps,
6
6
  type ChatHistorySection,
7
7
  } from "./ChatHistoryList.js";
8
+ export {
9
+ ChatHistoryRail,
10
+ type ChatHistoryRailLabels,
11
+ type ChatHistoryRailProps,
12
+ } from "./ChatHistoryRail.js";
@@ -16,6 +16,7 @@
16
16
  .an-chat-history {
17
17
  display: flex;
18
18
  flex-direction: column;
19
+ min-width: 0;
19
20
  min-height: 0;
20
21
  }
21
22
 
@@ -49,6 +50,7 @@
49
50
 
50
51
  .an-chat-history__list {
51
52
  overflow-y: auto;
53
+ min-width: 0;
52
54
  padding: 4px 0;
53
55
  max-height: 16rem;
54
56
  }
@@ -59,6 +61,10 @@
59
61
  min-height: 0;
60
62
  }
61
63
 
64
+ .an-chat-history-rail .an-chat-history__state:empty {
65
+ display: none;
66
+ }
67
+
62
68
  .an-chat-history__state {
63
69
  padding: 16px 12px;
64
70
  text-align: center;
@@ -72,6 +78,55 @@
72
78
  color: hsl(38 92% 50%);
73
79
  }
74
80
 
81
+ .an-chat-history-rail__footer {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 2px;
85
+ min-width: 0;
86
+ padding: 1px 4px 0;
87
+ }
88
+
89
+ .an-chat-history-rail__disclosure,
90
+ .an-chat-history-rail__new-chat {
91
+ display: flex;
92
+ align-items: center;
93
+ border: 0;
94
+ border-radius: calc(var(--radius) - 2px);
95
+ background: transparent;
96
+ color: hsl(var(--muted-foreground) / 0.72);
97
+ cursor: pointer;
98
+ }
99
+
100
+ .an-chat-history-rail__disclosure:hover,
101
+ .an-chat-history-rail__new-chat:hover {
102
+ background: hsl(var(--accent) / 0.5);
103
+ color: hsl(var(--accent-foreground));
104
+ }
105
+
106
+ .an-chat-history-rail__disclosure:focus-visible,
107
+ .an-chat-history-rail__new-chat:focus-visible {
108
+ outline: 2px solid hsl(var(--ring));
109
+ outline-offset: -2px;
110
+ }
111
+
112
+ .an-chat-history-rail__disclosure {
113
+ justify-content: center;
114
+ flex: 0 0 auto;
115
+ width: 30px;
116
+ min-height: 30px;
117
+ }
118
+
119
+ .an-chat-history-rail__new-chat {
120
+ flex: 1 1 auto;
121
+ gap: 8px;
122
+ min-width: 0;
123
+ min-height: 30px;
124
+ padding: 6px 10px;
125
+ font-size: 12px;
126
+ font-weight: 500;
127
+ text-align: start;
128
+ }
129
+
75
130
  .an-chat-history__section-label {
76
131
  padding: 6px 12px 4px;
77
132
  font-size: 10px;
@@ -83,12 +138,14 @@
83
138
  .an-chat-history--rail .an-chat-history__section {
84
139
  display: grid;
85
140
  gap: 1px;
141
+ min-width: 0;
86
142
  }
87
143
 
88
144
  /* ── Row ──────────────────────────────────────────────────────────────── */
89
145
 
90
146
  .an-chat-history-row {
91
147
  position: relative;
148
+ min-width: 0;
92
149
  }
93
150
 
94
151
  .an-chat-history--rail .an-chat-history-row {
@@ -99,6 +156,7 @@
99
156
  .an-chat-history-row__button {
100
157
  display: block;
101
158
  width: 100%;
159
+ min-width: 0;
102
160
  padding: 8px 12px;
103
161
  border: 0;
104
162
  background: transparent;
@@ -141,6 +199,7 @@
141
199
  justify-content: space-between;
142
200
  gap: 8px;
143
201
  min-width: 0;
202
+ overflow: hidden;
144
203
  }
145
204
 
146
205
  .an-chat-history-row__title {
@@ -1,4 +1,5 @@
1
1
  import { Button } from "@agent-native/toolkit/ui/button";
2
+ import { Checkbox } from "@agent-native/toolkit/ui/checkbox";
2
3
  import {
3
4
  Select,
4
5
  SelectContent,
@@ -62,6 +63,7 @@ export interface RealtimeVoiceModeCopy {
62
63
  startWithOpenAiKey: string;
63
64
  startVoiceMode: string;
64
65
  keepDictating: string;
66
+ rememberPreference: string;
65
67
  showChat: string;
66
68
  hideChat: string;
67
69
  endVoiceMode: string;
@@ -111,6 +113,8 @@ export interface RealtimeVoiceModeEntryProps {
111
113
  onOpenChange?: (open: boolean) => void;
112
114
  onStartVoiceMode: () => void;
113
115
  onKeepDictating: () => void;
116
+ preferredMode?: RealtimeVoiceInputMode | null;
117
+ onRememberPreference?: (mode: RealtimeVoiceInputMode) => void;
114
118
  setupRequired?: boolean;
115
119
  openAiConfigured?: boolean;
116
120
  connectingBuilder?: boolean;
@@ -119,6 +123,8 @@ export interface RealtimeVoiceModeEntryProps {
119
123
  className?: string;
120
124
  }
121
125
 
126
+ export type RealtimeVoiceInputMode = "realtime" | "dictation";
127
+
122
128
  /**
123
129
  * Composer mic entry point for apps that support a full-duplex voice session.
124
130
  * The first click offers voice mode without silently changing the existing
@@ -132,6 +138,8 @@ export function RealtimeVoiceModeEntry({
132
138
  onOpenChange,
133
139
  onStartVoiceMode,
134
140
  onKeepDictating,
141
+ preferredMode = null,
142
+ onRememberPreference,
135
143
  setupRequired = false,
136
144
  openAiConfigured = false,
137
145
  connectingBuilder = false,
@@ -143,6 +151,8 @@ export function RealtimeVoiceModeEntry({
143
151
  const open = controlledOpen ?? uncontrolledOpen;
144
152
  const titleId = useId();
145
153
  const descriptionId = useId();
154
+ const rememberPreferenceId = useId();
155
+ const [rememberPreference, setRememberPreference] = useState(false);
146
156
  const [collisionBoundary, setCollisionBoundary] =
147
157
  useState<HTMLElement | null>(null);
148
158
 
@@ -159,8 +169,9 @@ export function RealtimeVoiceModeEntry({
159
169
  onOpenChange?.(nextOpen);
160
170
  };
161
171
 
162
- const choose = (callback: () => void) => {
172
+ const choose = (mode: RealtimeVoiceInputMode, callback: () => void) => {
163
173
  setOpen(false);
174
+ if (rememberPreference) onRememberPreference?.(mode);
164
175
  callback();
165
176
  };
166
177
 
@@ -177,6 +188,23 @@ export function RealtimeVoiceModeEntry({
177
188
  disabled={disabled}
178
189
  aria-label={copy.entryButtonLabel}
179
190
  aria-expanded={open}
191
+ onClick={(event) => {
192
+ if (!preferredMode) return;
193
+ if (
194
+ preferredMode === "realtime" &&
195
+ (providerStatusPending || setupRequired)
196
+ ) {
197
+ return;
198
+ }
199
+ event.preventDefault();
200
+ event.stopPropagation();
201
+ choose(
202
+ preferredMode,
203
+ preferredMode === "realtime"
204
+ ? onStartVoiceMode
205
+ : onKeepDictating,
206
+ );
207
+ }}
180
208
  className={cn(
181
209
  "size-7 shrink-0 text-muted-foreground hover:text-foreground",
182
210
  className,
@@ -197,43 +225,54 @@ export function RealtimeVoiceModeEntry({
197
225
  collisionPadding={16}
198
226
  data-collision-boundary={collisionBoundary ? "agent-panel" : "viewport"}
199
227
  className={cn(
200
- "p-4",
228
+ setupRequired ? "p-4" : "p-3",
201
229
  setupRequired
202
230
  ? "w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,30rem),30rem)]"
203
- : "w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,22rem),22rem)]",
231
+ : "w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,18rem),18rem)]",
204
232
  )}
205
233
  aria-labelledby={titleId}
206
- aria-describedby={descriptionId}
234
+ aria-describedby={setupRequired ? descriptionId : undefined}
207
235
  >
208
- <div className="grid gap-3">
236
+ <div className={cn("grid", setupRequired ? "gap-3" : "gap-2.5")}>
209
237
  <div className="grid gap-1">
210
238
  <h2 id={titleId} className="text-sm font-semibold text-foreground">
211
239
  {setupRequired ? copy.setupTitle : copy.promptTitle}
212
240
  </h2>
213
- <p
214
- id={descriptionId}
215
- className="text-sm leading-relaxed text-muted-foreground"
216
- >
217
- {setupRequired ? copy.setupDescription : copy.promptDescription}
218
- </p>
241
+ {setupRequired ? (
242
+ <p
243
+ id={descriptionId}
244
+ className="text-sm leading-relaxed text-muted-foreground"
245
+ >
246
+ {copy.setupDescription}
247
+ </p>
248
+ ) : null}
219
249
  </div>
220
250
 
221
- <div className="flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap sm:justify-end">
222
- <Button
223
- type="button"
224
- variant="ghost"
225
- size="sm"
226
- onClick={() => choose(onKeepDictating)}
227
- >
228
- {copy.keepDictating}
229
- </Button>
251
+ <div
252
+ className={cn(
253
+ "gap-2",
254
+ setupRequired
255
+ ? "flex flex-col-reverse sm:flex-row sm:flex-wrap sm:justify-end"
256
+ : "grid",
257
+ )}
258
+ >
230
259
  {setupRequired ? (
231
260
  <>
261
+ <Button
262
+ type="button"
263
+ variant="ghost"
264
+ size="sm"
265
+ onClick={() => choose("dictation", onKeepDictating)}
266
+ >
267
+ {copy.keepDictating}
268
+ </Button>
232
269
  <Button
233
270
  type="button"
234
271
  variant="outline"
235
272
  size="sm"
236
- onClick={() => choose(onUseOpenAiKey ?? onStartVoiceMode)}
273
+ onClick={() =>
274
+ choose("realtime", onUseOpenAiKey ?? onStartVoiceMode)
275
+ }
237
276
  >
238
277
  {openAiConfigured
239
278
  ? copy.startWithOpenAiKey
@@ -244,7 +283,9 @@ export function RealtimeVoiceModeEntry({
244
283
  size="sm"
245
284
  className="whitespace-nowrap"
246
285
  disabled={connectingBuilder}
247
- onClick={() => choose(onConnectBuilder ?? onStartVoiceMode)}
286
+ onClick={() =>
287
+ choose("realtime", onConnectBuilder ?? onStartVoiceMode)
288
+ }
248
289
  >
249
290
  {connectingBuilder ? (
250
291
  <IconLoader2 className="animate-spin" />
@@ -253,21 +294,49 @@ export function RealtimeVoiceModeEntry({
253
294
  </Button>
254
295
  </>
255
296
  ) : (
256
- <Button
257
- type="button"
258
- size="sm"
259
- disabled={providerStatusPending}
260
- onClick={() => choose(onStartVoiceMode)}
261
- >
262
- {providerStatusPending ? (
263
- <IconLoader2 className="animate-spin" />
264
- ) : (
265
- <IconMicrophone />
266
- )}
267
- {copy.startVoiceMode}
268
- </Button>
297
+ <>
298
+ <Button
299
+ type="button"
300
+ size="sm"
301
+ className="w-full"
302
+ disabled={providerStatusPending}
303
+ onClick={() => choose("realtime", onStartVoiceMode)}
304
+ >
305
+ {providerStatusPending ? (
306
+ <IconLoader2 className="animate-spin" />
307
+ ) : (
308
+ <IconMicrophone />
309
+ )}
310
+ {copy.startVoiceMode}
311
+ </Button>
312
+ <Button
313
+ type="button"
314
+ variant="outline"
315
+ size="sm"
316
+ className="w-full"
317
+ onClick={() => choose("dictation", onKeepDictating)}
318
+ >
319
+ {copy.keepDictating}
320
+ </Button>
321
+ </>
269
322
  )}
270
323
  </div>
324
+
325
+ {!setupRequired ? (
326
+ <label
327
+ htmlFor={rememberPreferenceId}
328
+ className="flex cursor-pointer items-center gap-2 text-xs text-muted-foreground"
329
+ >
330
+ <Checkbox
331
+ id={rememberPreferenceId}
332
+ checked={rememberPreference}
333
+ onCheckedChange={(checked) =>
334
+ setRememberPreference(checked === true)
335
+ }
336
+ />
337
+ {copy.rememberPreference}
338
+ </label>
339
+ ) : null}
271
340
  </div>
272
341
  </PopoverContent>
273
342
  </Popover>
@@ -312,7 +381,7 @@ export interface RealtimeVoiceModeInlineSettings {
312
381
 
313
382
  const SILENT_AUDIO_LEVELS = createRealtimeVoiceAudioLevelStore();
314
383
  const WAVEFORM_WEIGHTS = [0.55, 0.82, 1, 0.82, 0.55];
315
- const AUDIO_ACTIVITY_THRESHOLD = 0.035;
384
+ const AUDIO_ACTIVITY_THRESHOLD = 0.1;
316
385
 
317
386
  function usePrefersReducedMotion(): boolean {
318
387
  const [reduced, setReduced] = useState(false);
@@ -831,6 +900,7 @@ export function RealtimeVoiceModeDock({
831
900
  className={cn(
832
901
  "relative isolate size-16 overflow-visible rounded-full ring-1 backdrop-blur-xl transition-transform duration-150 ease-out focus-visible:ring-offset-2 active:scale-[0.97] motion-reduce:transition-none",
833
902
  ORB_STATE_CLASSES[state],
903
+ state === "working" && "agent-realtime-voice-working",
834
904
  )}
835
905
  >
836
906
  <span className="relative z-10 flex items-center justify-center">
@@ -14,9 +14,11 @@ import {
14
14
  IconLoader2,
15
15
  IconX,
16
16
  } from "@tabler/icons-react";
17
+ import { useCallback, useEffect, useState } from "react";
17
18
 
18
19
  import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip.js";
19
20
  import { RealtimeVoiceModeEntry } from "./RealtimeVoiceMode.js";
21
+ import type { RealtimeVoiceInputMode } from "./RealtimeVoiceMode.js";
20
22
  import { useComposerRuntimeAdapters } from "./runtime-adapters.js";
21
23
  import {
22
24
  useRealtimeVoiceModeCopy,
@@ -24,6 +26,16 @@ import {
24
26
  } from "./useRealtimeVoiceMode.js";
25
27
  import type { VoiceDictationApi } from "./useVoiceDictation.js";
26
28
 
29
+ const VOICE_INPUT_PREFERENCE_KEY = "voice-input-preference";
30
+
31
+ export function normalizeVoiceInputPreference(
32
+ value: unknown,
33
+ ): RealtimeVoiceInputMode | null {
34
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
35
+ const mode = (value as { mode?: unknown }).mode;
36
+ return mode === "realtime" || mode === "dictation" ? mode : null;
37
+ }
38
+
27
39
  function openOpenAiKeySettings(): void {
28
40
  window.location.hash = "#secrets:OPENAI_API_KEY";
29
41
  window.dispatchEvent(new Event("agent-panel:open"));
@@ -63,6 +75,41 @@ export function VoiceButton({ voice, isMac, disabled }: VoiceButtonProps) {
63
75
  trackingFlow: "voice_transcription",
64
76
  onConnected: () => voiceProviders.refresh(),
65
77
  });
78
+ const [inputPreference, setInputPreference] = useState<{
79
+ ready: boolean;
80
+ mode: RealtimeVoiceInputMode | null;
81
+ }>({ ready: false, mode: null });
82
+
83
+ useEffect(() => {
84
+ let cancelled = false;
85
+ void Promise.resolve(
86
+ adapters.voice!.readAppState!(VOICE_INPUT_PREFERENCE_KEY),
87
+ )
88
+ .then((value) => {
89
+ if (!cancelled) {
90
+ setInputPreference({
91
+ ready: true,
92
+ mode: normalizeVoiceInputPreference(value),
93
+ });
94
+ }
95
+ })
96
+ .catch(() => {
97
+ if (!cancelled) setInputPreference({ ready: true, mode: null });
98
+ });
99
+ return () => {
100
+ cancelled = true;
101
+ };
102
+ }, [adapters]);
103
+
104
+ const rememberInputPreference = useCallback(
105
+ (mode: RealtimeVoiceInputMode) => {
106
+ setInputPreference({ ready: true, mode });
107
+ void Promise.resolve(
108
+ adapters.voice!.setAppState!(VOICE_INPUT_PREFERENCE_KEY, { mode }),
109
+ ).catch(() => undefined);
110
+ },
111
+ [adapters],
112
+ );
66
113
 
67
114
  if (!supported) return null;
68
115
 
@@ -90,6 +137,8 @@ export function VoiceButton({ voice, isMac, disabled }: VoiceButtonProps) {
90
137
  }}
91
138
  onStartVoiceMode={() => void realtimeVoice.start()}
92
139
  onKeepDictating={() => void start()}
140
+ preferredMode={inputPreference.ready ? inputPreference.mode : null}
141
+ onRememberPreference={rememberInputPreference}
93
142
  />
94
143
  );
95
144
  }
@@ -25,7 +25,8 @@ export function normalizeRealtimeVoiceRms(samples: Uint8Array): number {
25
25
  sumSquares += normalized * normalized;
26
26
  }
27
27
  const rms = Math.sqrt(sumSquares / samples.length);
28
- return clampLevel(Math.max(0, rms - 0.012) * 3.2);
28
+ const activeLevel = clampLevel((rms - 0.012) / 0.18);
29
+ return Math.sqrt(activeLevel);
29
30
  }
30
31
 
31
32
  export function smoothRealtimeVoiceLevel(
@@ -1018,6 +1018,7 @@ function voiceCopy(t: (key: string) => string): RealtimeVoiceModeCopy {
1018
1018
  startWithOpenAiKey: t("agentPanel.voiceMode.startWithOpenAiKey"),
1019
1019
  startVoiceMode: t("agentPanel.voiceMode.start"),
1020
1020
  keepDictating: t("agentPanel.voiceMode.keepDictating"),
1021
+ rememberPreference: t("agentPanel.voiceMode.rememberPreference"),
1021
1022
  showChat: t("agentPanel.voiceMode.showChat"),
1022
1023
  hideChat: t("agentPanel.voiceMode.hideChat"),
1023
1024
  endVoiceMode: t("agentPanel.voiceMode.end"),
@@ -1353,7 +1354,7 @@ function useRealtimeVoiceModeController(
1353
1354
  analyser.fftSize = 512;
1354
1355
  const source = context.createMediaStreamSource(stream);
1355
1356
  source.connect(analyser);
1356
- const buffer = new Uint8Array(analyser.frequencyBinCount);
1357
+ const buffer = new Uint8Array(analyser.fftSize);
1357
1358
  if (channel === "input") {
1358
1359
  inputSourceRef.current?.disconnect();
1359
1360
  inputSourceRef.current = source;
@@ -1728,9 +1729,10 @@ function useRealtimeVoiceModeController(
1728
1729
  audio.setAttribute("playsinline", "");
1729
1730
  audioRef.current = audio;
1730
1731
  peer.ontrack = (trackEvent) => {
1731
- const remoteStream = trackEvent.streams[0] ?? null;
1732
+ const remoteStream =
1733
+ trackEvent.streams[0] ?? new MediaStream([trackEvent.track]);
1732
1734
  audio.srcObject = remoteStream;
1733
- if (remoteStream) attachAudioMeter(remoteStream, "output");
1735
+ attachAudioMeter(remoteStream, "output");
1734
1736
  void audio.play().catch(() => undefined);
1735
1737
  };
1736
1738
 
@@ -19,3 +19,38 @@
19
19
  * compiled `.js` next to it for consumers that only install `dist/`.
20
20
  */
21
21
  @source "./**/*.{js,ts,tsx}";
22
+
23
+ .agent-realtime-voice-working::after {
24
+ position: absolute;
25
+ z-index: 0;
26
+ inset: 0;
27
+ border-radius: inherit;
28
+ pointer-events: none;
29
+ content: "";
30
+ }
31
+
32
+ @media (prefers-reduced-motion: no-preference) {
33
+ .agent-realtime-voice-working::after {
34
+ background: linear-gradient(
35
+ 90deg,
36
+ transparent 0%,
37
+ transparent 36%,
38
+ hsl(var(--foreground) / 0.14) 50%,
39
+ transparent 64%,
40
+ transparent 100%
41
+ );
42
+ background-repeat: repeat;
43
+ background-size: 14rem 100%;
44
+ animation: agent-realtime-voice-shine 2.6s linear infinite;
45
+ }
46
+
47
+ @keyframes agent-realtime-voice-shine {
48
+ from {
49
+ background-position: 0 0;
50
+ }
51
+
52
+ to {
53
+ background-position: 14rem 0;
54
+ }
55
+ }
56
+ }