@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
@@ -3,6 +3,14 @@ import {
3
3
  useActionQuery,
4
4
  } from "@agent-native/core/client/hooks";
5
5
  import { useT } from "@agent-native/core/client/i18n";
6
+ import {
7
+ Dialog,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogFooter,
11
+ DialogHeader,
12
+ DialogTitle,
13
+ } from "@agent-native/toolkit/ui/dialog";
6
14
  import {
7
15
  IconAlertTriangle,
8
16
  IconArchive,
@@ -12,6 +20,7 @@ import {
12
20
  IconCircleCheck,
13
21
  IconCircleDashed,
14
22
  IconClock,
23
+ IconCopy,
15
24
  IconDatabaseImport,
16
25
  IconDotsVertical,
17
26
  IconExternalLink,
@@ -73,6 +82,7 @@ import {
73
82
  type BrainCaptureReviewItem,
74
83
  type EnqueueCapturesDistillationResponse,
75
84
  type CapturesResponse,
85
+ type CreateSourceResponse,
76
86
  type BrainSource,
77
87
  type BrainWorkspaceConnectionGrantState,
78
88
  type BrainWorkspaceConnectionStatus,
@@ -90,6 +100,10 @@ import {
90
100
  sourceReviewRequired,
91
101
  sourceType,
92
102
  } from "@/lib/brain";
103
+ import {
104
+ createOneTimeIngestHandoff,
105
+ type OneTimeIngestHandoff,
106
+ } from "@/lib/ingest-handoff";
93
107
 
94
108
  type Provider = "manual" | "generic" | "clips" | "slack" | "granola" | "github";
95
109
  type CaptureStatusFilter = BrainCaptureReviewStatus | "all";
@@ -1499,16 +1513,25 @@ function SourceFact({ label, value }: { label: string; value: ReactNode }) {
1499
1513
  );
1500
1514
  }
1501
1515
 
1516
+ function ingestSourceKey(source: BrainSource) {
1517
+ const sourceKey = source.config?.sourceKey;
1518
+ return typeof sourceKey === "string" && sourceKey.trim()
1519
+ ? sourceKey.trim()
1520
+ : null;
1521
+ }
1522
+
1502
1523
  function SourceListItem({
1503
1524
  source,
1504
1525
  syncPending,
1505
1526
  onReview,
1527
+ onRotateIngestToken,
1506
1528
  onSync,
1507
1529
  onTune,
1508
1530
  }: {
1509
1531
  source: BrainSource;
1510
1532
  syncPending: boolean;
1511
1533
  onReview: () => void;
1534
+ onRotateIngestToken?: () => void;
1512
1535
  onSync: () => void;
1513
1536
  onTune: () => void;
1514
1537
  }) {
@@ -1602,6 +1625,12 @@ function SourceListItem({
1602
1625
  <IconSettings2 className="size-4" />
1603
1626
  {t("sources.tuneSource")}
1604
1627
  </DropdownMenuItem>
1628
+ {onRotateIngestToken ? (
1629
+ <DropdownMenuItem onSelect={onRotateIngestToken}>
1630
+ <IconRefresh className="size-4" />
1631
+ {t("sources.rotateIngestToken")}
1632
+ </DropdownMenuItem>
1633
+ ) : null}
1605
1634
  </DropdownMenuContent>
1606
1635
  </DropdownMenu>
1607
1636
  </div>
@@ -1633,6 +1662,11 @@ export default function SourcesRoute() {
1633
1662
  const type = params.get("type") ?? "all";
1634
1663
  const [advancedOpen, setAdvancedOpen] = useState(false);
1635
1664
  const [setupOpen, setSetupOpen] = useState(false);
1665
+ const [ingestHandoff, setIngestHandoff] =
1666
+ useState<OneTimeIngestHandoff | null>(null);
1667
+ const [copiedHandoffField, setCopiedHandoffField] = useState<string | null>(
1668
+ null,
1669
+ );
1636
1670
  const [editingSource, setEditingSource] = useState<BrainSource | null>(null);
1637
1671
  const [reviewSource, setReviewSource] = useState<BrainSource | null>(null);
1638
1672
  const [captureStatus, setCaptureStatus] =
@@ -1672,7 +1706,7 @@ export default function SourcesRoute() {
1672
1706
  }
1673
1707
  >("update-source" as any);
1674
1708
  const createSource = useActionMutation<
1675
- unknown,
1709
+ CreateSourceResponse,
1676
1710
  {
1677
1711
  title: string;
1678
1712
  provider: Provider;
@@ -1681,6 +1715,10 @@ export default function SourcesRoute() {
1681
1715
  sourceKey?: string;
1682
1716
  }
1683
1717
  >("create-source" as any);
1718
+ const rotateSourceIngestToken = useActionMutation<
1719
+ CreateSourceResponse,
1720
+ { sourceId: string }
1721
+ >("rotate-source-ingest-token" as any);
1684
1722
  const syncSource = useActionMutation<unknown, { sourceId: string }>(
1685
1723
  "sync-source" as any,
1686
1724
  );
@@ -1818,10 +1856,20 @@ export default function SourcesRoute() {
1818
1856
  setForm((current) => ({ ...current, ...patch }));
1819
1857
  }
1820
1858
 
1821
- function submitSource() {
1859
+ function closeIngestHandoff() {
1860
+ setIngestHandoff(null);
1861
+ setCopiedHandoffField(null);
1862
+ }
1863
+
1864
+ async function copyIngestHandoffValue(value: string, field: string) {
1865
+ await navigator.clipboard.writeText(value);
1866
+ setCopiedHandoffField(field);
1867
+ }
1868
+
1869
+ async function submitSource() {
1822
1870
  const config = buildConfig(form);
1823
1871
  if (editingSource) {
1824
- updateSource.mutate({
1872
+ await updateSource.mutateAsync({
1825
1873
  id: editingSource.id,
1826
1874
  title: form.title.trim() || defaultTitle(form.provider, t),
1827
1875
  status:
@@ -1829,17 +1877,39 @@ export default function SourcesRoute() {
1829
1877
  config,
1830
1878
  });
1831
1879
  } else {
1832
- createSource.mutate({
1880
+ const result = await createSource.mutateAsync({
1833
1881
  title: form.title.trim() || defaultTitle(form.provider, t),
1834
1882
  provider: form.provider,
1835
1883
  visibility: "org",
1836
1884
  config,
1837
1885
  sourceKey: form.sourceKey.trim() || undefined,
1838
1886
  });
1887
+ const handoff = createOneTimeIngestHandoff({
1888
+ origin: window.location.origin,
1889
+ provider: form.provider,
1890
+ result,
1891
+ sourceKey: form.sourceKey,
1892
+ });
1893
+ if (handoff) setIngestHandoff(handoff);
1839
1894
  }
1840
1895
  setSetupOpen(false);
1841
1896
  }
1842
1897
 
1898
+ async function rotateIngestToken(source: BrainSource) {
1899
+ const sourceKey = ingestSourceKey(source);
1900
+ if (!sourceKey) return;
1901
+ const result = await rotateSourceIngestToken.mutateAsync({
1902
+ sourceId: source.id,
1903
+ });
1904
+ const handoff = createOneTimeIngestHandoff({
1905
+ origin: window.location.origin,
1906
+ provider: source.provider ?? "",
1907
+ result,
1908
+ sourceKey,
1909
+ });
1910
+ if (handoff) setIngestHandoff(handoff);
1911
+ }
1912
+
1843
1913
  function toggleCaptureSelection(captureId: string, checked: boolean) {
1844
1914
  setSelectedCaptureIds((current) => {
1845
1915
  const next = new Set(current);
@@ -1909,8 +1979,17 @@ export default function SourcesRoute() {
1909
1979
  <SourceListItem
1910
1980
  key={source.id}
1911
1981
  source={source}
1912
- syncPending={syncSource.isPending}
1982
+ syncPending={
1983
+ syncSource.isPending || rotateSourceIngestToken.isPending
1984
+ }
1913
1985
  onReview={() => openCaptureReview(source)}
1986
+ onRotateIngestToken={
1987
+ (source.provider === "clips" ||
1988
+ source.provider === "generic") &&
1989
+ ingestSourceKey(source)
1990
+ ? () => void rotateIngestToken(source)
1991
+ : undefined
1992
+ }
1914
1993
  onSync={() => syncSource.mutate({ sourceId: source.id })}
1915
1994
  onTune={() => openEdit(source)}
1916
1995
  />
@@ -1928,6 +2007,7 @@ export default function SourcesRoute() {
1928
2007
  connectionProvidersQuery.isError ||
1929
2008
  updateSource.isError ||
1930
2009
  createSource.isError ||
2010
+ rotateSourceIngestToken.isError ||
1931
2011
  syncSource.isError ||
1932
2012
  syncDueSources.isError ||
1933
2013
  enqueueCapturesDistillation.isError ? (
@@ -2302,6 +2382,77 @@ export default function SourcesRoute() {
2302
2382
  </SheetContent>
2303
2383
  </Sheet>
2304
2384
 
2385
+ <Dialog
2386
+ open={Boolean(ingestHandoff)}
2387
+ onOpenChange={(open) => {
2388
+ if (!open) closeIngestHandoff();
2389
+ }}
2390
+ >
2391
+ {ingestHandoff ? (
2392
+ <DialogContent className="max-w-xl gap-5">
2393
+ <DialogHeader>
2394
+ <DialogTitle>
2395
+ {t("sources.connectIngestSource", {
2396
+ source: ingestHandoff.source.title,
2397
+ })}
2398
+ </DialogTitle>
2399
+ <DialogDescription>
2400
+ {t("sources.ingestHandoffDescription")}
2401
+ </DialogDescription>
2402
+ </DialogHeader>
2403
+
2404
+ <div className="grid gap-4">
2405
+ {[
2406
+ [t("sources.endpoint"), ingestHandoff.endpoint, "endpoint"],
2407
+ [t("sources.sourceKey"), ingestHandoff.sourceKey, "source-key"],
2408
+ [
2409
+ t("sources.ingestToken"),
2410
+ ingestHandoff.ingestToken,
2411
+ "ingest-token",
2412
+ ],
2413
+ ].map(([label, value, field]) => (
2414
+ <div key={field} className="grid gap-2">
2415
+ <Label htmlFor={`ingest-handoff-${field}`}>{label}</Label>
2416
+ <div className="flex gap-2">
2417
+ <Input
2418
+ id={`ingest-handoff-${field}`}
2419
+ value={value}
2420
+ readOnly
2421
+ className="min-w-0 font-mono text-xs"
2422
+ />
2423
+ <Button
2424
+ type="button"
2425
+ variant="outline"
2426
+ className="shrink-0"
2427
+ onClick={() => void copyIngestHandoffValue(value, field)}
2428
+ >
2429
+ {copiedHandoffField === field ? (
2430
+ <IconChecks className="size-4" />
2431
+ ) : (
2432
+ <IconCopy className="size-4" />
2433
+ )}
2434
+ {copiedHandoffField === field
2435
+ ? t("sources.copied")
2436
+ : t("sources.copy")}
2437
+ </Button>
2438
+ </div>
2439
+ </div>
2440
+ ))}
2441
+ </div>
2442
+
2443
+ <div className="rounded-md border border-border bg-muted/25 p-3 text-sm leading-6 text-muted-foreground">
2444
+ {t("sources.ingestTokenSecurity")}
2445
+ </div>
2446
+
2447
+ <DialogFooter>
2448
+ <Button type="button" onClick={closeIngestHandoff}>
2449
+ {t("sources.ingestHandoffSaved")}
2450
+ </Button>
2451
+ </DialogFooter>
2452
+ </DialogContent>
2453
+ ) : null}
2454
+ </Dialog>
2455
+
2305
2456
  <Sheet open={setupOpen} onOpenChange={setSetupOpen}>
2306
2457
  <SheetContent className="w-full overflow-y-auto sm:max-w-xl">
2307
2458
  <SheetHeader>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ Chat runs now keep working through long source syncs and always end with a visible answer.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ Chat stays current in background tabs and keeps its latest tool visibly active without covering the first message.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ The sidebar footer now keeps Feedback and the collapse control on one compact row.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ Full-page chat keeps the active conversation when moving to and from the sidebar.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ Long chat titles now stay inside the sidebar, and recent chats expand from a compact five-item list.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ New Clips and webhook sources now show their secure connection details once, with copy controls and token rotation.
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ import { randomBytes } from "node:crypto";
4
+ import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const FUNCTIONS_DIR = path.join(ROOT, ".netlify", "functions-internal");
10
+ const SERVER_DIR = path.join(FUNCTIONS_DIR, "server");
11
+ export const SCHEDULED_NAME = "brain-queue-cron";
12
+ export const WORKER_NAME = "brain-queue-sweep-background";
13
+ export const ROUTE_PATH = "/api/brain/queue/run";
14
+ export const SCHEDULE = "* * * * *";
15
+
16
+ function ensureDir(dir: string) {
17
+ mkdirSync(dir, { recursive: true });
18
+ }
19
+
20
+ export function scheduledTriggerSource(token: string) {
21
+ return `const WORKER_PATH = "/.netlify/functions/${WORKER_NAME}";
22
+ const CRON_TOKEN = ${JSON.stringify(token)};
23
+
24
+ function siteOrigin(request) {
25
+ return new URL(request.url).origin;
26
+ }
27
+
28
+ export default async function handler(request) {
29
+ const response = await fetch(new URL(WORKER_PATH, siteOrigin(request)), {
30
+ method: "POST",
31
+ headers: { "content-type": "application/json", "x-agent-native-brain-cron": CRON_TOKEN },
32
+ body: JSON.stringify({ scheduled: true }),
33
+ });
34
+ if (!response.ok && response.status !== 202) {
35
+ console.error("[brain-queue-cron] Background sweep trigger failed:", response.status);
36
+ }
37
+ }
38
+
39
+ export const config = {
40
+ name: "brain queue cron trigger",
41
+ generator: "agent-native brain build",
42
+ schedule: ${JSON.stringify(SCHEDULE)},
43
+ };
44
+ `;
45
+ }
46
+
47
+ export function backgroundWorkerSource(token: string) {
48
+ return `globalThis.__AGENT_NATIVE_BRAIN_SCHEDULED_RUNTIME__ = true;
49
+ const CRON_TOKEN = ${JSON.stringify(token)};
50
+ const ROUTE_PATH = ${JSON.stringify(ROUTE_PATH)};
51
+ let cachedHandler;
52
+
53
+ function timingSafeEquals(a, b) {
54
+ if (a.length !== b.length) return false;
55
+ let diff = 0;
56
+ for (let i = 0; i < a.length; i += 1) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
57
+ return diff === 0;
58
+ }
59
+
60
+ export default async function handler(request, context) {
61
+ const token = request.headers.get("x-agent-native-brain-cron") || "";
62
+ if (!timingSafeEquals(token, CRON_TOKEN)) return new Response("Unauthorized", { status: 401 });
63
+ cachedHandler ??= (await import("./main.mjs")).default;
64
+ const url = new URL(request.url);
65
+ url.pathname = ROUTE_PATH;
66
+ url.search = "";
67
+ return cachedHandler(new Request(url, {
68
+ method: "POST",
69
+ headers: { "content-type": "application/json" },
70
+ body: JSON.stringify({ scheduled: true }),
71
+ }), context);
72
+ }
73
+
74
+ export const config = {
75
+ name: "brain queue background sweep",
76
+ generator: "agent-native brain build",
77
+ background: true,
78
+ nodeBundler: "none",
79
+ includedFiles: ["**"],
80
+ preferStatic: false,
81
+ };
82
+ `;
83
+ }
84
+
85
+ export function emitNetlifyBrainQueueCron(
86
+ options: {
87
+ functionsDir?: string;
88
+ serverDir?: string;
89
+ token?: string;
90
+ } = {},
91
+ ) {
92
+ const functionsDir = options.functionsDir ?? FUNCTIONS_DIR;
93
+ const serverDir = options.serverDir ?? SERVER_DIR;
94
+ if (!existsSync(serverDir)) {
95
+ throw new Error(
96
+ "Expected Nitro Netlify server output before emitting Brain cron functions.",
97
+ );
98
+ }
99
+ const token = options.token ?? randomBytes(32).toString("hex");
100
+ const scheduledDir = path.join(functionsDir, SCHEDULED_NAME);
101
+ const workerDir = path.join(functionsDir, WORKER_NAME);
102
+ rmSync(scheduledDir, { recursive: true, force: true });
103
+ rmSync(workerDir, { recursive: true, force: true });
104
+ ensureDir(scheduledDir);
105
+ cpSync(serverDir, workerDir, { recursive: true });
106
+ rmSync(path.join(workerDir, "server.mjs"), { force: true });
107
+ writeFileSync(
108
+ path.join(scheduledDir, `${SCHEDULED_NAME}.mjs`),
109
+ scheduledTriggerSource(token),
110
+ );
111
+ writeFileSync(
112
+ path.join(workerDir, `${WORKER_NAME}.mjs`),
113
+ backgroundWorkerSource(token),
114
+ );
115
+ }
116
+
117
+ if (
118
+ process.argv[1] &&
119
+ path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
120
+ ) {
121
+ emitNetlifyBrainQueueCron();
122
+ console.log(
123
+ "[brain-queue-cron] Emitted scheduled queue sweep and background worker.",
124
+ );
125
+ }
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs brain"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter brain build"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter brain build && pnpm exec tsx templates/brain/jobs/emit-netlify-brain-queue-cron.ts"
4
4
  publish = "templates/brain/dist"
5
5
  functions = "templates/brain/.netlify/functions-internal"
6
6
 
@@ -15,3 +15,7 @@ NPM_CONFIG_PRODUCTION = "false"
15
15
  # auto-continuation path can hand off before Netlify kills the function.
16
16
  [functions."*"]
17
17
  timeout = 75
18
+
19
+ [functions.brain-queue-sweep-background]
20
+ timeout = 900
21
+ memory = "2gb"
@@ -0,0 +1,50 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { writeFile } from "node:fs/promises";
3
+
4
+ import { getDbExec } from "@agent-native/core/db";
5
+
6
+ const tokenFile = process.env.BRAIN_CLIPS_TOKEN_FILE;
7
+ if (!tokenFile) throw new Error("BRAIN_CLIPS_TOKEN_FILE is required");
8
+
9
+ const db = getDbExec();
10
+ const { rows } = await db.execute({
11
+ sql: "SELECT id, source_key, config_json FROM brain_sources WHERE provider = ? AND title = ?",
12
+ args: ["clips", "Clips exports"],
13
+ });
14
+ if (rows.length !== 1) {
15
+ throw new Error(`Expected one Clips exports source, found ${rows.length}`);
16
+ }
17
+
18
+ const source = rows[0] as {
19
+ id: string;
20
+ source_key?: string | null;
21
+ config_json?: string | null;
22
+ };
23
+ const sourceKey = source.source_key?.trim() || "clips";
24
+ const token = `brain_${randomBytes(32).toString("base64url")}`;
25
+ const tokenHash = createHash("sha256").update(token).digest("hex");
26
+ const config = JSON.parse(source.config_json || "{}") as Record<
27
+ string,
28
+ unknown
29
+ >;
30
+ config.sourceKey = sourceKey;
31
+ config.ingestTokenHash = tokenHash;
32
+
33
+ await db.execute({
34
+ sql: "UPDATE brain_sources SET source_key = ?, ingest_token_hash = ?, config_json = ?, updated_at = ? WHERE id = ?",
35
+ args: [
36
+ sourceKey,
37
+ tokenHash,
38
+ JSON.stringify(config),
39
+ new Date().toISOString(),
40
+ source.id,
41
+ ],
42
+ });
43
+ await writeFile(tokenFile, token, {
44
+ encoding: "utf8",
45
+ flag: "wx",
46
+ mode: 0o600,
47
+ });
48
+ console.log(
49
+ JSON.stringify({ configured: true, sourceId: source.id, sourceKey }),
50
+ );
@@ -168,6 +168,10 @@ function preferredDeterministicSection(content: string): string {
168
168
  return content;
169
169
  }
170
170
 
171
+ function hasSummarySection(content: string): boolean {
172
+ return /(?:^|\n)\s*Summary\s*\n/i.test(content);
173
+ }
174
+
171
175
  function looksLikeRawTranscriptLine(line: string): boolean {
172
176
  return (
173
177
  /^\[[^\]\n]{1,80}\]\s*/.test(line) ||
@@ -208,6 +212,35 @@ function deterministicSanitizeContent(
208
212
  return retained.join("\n").slice(0, 80_000).trim();
209
213
  }
210
214
 
215
+ function deterministicMeetingOutageFallback(
216
+ input: CaptureSanitizationInput,
217
+ decision: BrainSensitivityDecision,
218
+ capturedAt: string,
219
+ ): string | null {
220
+ if (
221
+ decision.disposition !== "allowed" ||
222
+ input.kind !== "transcript" ||
223
+ !["granola", "clips"].includes(input.source.provider) ||
224
+ !shouldSanitizeCaptureBeforeStorage(input) ||
225
+ !hasSummarySection(decision.safeContent) ||
226
+ PERSONAL_SIGNAL.test(decision.safeContent) ||
227
+ RECRUITING_SIGNAL.test(decision.safeContent)
228
+ ) {
229
+ return null;
230
+ }
231
+
232
+ const content = deterministicSanitizeContent(
233
+ sanitizeSensitiveText(decision.safeContent),
234
+ { dropTranscriptLines: true },
235
+ );
236
+ if (content === DEFAULT_SANITIZATION_OUTPUT) return null;
237
+
238
+ return fallbackSensitivityDecision(content, capturedAt).disposition ===
239
+ "allowed"
240
+ ? content
241
+ : null;
242
+ }
243
+
211
244
  function safeTranscriptTitle(input: CaptureSanitizationInput): string {
212
245
  const date = input.capturedAt?.slice(0, 10);
213
246
  const providerLabel =
@@ -506,7 +539,9 @@ export async function sanitizeCaptureForStorage(
506
539
  deterministicQuarantineDecision(input.title, capturedAt) ??
507
540
  (titleDecision.disposition === "allowed" ? null : titleDecision) ??
508
541
  deterministicQuarantineDecision(input.content, capturedAt);
542
+ const sanitizationRequested = shouldSanitizeCaptureBeforeStorage(input);
509
543
  let fallbackReason: string | undefined;
544
+ let classifierOutageFallback = false;
510
545
  const classifierConfigured = Boolean(
511
546
  stringSetting(input.settings.privacyClassifierModel) &&
512
547
  stringSetting(input.settings.privacyClassifierEngine),
@@ -526,13 +561,35 @@ export async function sanitizeCaptureForStorage(
526
561
  }
527
562
  decision ??= fallbackSensitivityDecision(input.content, capturedAt);
528
563
  if (classifierFailed) {
529
- decision = {
530
- ...decision,
531
- disposition: "quarantined",
532
- confidenceBand: "uncertain",
533
- };
564
+ const deterministicMeetingContent = deterministicMeetingOutageFallback(
565
+ input,
566
+ decision,
567
+ capturedAt,
568
+ );
569
+ if (deterministicMeetingContent) {
570
+ classifierOutageFallback = true;
571
+ decision = {
572
+ ...decision,
573
+ safeContent: deterministicMeetingContent,
574
+ safeSegments: [
575
+ {
576
+ id: "deterministic-meeting-summary",
577
+ capturedAt,
578
+ text: deterministicMeetingContent,
579
+ reactionCount: 0,
580
+ },
581
+ ],
582
+ confidenceBand: "deterministic",
583
+ classifier: "deterministic",
584
+ };
585
+ } else {
586
+ decision = {
587
+ ...decision,
588
+ disposition: "quarantined",
589
+ confidenceBand: "uncertain",
590
+ };
591
+ }
534
592
  }
535
- const sanitizationRequested = shouldSanitizeCaptureBeforeStorage(input);
536
593
 
537
594
  const sanitizedContent =
538
595
  decision.disposition === "allowed"
@@ -575,6 +632,7 @@ export async function sanitizeCaptureForStorage(
575
632
  ? input.settings.privacyClassifierModel
576
633
  : undefined,
577
634
  fallbackReason,
635
+ classifierOutageFallback: classifierOutageFallback || undefined,
578
636
  disposition: decision.disposition,
579
637
  categories: decision.categories,
580
638
  confidenceBand: decision.confidenceBand,
@@ -10,6 +10,8 @@ import { and, desc, like, or } from "drizzle-orm";
10
10
  import actionsRegistry from "../../.generated/actions-registry.js";
11
11
  import { tryAnswerBrainA2AQuestion } from "../lib/a2a-fallback.js";
12
12
 
13
+ const BRAIN_BACKGROUND_RUN_SOFT_TIMEOUT_MS = 13 * 60_000;
14
+
13
15
  const INITIAL_TOOL_NAMES = [
14
16
  "get-brain-settings",
15
17
  "ask-brain",
@@ -39,6 +41,8 @@ export default createAgentChatPlugin({
39
41
  appId: "brain",
40
42
  actions: loadActionsFromStaticRegistry(actionsRegistry),
41
43
  initialToolNames: INITIAL_TOOL_NAMES,
44
+ durableBackgroundRuns: true,
45
+ runSoftTimeoutMs: BRAIN_BACKGROUND_RUN_SOFT_TIMEOUT_MS,
42
46
  resolveOrgId: async (event) => (await getOrgContext(event)).orgId,
43
47
  codeExecution: { production: "sandboxed" },
44
48
  systemPrompt: `You are the Brain institutional-knowledge agent.
@@ -2,6 +2,7 @@ import registerBrainDistillationQueueJob from "../jobs/distillation-queue.js";
2
2
  import registerBrainSourceSyncJob from "../jobs/sync-sources.js";
3
3
 
4
4
  export default () => {
5
+ if (process.env.NETLIFY === "true") return;
5
6
  registerBrainSourceSyncJob();
6
7
  registerBrainDistillationQueueJob();
7
8
  };