@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
@@ -18,6 +18,7 @@ import { and, asc, eq, gt, gte, isNull, lte, or, sql } from "drizzle-orm";
18
18
  import { z } from "zod";
19
19
 
20
20
  import { getDb, schema } from "../server/db/index.js";
21
+ import { writeBrainExportState } from "../server/lib/brain-export-state.js";
21
22
  import {
22
23
  getActiveOrganizationId,
23
24
  getCurrentOwnerEmail,
@@ -232,6 +233,11 @@ async function buildPayload(
232
233
  .orderBy(asc(schema.recordingTags.tag)),
233
234
  ]);
234
235
 
236
+ const summary = meeting?.summaryMd?.trim();
237
+ const captureText = summary
238
+ ? `Summary\n${summary}\n\nTranscript\n${transcriptText}`
239
+ : transcriptText;
240
+
235
241
  return {
236
242
  sourceKey: "clips",
237
243
  externalId: `clips:recording:${recording.id}`,
@@ -246,7 +252,7 @@ async function buildPayload(
246
252
  meeting?.scheduledStart ||
247
253
  recording.createdAt ||
248
254
  new Date().toISOString(),
249
- transcript: transcriptText,
255
+ transcript: captureText,
250
256
  segments,
251
257
  sourceUrl: resolveSourceUrl(recording.id),
252
258
  tags: tags.map((row) => row.tag).filter(Boolean),
@@ -346,14 +352,17 @@ async function exportRecording(
346
352
  recording: Recording,
347
353
  transcript: RecordingTranscript,
348
354
  destination: BrainDestination,
355
+ attempt = 1,
349
356
  ): Promise<BrainExportResult> {
350
357
  const payload = await buildPayload(recording, transcript);
351
358
  if (!payload) {
352
- return {
359
+ const result: BrainExportResult = {
353
360
  recordingId: recording.id,
354
361
  status: "skipped",
355
362
  reason: "empty-transcript",
356
363
  };
364
+ await persistBrainExportResult(result, attempt);
365
+ return result;
357
366
  }
358
367
 
359
368
  try {
@@ -370,7 +379,9 @@ async function exportRecording(
370
379
  },
371
380
  { maxRedirects: 0 },
372
381
  );
373
- return interpretBrainResponse(recording.id, response);
382
+ const result = await interpretBrainResponse(recording.id, response);
383
+ await persistBrainExportResult(result, attempt);
384
+ return result;
374
385
  } catch (err) {
375
386
  const reason =
376
387
  (err as Error)?.name === "TimeoutError" ||
@@ -382,10 +393,65 @@ async function exportRecording(
382
393
  reason,
383
394
  error: (err as Error)?.message ?? String(err),
384
395
  });
385
- return { recordingId: recording.id, status: "failed", reason };
396
+ const result: BrainExportResult = {
397
+ recordingId: recording.id,
398
+ status: "failed",
399
+ reason,
400
+ };
401
+ await persistBrainExportResult(result, attempt);
402
+ return result;
386
403
  }
387
404
  }
388
405
 
406
+ async function persistBrainExportResult(
407
+ result: BrainExportResult,
408
+ attempts: number,
409
+ ): Promise<void> {
410
+ const updatedAt = new Date().toISOString();
411
+ await writeBrainExportState({
412
+ recordingId: result.recordingId,
413
+ status: result.status,
414
+ attempts,
415
+ updatedAt,
416
+ ...(result.status === "exported" ? { captureId: result.captureId } : {}),
417
+ ...(result.status === "quarantined"
418
+ ? {
419
+ sensitivityReceiptId: result.sensitivityReceiptId,
420
+ sensitivityDisposition: result.sensitivityDisposition,
421
+ }
422
+ : {}),
423
+ ...(result.status === "failed"
424
+ ? {
425
+ reason: result.reason,
426
+ nextAttemptAt: new Date(
427
+ Date.now() +
428
+ Math.min(15 * 60_000, 30_000 * 2 ** Math.max(0, attempts - 1)),
429
+ ).toISOString(),
430
+ }
431
+ : result.status === "skipped"
432
+ ? { reason: result.reason }
433
+ : {}),
434
+ });
435
+ }
436
+
437
+ async function persistRetryableBrainExportFailure(
438
+ recordingId: string,
439
+ reason: string,
440
+ attempts: number,
441
+ ): Promise<void> {
442
+ await writeBrainExportState({
443
+ recordingId,
444
+ status: "failed",
445
+ attempts,
446
+ reason,
447
+ updatedAt: new Date().toISOString(),
448
+ nextAttemptAt: new Date(
449
+ Date.now() +
450
+ Math.min(15 * 60_000, 30_000 * 2 ** Math.max(0, attempts - 1)),
451
+ ).toISOString(),
452
+ });
453
+ }
454
+
389
455
  async function mapWithConcurrency<T, R>(
390
456
  values: T[],
391
457
  concurrency: number,
@@ -535,6 +601,7 @@ export default defineAction({
535
601
  .min(1)
536
602
  .max(8)
537
603
  .default(DEFAULT_CONCURRENCY),
604
+ retryAttempt: z.coerce.number().int().min(1).max(8).optional(),
538
605
  cursor: z
539
606
  .string()
540
607
  .min(1)
@@ -552,11 +619,17 @@ export default defineAction({
552
619
  );
553
620
  const destinationResult = await resolveBrainDestination();
554
621
  if (!destinationResult.destination) {
555
- return {
622
+ const result: BrainExportResult = {
556
623
  recordingId: args.recordingId,
557
624
  status: "skipped" as const,
558
625
  reason: destinationResult.reason,
559
626
  };
627
+ await persistRetryableBrainExportFailure(
628
+ result.recordingId,
629
+ result.reason,
630
+ 0,
631
+ );
632
+ return result;
560
633
  }
561
634
  const [transcript] = await getDb()
562
635
  .select()
@@ -564,16 +637,19 @@ export default defineAction({
564
637
  .where(eq(schema.recordingTranscripts.recordingId, args.recordingId))
565
638
  .limit(1);
566
639
  if (!transcript) {
567
- return {
640
+ const result: BrainExportResult = {
568
641
  recordingId: args.recordingId,
569
642
  status: "skipped" as const,
570
643
  reason: "empty-transcript",
571
644
  };
645
+ await persistBrainExportResult(result, args.retryAttempt ?? 0);
646
+ return result;
572
647
  }
573
648
  return exportRecording(
574
649
  access.resource as Recording,
575
650
  transcript,
576
651
  destinationResult.destination,
652
+ args.retryAttempt ?? 1,
577
653
  );
578
654
  }
579
655
 
@@ -53,6 +53,7 @@ import { and, eq } from "drizzle-orm";
53
53
  import { z } from "zod";
54
54
 
55
55
  import { getDb, schema } from "../server/db/index.js";
56
+ import { writeBrainExportState } from "../server/lib/brain-export-state.js";
56
57
  import { dispatchPostFinalizeJob } from "../server/lib/post-finalize-dispatch.js";
57
58
  import {
58
59
  getCurrentOwnerEmail,
@@ -66,7 +67,6 @@ import {
66
67
  parseTranscriptSegments,
67
68
  } from "../shared/transcript-segments.js";
68
69
  import cleanupTranscript from "./cleanup-transcript.js";
69
- import exportToBrain from "./export-to-brain.js";
70
70
  import { loadAgentsMdContext } from "./lib/agents-md-context.js";
71
71
  import {
72
72
  AudioOnlyExtractionError,
@@ -309,15 +309,26 @@ function scheduleAutoTranscriptRetry({
309
309
  });
310
310
  }
311
311
 
312
- function queueBrainExport(recordingId: string): void {
313
- void Promise.resolve(exportToBrain.run({ recordingId })).catch(
314
- (err: unknown) => {
315
- console.warn(
316
- `[clips] Brain export skipped for ${recordingId}:`,
317
- (err as Error)?.message ?? String(err),
318
- );
319
- },
320
- );
312
+ async function queueBrainExport(recordingId: string): Promise<void> {
313
+ await writeBrainExportState({
314
+ recordingId,
315
+ status: "pending",
316
+ attempts: 0,
317
+ updatedAt: new Date().toISOString(),
318
+ nextAttemptAt: new Date(Date.now() + 60_000).toISOString(),
319
+ });
320
+ try {
321
+ await dispatchPostFinalizeJob({
322
+ recordingId,
323
+ kind: "brain-export",
324
+ requireAccepted: true,
325
+ });
326
+ } catch (error) {
327
+ console.warn(
328
+ `[clips] Brain export dispatch failed for ${recordingId}:`,
329
+ error instanceof Error ? error.message : String(error),
330
+ );
331
+ }
321
332
  }
322
333
 
323
334
  function verboseTranscriptErrors(): boolean {
@@ -542,7 +553,7 @@ export async function importLoomTranscriptForRecording({
542
553
  now,
543
554
  });
544
555
  await writeAppState("refresh-signal", { ts: Date.now() });
545
- queueBrainExport(recordingId);
556
+ await queueBrainExport(recordingId);
546
557
  return {
547
558
  recordingId,
548
559
  status: "ready" as const,
@@ -955,7 +966,7 @@ async function completeReadyTranscript({
955
966
  // (`transcript-cleanup-${recordingId}`) before its next 2s tick lands —
956
967
  // otherwise the "Cleaning up…" badge can lag for one full poll interval.
957
968
  await writeAppState("refresh-signal", { ts: Date.now() });
958
- queueBrainExport(recordingId);
969
+ await queueBrainExport(recordingId);
959
970
 
960
971
  return {
961
972
  recordingId,
@@ -1385,7 +1396,7 @@ const requestTranscriptAction = defineAction({
1385
1396
  now,
1386
1397
  });
1387
1398
  await writeAppState("refresh-signal", { ts: Date.now() });
1388
- queueBrainExport(args.recordingId);
1399
+ await queueBrainExport(args.recordingId);
1389
1400
  await clearBuilderCreditsExhausted();
1390
1401
 
1391
1402
  // Re-read title fresh — `rec.title` was fetched before the 30+ s
@@ -1647,7 +1658,7 @@ const requestTranscriptAction = defineAction({
1647
1658
  });
1648
1659
 
1649
1660
  await writeAppState("refresh-signal", { ts: Date.now() });
1650
- queueBrainExport(args.recordingId);
1661
+ await queueBrainExport(args.recordingId);
1651
1662
 
1652
1663
  // Generate transcript-backed metadata without replacing a human title or
1653
1664
  // description. The title action keeps any local heuristic replaceable
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: improved
3
+ ---
4
+
5
+ Ready clip transcripts now keep an export receipt and retry Brain delivery after temporary failures.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-22
4
+ ---
5
+
6
+ German umlauts and other non-ASCII characters now paste correctly from macOS dictation
@@ -1854,7 +1854,21 @@ mod tests {
1854
1854
 
1855
1855
  #[cfg(target_os = "macos")]
1856
1856
  mod macos_only {
1857
- use super::super::chunk_graphemes_by_utf16_units;
1857
+ use super::super::{chunk_graphemes_by_utf16_units, utf8_pasteboard_command};
1858
+ use std::ffi::OsStr;
1859
+
1860
+ #[test]
1861
+ fn pasteboard_commands_force_utf8_for_gui_launches() {
1862
+ let command = utf8_pasteboard_command("pbcopy");
1863
+ let env_value = |key: &str| {
1864
+ command
1865
+ .get_envs()
1866
+ .find(|(name, _)| *name == OsStr::new(key))
1867
+ .and_then(|(_, value)| value)
1868
+ };
1869
+ assert_eq!(env_value("LANG"), Some(OsStr::new("en_US.UTF-8")));
1870
+ assert_eq!(env_value("LC_ALL"), Some(OsStr::new("en_US.UTF-8")));
1871
+ }
1858
1872
 
1859
1873
  #[test]
1860
1874
  fn never_splits_a_zwj_family_emoji_across_chunks() {
@@ -1892,9 +1906,18 @@ mod tests {
1892
1906
  }
1893
1907
  }
1894
1908
 
1909
+ #[cfg(target_os = "macos")]
1910
+ fn utf8_pasteboard_command(program: &str) -> Command {
1911
+ let mut command = Command::new(program);
1912
+ command
1913
+ .env("LANG", "en_US.UTF-8")
1914
+ .env("LC_ALL", "en_US.UTF-8");
1915
+ command
1916
+ }
1917
+
1895
1918
  #[cfg(target_os = "macos")]
1896
1919
  fn write_clipboard(text: &str) -> Result<(), String> {
1897
- let mut child = Command::new("pbcopy")
1920
+ let mut child = utf8_pasteboard_command("pbcopy")
1898
1921
  .stdin(Stdio::piped())
1899
1922
  .spawn()
1900
1923
  .map_err(|e| format!("pbcopy spawn: {e}"))?;
@@ -1926,7 +1949,7 @@ fn write_clipboard(_text: &str) -> Result<(), String> {
1926
1949
  /// content on the clipboard are not preserved by the later restore.
1927
1950
  #[cfg(target_os = "macos")]
1928
1951
  fn read_clipboard() -> Option<String> {
1929
- let out = Command::new("pbpaste").output().ok()?;
1952
+ let out = utf8_pasteboard_command("pbpaste").output().ok()?;
1930
1953
  if !out.status.success() {
1931
1954
  return None;
1932
1955
  }
@@ -0,0 +1,118 @@
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 = "clips-brain-export-cron";
12
+ export const WORKER_NAME = "clips-brain-export-sweep-background";
13
+ export const ROUTE_PATH = "/api/clips/brain-export/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
+ export default async function handler(request) {
25
+ const response = await fetch(new URL(WORKER_PATH, new URL(request.url).origin), {
26
+ method: "POST",
27
+ headers: { "content-type": "application/json", "x-agent-native-clips-brain-export-cron": CRON_TOKEN },
28
+ body: JSON.stringify({ scheduled: true }),
29
+ });
30
+ if (!response.ok && response.status !== 202) console.error("[clips-brain-export-cron] Background sweep trigger failed:", response.status);
31
+ }
32
+
33
+ export const config = {
34
+ name: "Clips Brain export cron trigger",
35
+ generator: "agent-native clips build",
36
+ schedule: ${JSON.stringify(SCHEDULE)},
37
+ };
38
+ `;
39
+ }
40
+
41
+ export function backgroundWorkerSource(token: string) {
42
+ return `globalThis.__AGENT_NATIVE_CLIPS_BRAIN_EXPORT_SCHEDULED_RUNTIME__ = true;
43
+ const CRON_TOKEN = ${JSON.stringify(token)};
44
+ const ROUTE_PATH = ${JSON.stringify(ROUTE_PATH)};
45
+ let cachedHandler;
46
+
47
+ function timingSafeEquals(a, b) {
48
+ if (a.length !== b.length) return false;
49
+ let diff = 0;
50
+ for (let i = 0; i < a.length; i += 1) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
51
+ return diff === 0;
52
+ }
53
+
54
+ export default async function handler(request, context) {
55
+ const token = request.headers.get("x-agent-native-clips-brain-export-cron") || "";
56
+ if (!timingSafeEquals(token, CRON_TOKEN)) return new Response("Unauthorized", { status: 401 });
57
+ cachedHandler ??= (await import("./main.mjs")).default;
58
+ const url = new URL(request.url);
59
+ url.pathname = ROUTE_PATH;
60
+ url.search = "";
61
+ return cachedHandler(new Request(url, {
62
+ method: "POST",
63
+ headers: { "content-type": "application/json" },
64
+ body: JSON.stringify({ scheduled: true }),
65
+ }), context);
66
+ }
67
+
68
+ export const config = {
69
+ name: "Clips Brain export background sweep",
70
+ generator: "agent-native clips build",
71
+ background: true,
72
+ nodeBundler: "none",
73
+ includedFiles: ["**"],
74
+ preferStatic: false,
75
+ };
76
+ `;
77
+ }
78
+
79
+ export function emitNetlifyBrainExportCron(
80
+ options: {
81
+ functionsDir?: string;
82
+ serverDir?: string;
83
+ token?: string;
84
+ } = {},
85
+ ) {
86
+ const functionsDir = options.functionsDir ?? FUNCTIONS_DIR;
87
+ const serverDir = options.serverDir ?? SERVER_DIR;
88
+ if (!existsSync(serverDir))
89
+ throw new Error(
90
+ "Expected Nitro Netlify server output before emitting Clips Brain export functions.",
91
+ );
92
+ const token = options.token ?? randomBytes(32).toString("hex");
93
+ const scheduledDir = path.join(functionsDir, SCHEDULED_NAME);
94
+ const workerDir = path.join(functionsDir, WORKER_NAME);
95
+ rmSync(scheduledDir, { recursive: true, force: true });
96
+ rmSync(workerDir, { recursive: true, force: true });
97
+ ensureDir(scheduledDir);
98
+ cpSync(serverDir, workerDir, { recursive: true });
99
+ rmSync(path.join(workerDir, "server.mjs"), { force: true });
100
+ writeFileSync(
101
+ path.join(scheduledDir, `${SCHEDULED_NAME}.mjs`),
102
+ scheduledTriggerSource(token),
103
+ );
104
+ writeFileSync(
105
+ path.join(workerDir, `${WORKER_NAME}.mjs`),
106
+ backgroundWorkerSource(token),
107
+ );
108
+ }
109
+
110
+ if (
111
+ process.argv[1] &&
112
+ path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
113
+ ) {
114
+ emitNetlifyBrainExportCron();
115
+ console.log(
116
+ "[clips-brain-export-cron] Emitted scheduled export sweep and background worker.",
117
+ );
118
+ }
@@ -1,6 +1,6 @@
1
1
  [build]
2
2
  ignore = "node ./scripts/netlify-ignore-build.mjs clips"
3
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build"
3
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build && pnpm exec tsx templates/clips/jobs/emit-netlify-brain-export-cron.ts"
4
4
  publish = "templates/clips/dist"
5
5
  functions = "templates/clips/.netlify/functions-internal"
6
6
 
@@ -25,3 +25,6 @@ AGENT_NATIVE_SERVERLESS_FFMPEG_ARCH = "x64"
25
25
  # auto-continuation path can hand off before Netlify kills the function.
26
26
  [functions."*"]
27
27
  timeout = 75
28
+
29
+ [functions.clips-brain-export-sweep-background]
30
+ timeout = 120
@@ -0,0 +1,92 @@
1
+ import { readFile } from "node:fs/promises";
2
+
3
+ import { writeAppState } from "@agent-native/core/application-state";
4
+ import { saveCredential } from "@agent-native/core/credentials";
5
+ import { getDbExec } from "@agent-native/core/db";
6
+ import { runWithRequestContext } from "@agent-native/core/server";
7
+
8
+ const tokenFile = process.env.BRAIN_CLIPS_TOKEN_FILE;
9
+ if (!tokenFile) throw new Error("BRAIN_CLIPS_TOKEN_FILE is required");
10
+ const token = (await readFile(tokenFile, "utf8")).trim();
11
+ if (!token.startsWith("brain_") || token.length < 32) {
12
+ throw new Error("The Brain ingest token is invalid");
13
+ }
14
+
15
+ const db = getDbExec();
16
+ const { rows } = await db.execute({
17
+ sql: `SELECT id, owner_email, org_id, created_at
18
+ FROM recordings
19
+ WHERE status = ? AND trashed_at IS NULL AND created_at >= ?
20
+ AND EXISTS (
21
+ SELECT 1 FROM recording_transcripts
22
+ WHERE recording_transcripts.recording_id = recordings.id
23
+ AND recording_transcripts.status = ?
24
+ AND LENGTH(TRIM(COALESCE(recording_transcripts.full_text, ''))) > 0
25
+ )
26
+ ORDER BY created_at DESC
27
+ LIMIT ?`,
28
+ args: [
29
+ "ready",
30
+ new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(),
31
+ "ready",
32
+ 20,
33
+ ],
34
+ });
35
+ if (rows.length === 0)
36
+ throw new Error("No recent ready Clips recordings found");
37
+
38
+ const recordings = rows as Array<{
39
+ id: string;
40
+ owner_email: string;
41
+ org_id?: string | null;
42
+ }>;
43
+ const ownerEmail = recordings[0]!.owner_email;
44
+ const orgId = recordings[0]!.org_id?.trim();
45
+ if (!ownerEmail || !orgId) {
46
+ throw new Error(
47
+ "Recent Clips recordings are missing owner or organization scope",
48
+ );
49
+ }
50
+ if (
51
+ recordings.some(
52
+ (recording) =>
53
+ recording.owner_email !== ownerEmail ||
54
+ recording.org_id?.trim() !== orgId,
55
+ )
56
+ ) {
57
+ throw new Error(
58
+ "Recent Clips recordings span multiple owners or organizations",
59
+ );
60
+ }
61
+
62
+ await saveCredential(
63
+ "BRAIN_INGEST_URL",
64
+ "https://brain.agent-native.com/api/_agent-native/brain/ingest",
65
+ { userEmail: ownerEmail, orgId, scope: "org" },
66
+ );
67
+ await saveCredential("BRAIN_INGEST_TOKEN", token, {
68
+ userEmail: ownerEmail,
69
+ orgId,
70
+ scope: "org",
71
+ });
72
+
73
+ await runWithRequestContext({ userEmail: ownerEmail, orgId }, async () => {
74
+ const now = new Date().toISOString();
75
+ for (const recording of recordings) {
76
+ await writeAppState(`clips-brain-export-${recording.id}`, {
77
+ recordingId: recording.id,
78
+ status: "pending",
79
+ attempts: 0,
80
+ updatedAt: now,
81
+ nextAttemptAt: now,
82
+ });
83
+ }
84
+ });
85
+
86
+ console.log(
87
+ JSON.stringify({
88
+ configured: true,
89
+ queued: recordings.length,
90
+ organizationScoped: true,
91
+ }),
92
+ );