@agent-native/core 0.115.4 → 0.116.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 (408) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/corpus/README.md +3 -3
  3. package/corpus/core/CHANGELOG.md +28 -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 +1 -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/chat/message-components.tsx +43 -16
  36. package/corpus/core/src/client/chat/tool-call-display.tsx +30 -18
  37. package/corpus/core/src/client/chat/tool-render-registry.tsx +2 -0
  38. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  39. package/corpus/core/src/client/resources/ResourceTree.tsx +7 -10
  40. package/corpus/core/src/client/resources/ResourcesPanel.tsx +33 -70
  41. package/corpus/core/src/client/resources/mcp-connection-resume.ts +107 -0
  42. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +65 -12
  43. package/corpus/core/src/client/resources/mcp-integration-logos.ts +4 -0
  44. package/corpus/core/src/client/sse-event-processor.ts +50 -14
  45. package/corpus/core/src/client/use-db-sync.ts +16 -6
  46. package/corpus/core/src/connections/catalog.ts +7 -1
  47. package/corpus/core/src/eject/remote-mcp-presets.ts +37 -5
  48. package/corpus/core/src/guards/explicit-collab-access.ts +229 -0
  49. package/corpus/core/src/guards/index.ts +1 -0
  50. package/corpus/core/src/localization/default-messages.ts +54 -5
  51. package/corpus/core/src/mcp/connect-route.ts +34 -51
  52. package/corpus/core/src/provider-api/index.ts +1 -1
  53. package/corpus/core/src/server/collab-plugin.ts +137 -18
  54. package/corpus/core/src/server/index.ts +2 -0
  55. package/corpus/core/src/styles/agent-native.css +40 -0
  56. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  57. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  58. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  59. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  60. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  61. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  62. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  63. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  64. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  65. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  66. package/corpus/templates/analytics/AGENTS.md +7 -2
  67. package/corpus/templates/analytics/actions/export-dashboard-panel-to-google-sheet.ts +144 -0
  68. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -88
  69. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +34 -37
  70. package/corpus/templates/analytics/app/global.css +0 -4
  71. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -7
  72. package/corpus/templates/analytics/app/i18n-data.ts +107 -0
  73. package/corpus/templates/analytics/app/lib/data-source-status.ts +9 -0
  74. package/corpus/templates/analytics/app/lib/sql-query.ts +1 -2
  75. package/corpus/templates/analytics/app/pages/Ask.tsx +2 -7
  76. package/corpus/templates/analytics/app/pages/DataSources.tsx +48 -0
  77. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +59 -0
  78. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -0
  79. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -1
  80. package/corpus/templates/analytics/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  81. package/corpus/templates/analytics/changelog/2026-07-22-daily-dashboard-email-query-batches.md +6 -0
  82. package/corpus/templates/analytics/changelog/2026-07-22-export-table-panels-directly-to-google-sheets.md +6 -0
  83. package/corpus/templates/analytics/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  84. package/corpus/templates/analytics/changelog/2026-07-22-recent-chats-can-expand-in-the-sidebar.md +6 -0
  85. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +8 -8
  86. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +31 -0
  87. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +3 -5
  88. package/corpus/templates/analytics/server/lib/dashboard-report.ts +12 -0
  89. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +3 -5
  90. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +150 -21
  91. package/corpus/templates/analytics/server/lib/google-sheets-export.ts +188 -0
  92. package/corpus/templates/analytics/server/lib/provider-api.ts +7 -2
  93. package/corpus/templates/analytics/server/plugins/collab.ts +6 -3
  94. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  95. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  96. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +35 -13
  97. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  98. package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
  99. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +54 -62
  100. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -6
  101. package/corpus/templates/assets/app/routes/_index.tsx +8 -5
  102. package/corpus/templates/assets/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  103. package/corpus/templates/assets/changelog/2026-07-22-recent-chats-expand-from-a-compact-list.md +6 -0
  104. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  105. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  106. package/corpus/templates/brain/actions/rotate-source-ingest-token.ts +77 -0
  107. package/corpus/templates/brain/app/components/layout/Layout.tsx +5 -1
  108. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -51
  109. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +2 -9
  110. package/corpus/templates/brain/app/i18n-data.ts +120 -0
  111. package/corpus/templates/brain/app/routes/sources.tsx +156 -5
  112. package/corpus/templates/brain/changelog/2026-07-22-chat-runs-always-end-with-an-answer.md +6 -0
  113. package/corpus/templates/brain/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  114. package/corpus/templates/brain/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  115. package/corpus/templates/brain/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  116. package/corpus/templates/brain/changelog/2026-07-22-long-chat-titles-stay-inside-the-sidebar.md +6 -0
  117. package/corpus/templates/brain/changelog/2026-07-22-new-clips-and-webhook-sources-now-show-their-secure-connecti.md +6 -0
  118. package/corpus/templates/brain/jobs/emit-netlify-brain-queue-cron.ts +125 -0
  119. package/corpus/templates/brain/netlify.toml +5 -1
  120. package/corpus/templates/brain/scripts/configure-prod-clips-source.ts +50 -0
  121. package/corpus/templates/brain/server/lib/capture-sanitization.ts +64 -6
  122. package/corpus/templates/brain/server/plugins/agent-chat.ts +4 -0
  123. package/corpus/templates/brain/server/plugins/brain-jobs.ts +1 -0
  124. package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +55 -0
  125. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  126. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  127. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  128. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  129. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -0
  130. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  131. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  132. package/corpus/templates/chat/app/routes/_index.tsx +8 -5
  133. package/corpus/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  134. package/corpus/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  135. package/corpus/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  136. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  137. package/corpus/templates/clips/AGENTS.md +9 -1
  138. package/corpus/templates/clips/actions/export-to-brain.ts +82 -6
  139. package/corpus/templates/clips/actions/request-transcript.ts +25 -14
  140. package/corpus/templates/clips/changelog/2026-07-22-clips-transcripts-now-retry-brain-export.md +5 -0
  141. package/corpus/templates/clips/changelog/2026-07-22-german-umlauts-and-other-non-ascii-characters-now-paste-corr.md +6 -0
  142. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +26 -3
  143. package/corpus/templates/clips/jobs/emit-netlify-brain-export-cron.ts +118 -0
  144. package/corpus/templates/clips/netlify.toml +4 -1
  145. package/corpus/templates/clips/scripts/configure-prod-brain-export.ts +92 -0
  146. package/corpus/templates/clips/server/jobs/brain-export.ts +182 -0
  147. package/corpus/templates/clips/server/lib/brain-export-state.ts +42 -0
  148. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +5 -1
  149. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  150. package/corpus/templates/clips/server/register-secrets.ts +42 -0
  151. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +10 -1
  152. package/corpus/templates/clips/server/routes/api/clips/brain-export/run.post.ts +45 -0
  153. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  154. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  155. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -1
  156. package/corpus/templates/content/server/plugins/collab.ts +1 -1
  157. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  158. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  159. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +2 -0
  160. package/corpus/templates/crm/actions/run.ts +2 -6
  161. package/corpus/templates/crm/actions/update-crm-record.ts +4 -2
  162. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +6 -0
  163. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +6 -0
  164. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +6 -0
  165. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +6 -0
  166. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  167. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +4 -1
  168. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  169. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  170. package/corpus/templates/design/server/plugins/collab.ts +11 -8
  171. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  172. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  173. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  174. package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -0
  175. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +1 -1
  176. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +2 -9
  177. package/corpus/templates/forms/app/root.tsx +1 -6
  178. package/corpus/templates/forms/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  179. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  180. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  181. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  182. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  183. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  184. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  185. package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -1
  186. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +57 -72
  187. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +2 -9
  188. package/corpus/templates/plan/app/lib/create-plan-routing.ts +2 -2
  189. package/corpus/templates/plan/app/pages/PlanChatPage.tsx +0 -1
  190. package/corpus/templates/plan/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  191. package/corpus/templates/plan/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  192. package/corpus/templates/plan/changelog/2026-07-22-recent-chats-and-sidebar-controls-are-more-compact.md +6 -0
  193. package/corpus/templates/plan/server/plugins/collab.ts +5 -2
  194. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  195. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +1 -1
  196. package/corpus/templates/slides/server/plugins/collab.ts +11 -8
  197. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  198. package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
  199. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +5 -22
  200. package/corpus/templates/tasks/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  201. package/corpus/toolkit/CHANGELOG.md +7 -0
  202. package/corpus/toolkit/README.md +12 -3
  203. package/corpus/toolkit/package.json +1 -1
  204. package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +89 -0
  205. package/corpus/toolkit/src/chat-history/index.ts +5 -0
  206. package/corpus/toolkit/src/chat-history.css +59 -0
  207. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +106 -36
  208. package/corpus/toolkit/src/composer/VoiceButton.tsx +49 -0
  209. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +2 -1
  210. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +5 -3
  211. package/corpus/toolkit/src/styles.css +35 -0
  212. package/dist/agent/production-agent.d.ts.map +1 -1
  213. package/dist/agent/production-agent.js +13 -3
  214. package/dist/agent/production-agent.js.map +1 -1
  215. package/dist/cli/doctor.d.ts +1 -1
  216. package/dist/cli/doctor.d.ts.map +1 -1
  217. package/dist/cli/doctor.js +5 -2
  218. package/dist/cli/doctor.js.map +1 -1
  219. package/dist/client/AgentPanel.d.ts +1 -0
  220. package/dist/client/AgentPanel.d.ts.map +1 -1
  221. package/dist/client/AgentPanel.js +38 -18
  222. package/dist/client/AgentPanel.js.map +1 -1
  223. package/dist/client/AssistantChat.d.ts.map +1 -1
  224. package/dist/client/AssistantChat.js +55 -20
  225. package/dist/client/AssistantChat.js.map +1 -1
  226. package/dist/client/FeedbackButton.d.ts +4 -2
  227. package/dist/client/FeedbackButton.d.ts.map +1 -1
  228. package/dist/client/FeedbackButton.js +18 -2
  229. package/dist/client/FeedbackButton.js.map +1 -1
  230. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  231. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  232. package/dist/client/MultiTabAssistantChat.js +2 -3
  233. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  234. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  235. package/dist/client/agent-chat-adapter.js +11 -3
  236. package/dist/client/agent-chat-adapter.js.map +1 -1
  237. package/dist/client/agent-page/AgentTabFrame.d.ts +3 -1
  238. package/dist/client/agent-page/AgentTabFrame.d.ts.map +1 -1
  239. package/dist/client/agent-page/AgentTabFrame.js +3 -2
  240. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  241. package/dist/client/agent-page/AgentTabsPage.d.ts +2 -0
  242. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  243. package/dist/client/agent-page/AgentTabsPage.js +26 -15
  244. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  245. package/dist/client/chat/message-components.d.ts +6 -1
  246. package/dist/client/chat/message-components.d.ts.map +1 -1
  247. package/dist/client/chat/message-components.js +17 -11
  248. package/dist/client/chat/message-components.js.map +1 -1
  249. package/dist/client/chat/tool-call-display.d.ts +11 -1
  250. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  251. package/dist/client/chat/tool-call-display.js +16 -17
  252. package/dist/client/chat/tool-call-display.js.map +1 -1
  253. package/dist/client/chat/tool-render-registry.d.ts +2 -0
  254. package/dist/client/chat/tool-render-registry.d.ts.map +1 -1
  255. package/dist/client/chat/tool-render-registry.js.map +1 -1
  256. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  257. package/dist/client/resources/McpConnectionSuggestion.js +15 -1
  258. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  259. package/dist/client/resources/ResourceTree.d.ts.map +1 -1
  260. package/dist/client/resources/ResourceTree.js +3 -3
  261. package/dist/client/resources/ResourceTree.js.map +1 -1
  262. package/dist/client/resources/ResourcesPanel.d.ts +1 -0
  263. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  264. package/dist/client/resources/ResourcesPanel.js +34 -20
  265. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  266. package/dist/client/resources/mcp-connection-resume.d.ts +11 -0
  267. package/dist/client/resources/mcp-connection-resume.d.ts.map +1 -0
  268. package/dist/client/resources/mcp-connection-resume.js +95 -0
  269. package/dist/client/resources/mcp-connection-resume.js.map +1 -0
  270. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  271. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  272. package/dist/client/resources/mcp-integration-catalog.js +63 -8
  273. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  274. package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -1
  275. package/dist/client/resources/mcp-integration-logos.js +4 -0
  276. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  277. package/dist/client/sse-event-processor.d.ts +5 -0
  278. package/dist/client/sse-event-processor.d.ts.map +1 -1
  279. package/dist/client/sse-event-processor.js +39 -13
  280. package/dist/client/sse-event-processor.js.map +1 -1
  281. package/dist/client/use-db-sync.d.ts +3 -2
  282. package/dist/client/use-db-sync.d.ts.map +1 -1
  283. package/dist/client/use-db-sync.js +17 -6
  284. package/dist/client/use-db-sync.js.map +1 -1
  285. package/dist/collab/routes.d.ts +1 -1
  286. package/dist/connections/catalog.d.ts +1 -1
  287. package/dist/connections/catalog.d.ts.map +1 -1
  288. package/dist/connections/catalog.js +7 -1
  289. package/dist/connections/catalog.js.map +1 -1
  290. package/dist/eject/remote-mcp-presets.d.ts.map +1 -1
  291. package/dist/eject/remote-mcp-presets.js +33 -3
  292. package/dist/eject/remote-mcp-presets.js.map +1 -1
  293. package/dist/guards/explicit-collab-access.d.ts +8 -0
  294. package/dist/guards/explicit-collab-access.d.ts.map +1 -0
  295. package/dist/guards/explicit-collab-access.js +214 -0
  296. package/dist/guards/explicit-collab-access.js.map +1 -0
  297. package/dist/guards/index.d.ts +1 -0
  298. package/dist/guards/index.d.ts.map +1 -1
  299. package/dist/guards/index.js +1 -0
  300. package/dist/guards/index.js.map +1 -1
  301. package/dist/localization/default-messages.d.ts +43 -0
  302. package/dist/localization/default-messages.d.ts.map +1 -1
  303. package/dist/localization/default-messages.js +48 -5
  304. package/dist/localization/default-messages.js.map +1 -1
  305. package/dist/mcp/connect-route.d.ts.map +1 -1
  306. package/dist/mcp/connect-route.js +34 -48
  307. package/dist/mcp/connect-route.js.map +1 -1
  308. package/dist/notifications/routes.d.ts +3 -3
  309. package/dist/observability/routes.d.ts +1 -1
  310. package/dist/progress/routes.d.ts +1 -1
  311. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  312. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  313. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  314. package/dist/provider-api/index.js +1 -1
  315. package/dist/provider-api/index.js.map +1 -1
  316. package/dist/server/collab-plugin.d.ts +34 -3
  317. package/dist/server/collab-plugin.d.ts.map +1 -1
  318. package/dist/server/collab-plugin.js +65 -12
  319. package/dist/server/collab-plugin.js.map +1 -1
  320. package/dist/server/index.d.ts +1 -1
  321. package/dist/server/index.d.ts.map +1 -1
  322. package/dist/server/index.js.map +1 -1
  323. package/dist/styles/agent-native.css +40 -0
  324. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  325. package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  326. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -0
  327. package/dist/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  328. package/dist/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  329. package/dist/templates/chat/app/routes/_index.tsx +8 -5
  330. package/dist/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  331. package/dist/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  332. package/dist/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  333. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  334. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  335. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  336. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  337. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  338. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  339. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  340. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  341. package/docs/AGENTS.md +1 -1
  342. package/docs/content/agent-mentions.mdx +2 -2
  343. package/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
  344. package/docs/content/agent-teams.mdx +4 -4
  345. package/docs/content/dispatch.mdx +10 -10
  346. package/docs/content/drop-in-agent.mdx +4 -4
  347. package/docs/content/external-agents.mdx +1 -1
  348. package/docs/content/frames.mdx +1 -1
  349. package/docs/content/integrations.mdx +13 -2
  350. package/docs/content/key-concepts.mdx +2 -2
  351. package/docs/content/mcp-clients.mdx +7 -1
  352. package/docs/content/multi-app-workspace.mdx +3 -3
  353. package/docs/content/real-time-collaboration.mdx +33 -15
  354. package/docs/content/recurring-jobs.mdx +7 -7
  355. package/docs/content/skills-guide.mdx +4 -4
  356. package/docs/content/template-brain.mdx +1 -1
  357. package/docs/content/toolkit-collaboration.mdx +1 -1
  358. package/docs/content/using-your-agent.mdx +1 -1
  359. package/docs/content/what-is-agent-native.mdx +4 -4
  360. package/docs/content/workspace-management.mdx +4 -4
  361. package/package.json +3 -3
  362. package/src/agent/production-agent.ts +13 -3
  363. package/src/cli/doctor.ts +6 -1
  364. package/src/client/AgentPanel.tsx +79 -41
  365. package/src/client/AssistantChat.tsx +71 -25
  366. package/src/client/FeedbackButton.tsx +33 -7
  367. package/src/client/MultiTabAssistantChat.tsx +6 -8
  368. package/src/client/agent-chat-adapter.ts +11 -2
  369. package/src/client/agent-page/AgentTabFrame.tsx +22 -3
  370. package/src/client/agent-page/AgentTabsPage.tsx +34 -19
  371. package/src/client/chat/message-components.tsx +43 -16
  372. package/src/client/chat/tool-call-display.tsx +30 -18
  373. package/src/client/chat/tool-render-registry.tsx +2 -0
  374. package/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  375. package/src/client/resources/ResourceTree.tsx +7 -10
  376. package/src/client/resources/ResourcesPanel.tsx +33 -70
  377. package/src/client/resources/mcp-connection-resume.ts +107 -0
  378. package/src/client/resources/mcp-integration-catalog.ts +65 -12
  379. package/src/client/resources/mcp-integration-logos.ts +4 -0
  380. package/src/client/sse-event-processor.ts +50 -14
  381. package/src/client/use-db-sync.ts +16 -6
  382. package/src/connections/catalog.ts +7 -1
  383. package/src/eject/remote-mcp-presets.ts +37 -5
  384. package/src/guards/explicit-collab-access.ts +229 -0
  385. package/src/guards/index.ts +1 -0
  386. package/src/localization/default-messages.ts +54 -5
  387. package/src/mcp/connect-route.ts +34 -51
  388. package/src/provider-api/index.ts +1 -1
  389. package/src/server/collab-plugin.ts +137 -18
  390. package/src/server/index.ts +2 -0
  391. package/src/styles/agent-native.css +40 -0
  392. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  393. package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  394. package/src/templates/chat/app/components/layout/Layout.tsx +1 -0
  395. package/src/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  396. package/src/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  397. package/src/templates/chat/app/routes/_index.tsx +8 -5
  398. package/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  399. package/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  400. package/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  401. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  402. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  403. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  404. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  405. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  406. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  407. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  408. 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
+ }