@agent-native/core 0.112.0 → 0.113.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 (490) hide show
  1. package/corpus/README.md +4 -2
  2. package/corpus/core/CHANGELOG.md +22 -0
  3. package/corpus/core/docs/AGENTS.md +7 -0
  4. package/corpus/core/docs/content/integrations.mdx +63 -2
  5. package/corpus/core/docs/design/provider-integration-authority.md +117 -0
  6. package/corpus/core/package.json +6 -1
  7. package/corpus/core/scripts/materialize-source-corpus.mjs +9 -2
  8. package/corpus/core/src/cli/atomic-json-file.ts +324 -0
  9. package/corpus/core/src/cli/claude-code-participant.ts +515 -0
  10. package/corpus/core/src/cli/code-agent-executor.ts +18 -10
  11. package/corpus/core/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
  12. package/corpus/core/src/cli/code-agent-runs.ts +55 -62
  13. package/corpus/core/src/cli/codex-cli-participant.ts +496 -0
  14. package/corpus/core/src/cli/multi-frontier-runs.ts +1246 -135
  15. package/corpus/core/src/client/chat/ChatHistoryList.tsx +7 -404
  16. package/corpus/core/src/integrations/index.ts +10 -0
  17. package/corpus/core/src/integrations/slack-oauth.ts +1 -0
  18. package/corpus/core/src/integrations/webhook-delivery.ts +55 -0
  19. package/corpus/core/src/notifications/channels.ts +4 -10
  20. package/corpus/core/src/provider-api/actions/custom-provider-registration.ts +197 -0
  21. package/corpus/core/src/provider-api/actions/delete-staged-dataset.ts +3 -42
  22. package/corpus/core/src/provider-api/actions/list-staged-datasets.ts +3 -45
  23. package/corpus/core/src/provider-api/actions/provider-api-audit.ts +88 -0
  24. package/corpus/core/src/provider-api/actions/provider-api.ts +460 -0
  25. package/corpus/core/src/provider-api/actions/query-staged-dataset.ts +3 -140
  26. package/corpus/core/src/provider-api/actions/staged-datasets.ts +359 -0
  27. package/corpus/core/src/provider-api/gong.ts +108 -0
  28. package/corpus/core/src/scripts/docs/source-search.ts +2 -1
  29. package/corpus/core/src/server/google-oauth-credentials.ts +80 -3
  30. package/corpus/core/src/server/index.ts +6 -0
  31. package/corpus/core/src/styles/chat-history-list.css +1 -313
  32. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  33. package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  34. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  35. package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  36. package/corpus/core/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  37. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  38. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  39. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  40. package/corpus/core/src/templates/default/AGENTS.md +25 -18
  41. package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  42. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  43. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  44. package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  45. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  46. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  47. package/corpus/core/src/templates/headless/AGENTS.md +7 -0
  48. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  49. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  50. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  51. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  54. package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -0
  55. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  56. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -12
  57. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  58. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +11 -2
  59. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  60. package/corpus/templates/analytics/actions/delete-staged-dataset.ts +3 -23
  61. package/corpus/templates/analytics/actions/list-staged-datasets.ts +3 -27
  62. package/corpus/templates/analytics/actions/provider-api-catalog.ts +16 -19
  63. package/corpus/templates/analytics/actions/provider-api-docs.ts +3 -5
  64. package/corpus/templates/analytics/actions/provider-api-request.ts +162 -185
  65. package/corpus/templates/analytics/actions/query-staged-dataset.ts +3 -47
  66. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +52 -161
  67. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -12
  68. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  69. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +11 -2
  70. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  71. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +63 -160
  72. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -12
  73. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  74. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +11 -2
  75. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  76. package/corpus/templates/brain/actions/_schemas.ts +13 -7
  77. package/corpus/templates/brain/actions/delete-staged-dataset.ts +3 -24
  78. package/corpus/templates/brain/actions/list-staged-datasets.ts +3 -28
  79. package/corpus/templates/brain/actions/provider-api-catalog.ts +5 -11
  80. package/corpus/templates/brain/actions/provider-api-docs.ts +3 -5
  81. package/corpus/templates/brain/actions/provider-api-request.ts +3 -37
  82. package/corpus/templates/brain/actions/query-staged-dataset.ts +3 -48
  83. package/corpus/templates/brain/actions/update-source.ts +5 -1
  84. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +48 -154
  85. package/corpus/templates/brain/app/lib/brain.ts +55 -0
  86. package/corpus/templates/brain/app/lib/ingest-handoff.ts +35 -0
  87. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-can-now-join-configured-public-channels-befo.md +6 -0
  88. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-continue-across-the-configured-number-of.md +6 -0
  89. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-start-with-a-safe-four-week-window-and-r.md +6 -0
  90. package/corpus/templates/brain/server/lib/connectors.ts +53 -2
  91. package/corpus/templates/brain/server/lib/slack-channel-membership.ts +44 -0
  92. package/corpus/templates/brain/shared/slack-source-config.ts +60 -0
  93. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -12
  94. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  95. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +11 -2
  96. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  97. package/corpus/templates/calendar/actions/delete-staged-dataset.ts +3 -24
  98. package/corpus/templates/calendar/actions/list-staged-datasets.ts +3 -28
  99. package/corpus/templates/calendar/actions/provider-api-catalog.ts +5 -11
  100. package/corpus/templates/calendar/actions/provider-api-docs.ts +3 -5
  101. package/corpus/templates/calendar/actions/provider-api-request.ts +3 -37
  102. package/corpus/templates/calendar/actions/query-staged-dataset.ts +3 -48
  103. package/corpus/templates/calendar/server/handlers/gong.ts +6 -61
  104. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -8
  105. package/corpus/templates/calendar/server/lib/google-calendar.ts +7 -45
  106. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  107. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  108. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  109. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  110. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  111. package/corpus/templates/chat/AGENTS.md +5 -2
  112. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  113. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -12
  114. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  115. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +11 -2
  116. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  117. package/corpus/templates/clips/AGENTS.md +11 -0
  118. package/corpus/templates/clips/actions/export-to-brain.ts +505 -171
  119. package/corpus/templates/clips/changelog/2026-07-19-ready-clips-transcripts-can-be-backfilled-to-brain-for-a-bou.md +6 -0
  120. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -1
  121. package/corpus/templates/clips/server/lib/google-calendar-client.ts +12 -22
  122. package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +9 -6
  123. package/corpus/templates/clips/server/lib/slack-oauth.ts +12 -47
  124. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +2 -6
  125. package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +4 -4
  126. package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -12
  127. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  128. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +11 -2
  129. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  130. package/corpus/templates/content/actions/delete-staged-dataset.ts +3 -24
  131. package/corpus/templates/content/actions/list-staged-datasets.ts +3 -28
  132. package/corpus/templates/content/actions/provider-api-catalog.ts +5 -11
  133. package/corpus/templates/content/actions/provider-api-docs.ts +3 -5
  134. package/corpus/templates/content/actions/provider-api-request.ts +3 -37
  135. package/corpus/templates/content/actions/query-staged-dataset.ts +3 -48
  136. package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -12
  137. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  138. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +11 -2
  139. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  140. package/corpus/templates/design/actions/delete-staged-dataset.ts +4 -23
  141. package/corpus/templates/design/actions/list-staged-datasets.ts +4 -27
  142. package/corpus/templates/design/actions/provider-api-catalog.ts +15 -18
  143. package/corpus/templates/design/actions/provider-api-docs.ts +57 -48
  144. package/corpus/templates/design/actions/provider-api-request.ts +140 -164
  145. package/corpus/templates/design/actions/query-staged-dataset.ts +4 -47
  146. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -12
  147. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  148. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +11 -2
  149. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  150. package/corpus/templates/dispatch/actions/delete-staged-dataset.ts +4 -23
  151. package/corpus/templates/dispatch/actions/list-staged-datasets.ts +4 -30
  152. package/corpus/templates/dispatch/actions/provider-api-catalog.ts +15 -18
  153. package/corpus/templates/dispatch/actions/provider-api-docs.ts +57 -48
  154. package/corpus/templates/dispatch/actions/provider-api-request.ts +138 -163
  155. package/corpus/templates/dispatch/actions/query-staged-dataset.ts +4 -47
  156. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -12
  157. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  158. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +11 -2
  159. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  160. package/corpus/templates/forms/server/lib/integrations.ts +23 -22
  161. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  162. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  163. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -12
  164. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  165. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +11 -2
  166. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  167. package/corpus/templates/mail/actions/delete-staged-dataset.ts +4 -23
  168. package/corpus/templates/mail/actions/list-staged-datasets.ts +4 -27
  169. package/corpus/templates/mail/actions/provider-api-catalog.ts +15 -18
  170. package/corpus/templates/mail/actions/provider-api-docs.ts +57 -48
  171. package/corpus/templates/mail/actions/provider-api-request.ts +146 -173
  172. package/corpus/templates/mail/actions/query-staged-dataset.ts +4 -47
  173. package/corpus/templates/mail/server/handlers/gong.ts +6 -61
  174. package/corpus/templates/mail/server/lib/google-auth.ts +10 -13
  175. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -12
  176. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  177. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +11 -2
  178. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  179. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +47 -156
  180. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -12
  181. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  182. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +11 -2
  183. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  184. package/corpus/templates/slides/actions/delete-staged-dataset.ts +4 -23
  185. package/corpus/templates/slides/actions/list-staged-datasets.ts +4 -27
  186. package/corpus/templates/slides/actions/provider-api-catalog.ts +15 -18
  187. package/corpus/templates/slides/actions/provider-api-docs.ts +57 -48
  188. package/corpus/templates/slides/actions/provider-api-request.ts +145 -172
  189. package/corpus/templates/slides/actions/query-staged-dataset.ts +4 -47
  190. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +11 -36
  191. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -12
  192. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +10 -0
  193. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  194. package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +11 -2
  195. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  196. package/corpus/toolkit/CHANGELOG.md +125 -0
  197. package/corpus/toolkit/README.md +60 -0
  198. package/corpus/toolkit/export-snapshot.json +35 -0
  199. package/corpus/toolkit/migration-manifest.json +4 -0
  200. package/corpus/toolkit/package.json +281 -0
  201. package/corpus/toolkit/scripts/finalize-build.mjs +55 -0
  202. package/corpus/toolkit/src/app-shell/header-actions.tsx +84 -0
  203. package/corpus/toolkit/src/app-shell/index.ts +1 -0
  204. package/corpus/toolkit/src/chat-history/ChatHistoryList.tsx +469 -0
  205. package/corpus/toolkit/src/chat-history/index.ts +7 -0
  206. package/corpus/toolkit/src/chat-history.css +314 -0
  207. package/corpus/toolkit/src/collab-ui/AgentPresenceChip.tsx +69 -0
  208. package/corpus/toolkit/src/collab-ui/LiveCursorOverlay.tsx +241 -0
  209. package/corpus/toolkit/src/collab-ui/PresenceBar.tsx +346 -0
  210. package/corpus/toolkit/src/collab-ui/RecentEditHighlights.tsx +225 -0
  211. package/corpus/toolkit/src/collab-ui/RemoteSelectionRings.tsx +227 -0
  212. package/corpus/toolkit/src/collab-ui/agent-identity.ts +16 -0
  213. package/corpus/toolkit/src/collab-ui/index.ts +39 -0
  214. package/corpus/toolkit/src/collab-ui/lead-client.ts +32 -0
  215. package/corpus/toolkit/src/collab-ui/types.ts +96 -0
  216. package/corpus/toolkit/src/composer/AgentComposerFrame.tsx +59 -0
  217. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +983 -0
  218. package/corpus/toolkit/src/composer/MentionPopover.tsx +412 -0
  219. package/corpus/toolkit/src/composer/PastedTextChip.tsx +150 -0
  220. package/corpus/toolkit/src/composer/PromptComposer.tsx +718 -0
  221. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +856 -0
  222. package/corpus/toolkit/src/composer/TiptapComposer.tsx +2936 -0
  223. package/corpus/toolkit/src/composer/VoiceButton.tsx +255 -0
  224. package/corpus/toolkit/src/composer/asset-picker-url.ts +80 -0
  225. package/corpus/toolkit/src/composer/attachment-accept.ts +71 -0
  226. package/corpus/toolkit/src/composer/draft-key.ts +7 -0
  227. package/corpus/toolkit/src/composer/extensions/FileReference.tsx +55 -0
  228. package/corpus/toolkit/src/composer/extensions/MentionReference.tsx +93 -0
  229. package/corpus/toolkit/src/composer/extensions/SkillReference.tsx +56 -0
  230. package/corpus/toolkit/src/composer/index.ts +79 -0
  231. package/corpus/toolkit/src/composer/pasted-text.ts +160 -0
  232. package/corpus/toolkit/src/composer/prompt-attachments.ts +93 -0
  233. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +64 -0
  234. package/corpus/toolkit/src/composer/realtime-voice-transcript.ts +134 -0
  235. package/corpus/toolkit/src/composer/runtime-adapters.tsx +343 -0
  236. package/corpus/toolkit/src/composer/types.ts +66 -0
  237. package/corpus/toolkit/src/composer/use-file-search.ts +64 -0
  238. package/corpus/toolkit/src/composer/use-mention-search.ts +90 -0
  239. package/corpus/toolkit/src/composer/use-skills.ts +45 -0
  240. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +2117 -0
  241. package/corpus/toolkit/src/composer/useVoiceDictation.ts +1191 -0
  242. package/corpus/toolkit/src/context-ui/ContextMeter.tsx +101 -0
  243. package/corpus/toolkit/src/context-ui/ContextSegmentRow.tsx +184 -0
  244. package/corpus/toolkit/src/context-ui/ContextTreemap.tsx +125 -0
  245. package/corpus/toolkit/src/context-ui/ContextXRayPanel.tsx +377 -0
  246. package/corpus/toolkit/src/context-ui/format.ts +42 -0
  247. package/corpus/toolkit/src/context-ui/index.ts +15 -0
  248. package/corpus/toolkit/src/context-ui/types.ts +43 -0
  249. package/corpus/toolkit/src/design-tweaks/index.ts +1 -0
  250. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +1084 -0
  251. package/corpus/toolkit/src/editor/BubbleToolbar.tsx +253 -0
  252. package/corpus/toolkit/src/editor/CodeBlockNode.tsx +254 -0
  253. package/corpus/toolkit/src/editor/DragHandle.ts +1437 -0
  254. package/corpus/toolkit/src/editor/ImageExtension.ts +306 -0
  255. package/corpus/toolkit/src/editor/RegistryBlockContext.tsx +107 -0
  256. package/corpus/toolkit/src/editor/RegistryBlockNode.tsx +652 -0
  257. package/corpus/toolkit/src/editor/RichMarkdownEditor.tsx +81 -0
  258. package/corpus/toolkit/src/editor/RunId.ts +61 -0
  259. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +321 -0
  260. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +310 -0
  261. package/corpus/toolkit/src/editor/extensions.ts +402 -0
  262. package/corpus/toolkit/src/editor/gfmDoc.ts +92 -0
  263. package/corpus/toolkit/src/editor/index.ts +93 -0
  264. package/corpus/toolkit/src/editor/registrySlashCommands.ts +131 -0
  265. package/corpus/toolkit/src/editor/surgical-apply.ts +192 -0
  266. package/corpus/toolkit/src/editor/useCollabReconcile.ts +589 -0
  267. package/corpus/toolkit/src/editor.css +510 -0
  268. package/corpus/toolkit/src/hooks/index.ts +2 -0
  269. package/corpus/toolkit/src/hooks/use-mobile.tsx +21 -0
  270. package/corpus/toolkit/src/hooks/use-toast.ts +188 -0
  271. package/corpus/toolkit/src/index.ts +8 -0
  272. package/corpus/toolkit/src/onboarding/index.ts +1 -0
  273. package/corpus/toolkit/src/provider.tsx +47 -0
  274. package/corpus/toolkit/src/sharing/VisibilityBadge.tsx +37 -0
  275. package/corpus/toolkit/src/sharing/index.ts +4 -0
  276. package/corpus/toolkit/src/styles.css +21 -0
  277. package/corpus/toolkit/src/ui/accordion.tsx +56 -0
  278. package/corpus/toolkit/src/ui/alert-dialog.tsx +141 -0
  279. package/corpus/toolkit/src/ui/alert.tsx +59 -0
  280. package/corpus/toolkit/src/ui/aspect-ratio.tsx +5 -0
  281. package/corpus/toolkit/src/ui/avatar.tsx +48 -0
  282. package/corpus/toolkit/src/ui/badge.tsx +37 -0
  283. package/corpus/toolkit/src/ui/breadcrumb.tsx +115 -0
  284. package/corpus/toolkit/src/ui/button.tsx +78 -0
  285. package/corpus/toolkit/src/ui/calendar.tsx +214 -0
  286. package/corpus/toolkit/src/ui/card.tsx +86 -0
  287. package/corpus/toolkit/src/ui/carousel.tsx +260 -0
  288. package/corpus/toolkit/src/ui/chart.tsx +375 -0
  289. package/corpus/toolkit/src/ui/checkbox.tsx +28 -0
  290. package/corpus/toolkit/src/ui/collapsible.tsx +9 -0
  291. package/corpus/toolkit/src/ui/command.tsx +174 -0
  292. package/corpus/toolkit/src/ui/context-menu.tsx +202 -0
  293. package/corpus/toolkit/src/ui/date-picker.tsx +64 -0
  294. package/corpus/toolkit/src/ui/dialog.tsx +146 -0
  295. package/corpus/toolkit/src/ui/drawer.tsx +126 -0
  296. package/corpus/toolkit/src/ui/dropdown-menu.tsx +218 -0
  297. package/corpus/toolkit/src/ui/form.tsx +177 -0
  298. package/corpus/toolkit/src/ui/hover-card.tsx +27 -0
  299. package/corpus/toolkit/src/ui/index.ts +52 -0
  300. package/corpus/toolkit/src/ui/input-otp.tsx +69 -0
  301. package/corpus/toolkit/src/ui/input.tsx +22 -0
  302. package/corpus/toolkit/src/ui/label.tsx +24 -0
  303. package/corpus/toolkit/src/ui/logo.tsx +36 -0
  304. package/corpus/toolkit/src/ui/menubar.tsx +235 -0
  305. package/corpus/toolkit/src/ui/navigation-menu.tsx +128 -0
  306. package/corpus/toolkit/src/ui/pagination.tsx +121 -0
  307. package/corpus/toolkit/src/ui/popover.tsx +44 -0
  308. package/corpus/toolkit/src/ui/progress.tsx +26 -0
  309. package/corpus/toolkit/src/ui/radio-group.tsx +42 -0
  310. package/corpus/toolkit/src/ui/resizable.tsx +43 -0
  311. package/corpus/toolkit/src/ui/scroll-area.tsx +46 -0
  312. package/corpus/toolkit/src/ui/select.tsx +158 -0
  313. package/corpus/toolkit/src/ui/separator.tsx +29 -0
  314. package/corpus/toolkit/src/ui/sheet.tsx +160 -0
  315. package/corpus/toolkit/src/ui/sidebar.tsx +777 -0
  316. package/corpus/toolkit/src/ui/skeleton.tsx +18 -0
  317. package/corpus/toolkit/src/ui/slider.tsx +26 -0
  318. package/corpus/toolkit/src/ui/sonner.tsx +58 -0
  319. package/corpus/toolkit/src/ui/spinner.tsx +17 -0
  320. package/corpus/toolkit/src/ui/switch.tsx +27 -0
  321. package/corpus/toolkit/src/ui/table.tsx +117 -0
  322. package/corpus/toolkit/src/ui/tabs.tsx +53 -0
  323. package/corpus/toolkit/src/ui/textarea.tsx +23 -0
  324. package/corpus/toolkit/src/ui/toast.tsx +127 -0
  325. package/corpus/toolkit/src/ui/toaster.tsx +33 -0
  326. package/corpus/toolkit/src/ui/toggle-group.tsx +59 -0
  327. package/corpus/toolkit/src/ui/toggle.tsx +43 -0
  328. package/corpus/toolkit/src/ui/tooltip.tsx +53 -0
  329. package/corpus/toolkit/src/ui/use-toast.ts +3 -0
  330. package/corpus/toolkit/src/utils.ts +8 -0
  331. package/corpus/toolkit/tsconfig.json +23 -0
  332. package/dist/cli/atomic-json-file.d.ts +30 -0
  333. package/dist/cli/atomic-json-file.d.ts.map +1 -0
  334. package/dist/cli/atomic-json-file.js +240 -0
  335. package/dist/cli/atomic-json-file.js.map +1 -0
  336. package/dist/cli/claude-code-participant.d.ts +77 -0
  337. package/dist/cli/claude-code-participant.d.ts.map +1 -0
  338. package/dist/cli/claude-code-participant.js +355 -0
  339. package/dist/cli/claude-code-participant.js.map +1 -0
  340. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  341. package/dist/cli/code-agent-executor.js +18 -10
  342. package/dist/cli/code-agent-executor.js.map +1 -1
  343. package/dist/cli/code-agent-runs.concurrent-worker.d.ts +2 -0
  344. package/dist/cli/code-agent-runs.concurrent-worker.d.ts.map +1 -0
  345. package/dist/cli/code-agent-runs.concurrent-worker.js +32 -0
  346. package/dist/cli/code-agent-runs.concurrent-worker.js.map +1 -0
  347. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  348. package/dist/cli/code-agent-runs.js +43 -56
  349. package/dist/cli/code-agent-runs.js.map +1 -1
  350. package/dist/cli/codex-cli-participant.d.ts +77 -0
  351. package/dist/cli/codex-cli-participant.d.ts.map +1 -0
  352. package/dist/cli/codex-cli-participant.js +354 -0
  353. package/dist/cli/codex-cli-participant.js.map +1 -0
  354. package/dist/cli/multi-frontier-runs.d.ts +115 -0
  355. package/dist/cli/multi-frontier-runs.d.ts.map +1 -1
  356. package/dist/cli/multi-frontier-runs.js +831 -113
  357. package/dist/cli/multi-frontier-runs.js.map +1 -1
  358. package/dist/client/chat/ChatHistoryList.d.ts +1 -87
  359. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -1
  360. package/dist/client/chat/ChatHistoryList.js +1 -82
  361. package/dist/client/chat/ChatHistoryList.js.map +1 -1
  362. package/dist/integrations/index.d.ts +2 -1
  363. package/dist/integrations/index.d.ts.map +1 -1
  364. package/dist/integrations/index.js +2 -1
  365. package/dist/integrations/index.js.map +1 -1
  366. package/dist/integrations/slack-manifest.d.ts +1 -1
  367. package/dist/integrations/slack-oauth.d.ts +1 -1
  368. package/dist/integrations/slack-oauth.d.ts.map +1 -1
  369. package/dist/integrations/slack-oauth.js +1 -0
  370. package/dist/integrations/slack-oauth.js.map +1 -1
  371. package/dist/integrations/webhook-delivery.d.ts +23 -0
  372. package/dist/integrations/webhook-delivery.d.ts.map +1 -0
  373. package/dist/integrations/webhook-delivery.js +32 -0
  374. package/dist/integrations/webhook-delivery.js.map +1 -0
  375. package/dist/notifications/channels.d.ts.map +1 -1
  376. package/dist/notifications/channels.js +4 -9
  377. package/dist/notifications/channels.js.map +1 -1
  378. package/dist/notifications/routes.d.ts +3 -3
  379. package/dist/provider-api/actions/custom-provider-registration.d.ts +136 -0
  380. package/dist/provider-api/actions/custom-provider-registration.d.ts.map +1 -0
  381. package/dist/provider-api/actions/custom-provider-registration.js +154 -0
  382. package/dist/provider-api/actions/custom-provider-registration.js.map +1 -0
  383. package/dist/provider-api/actions/delete-staged-dataset.d.ts +1 -1
  384. package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -1
  385. package/dist/provider-api/actions/delete-staged-dataset.js +3 -34
  386. package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -1
  387. package/dist/provider-api/actions/list-staged-datasets.d.ts +2 -3
  388. package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -1
  389. package/dist/provider-api/actions/list-staged-datasets.js +3 -40
  390. package/dist/provider-api/actions/list-staged-datasets.js.map +1 -1
  391. package/dist/provider-api/actions/provider-api-audit.d.ts +7 -0
  392. package/dist/provider-api/actions/provider-api-audit.d.ts.map +1 -0
  393. package/dist/provider-api/actions/provider-api-audit.js +74 -0
  394. package/dist/provider-api/actions/provider-api-audit.js.map +1 -0
  395. package/dist/provider-api/actions/provider-api.d.ts +370 -0
  396. package/dist/provider-api/actions/provider-api.d.ts.map +1 -0
  397. package/dist/provider-api/actions/provider-api.js +280 -0
  398. package/dist/provider-api/actions/provider-api.js.map +1 -0
  399. package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
  400. package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
  401. package/dist/provider-api/actions/query-staged-dataset.js +3 -116
  402. package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
  403. package/dist/provider-api/actions/staged-datasets.d.ts +190 -0
  404. package/dist/provider-api/actions/staged-datasets.d.ts.map +1 -0
  405. package/dist/provider-api/actions/staged-datasets.js +210 -0
  406. package/dist/provider-api/actions/staged-datasets.js.map +1 -0
  407. package/dist/provider-api/gong.d.ts +44 -0
  408. package/dist/provider-api/gong.d.ts.map +1 -0
  409. package/dist/provider-api/gong.js +56 -0
  410. package/dist/provider-api/gong.js.map +1 -0
  411. package/dist/scripts/docs/source-search.d.ts +2 -1
  412. package/dist/scripts/docs/source-search.d.ts.map +1 -1
  413. package/dist/scripts/docs/source-search.js +2 -1
  414. package/dist/scripts/docs/source-search.js.map +1 -1
  415. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  416. package/dist/server/google-oauth-credentials.d.ts +35 -0
  417. package/dist/server/google-oauth-credentials.d.ts.map +1 -1
  418. package/dist/server/google-oauth-credentials.js +48 -2
  419. package/dist/server/google-oauth-credentials.js.map +1 -1
  420. package/dist/server/index.d.ts +1 -1
  421. package/dist/server/index.d.ts.map +1 -1
  422. package/dist/server/index.js +1 -1
  423. package/dist/server/index.js.map +1 -1
  424. package/dist/styles/chat-history-list.css +1 -313
  425. package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  426. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  427. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  428. package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  429. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  430. package/dist/templates/chat/AGENTS.md +5 -2
  431. package/dist/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  432. package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -12
  433. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  434. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  435. package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  436. package/dist/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  437. package/dist/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  438. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  439. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  440. package/dist/templates/default/AGENTS.md +25 -18
  441. package/dist/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  442. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  443. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  444. package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  445. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  446. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  447. package/dist/templates/headless/AGENTS.md +7 -0
  448. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  449. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  450. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  451. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  452. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  453. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  454. package/dist/templates/workspace-core/AGENTS.md +4 -0
  455. package/dist/templates/workspace-root/AGENTS.md +4 -0
  456. package/docs/AGENTS.md +7 -0
  457. package/docs/content/integrations.mdx +63 -2
  458. package/docs/design/provider-integration-authority.md +117 -0
  459. package/package.json +7 -2
  460. package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  461. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  462. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  463. package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  464. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  465. package/src/templates/chat/AGENTS.md +5 -2
  466. package/src/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  467. package/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  468. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  469. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  470. package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  471. package/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  472. package/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  473. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  474. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  475. package/src/templates/default/AGENTS.md +25 -18
  476. package/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  477. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  478. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  479. package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  480. package/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  481. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  482. package/src/templates/headless/AGENTS.md +7 -0
  483. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  484. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  485. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  486. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  487. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  488. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  489. package/src/templates/workspace-core/AGENTS.md +4 -0
  490. package/src/templates/workspace-root/AGENTS.md +4 -0
@@ -1,27 +1,40 @@
1
1
  /**
2
- * Best-effort export of a Clips recording transcript to Brain.
2
+ * Export Clips transcripts to Brain's signed ingest endpoint.
3
3
  *
4
4
  * Configure the destination with scoped credentials:
5
5
  * - BRAIN_INGEST_URL: Brain generic ingest endpoint
6
- * - BRAIN_INGEST_TOKEN: optional bearer token
7
- *
8
- * This action never reads Brain data directly. It validates access to the
9
- * Clips recording, builds a RawCapturePayload, and posts it to Brain.
6
+ * - BRAIN_INGEST_TOKEN: per-source bearer token
10
7
  */
11
8
 
12
9
  import { defineAction } from "@agent-native/core";
13
10
  import { resolveCredential } from "@agent-native/core/credentials";
14
- import { getCredentialContext } from "@agent-native/core/server/request-context";
15
- import { assertAccess } from "@agent-native/core/sharing";
16
- import { asc, eq } from "drizzle-orm";
11
+ import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
12
+ import {
13
+ getAppProductionUrl,
14
+ getCredentialContext,
15
+ } from "@agent-native/core/server";
16
+ import { accessFilter, assertAccess } from "@agent-native/core/sharing";
17
+ import { and, asc, eq, gt, gte, isNull, lte, or, sql } from "drizzle-orm";
17
18
  import { z } from "zod";
18
19
 
19
20
  import { getDb, schema } from "../server/db/index.js";
21
+ import {
22
+ getActiveOrganizationId,
23
+ getCurrentOwnerEmail,
24
+ ownerEmailMatches,
25
+ } from "../server/lib/recordings.js";
20
26
  import {
21
27
  normalizeTranscriptSegments,
22
28
  parseTranscriptSegments,
23
29
  } from "../shared/transcript-segments.js";
24
30
 
31
+ const DEFAULT_LOOKBACK_DAYS = 28;
32
+ const DEFAULT_BACKFILL_LIMIT = 100;
33
+ const DEFAULT_CONCURRENCY = 4;
34
+
35
+ type Recording = typeof schema.recordings.$inferSelect;
36
+ type RecordingTranscript = typeof schema.recordingTranscripts.$inferSelect;
37
+
25
38
  type RawCapturePayload = {
26
39
  sourceKey: "clips";
27
40
  externalId: string;
@@ -38,11 +51,62 @@ type RawCapturePayload = {
38
51
  endMs: number;
39
52
  text: string;
40
53
  }>;
41
- sourceUrl: string | null;
54
+ sourceUrl: string;
42
55
  tags: string[];
43
56
  raw: Record<string, unknown>;
44
57
  };
45
58
 
59
+ type BrainDestination = {
60
+ ingestUrl: string;
61
+ ingestToken: string;
62
+ };
63
+
64
+ type BackfillCursor = {
65
+ version: 1;
66
+ lookbackDays: number;
67
+ since: string;
68
+ until: string;
69
+ afterCreatedAt: string;
70
+ afterId: string;
71
+ };
72
+
73
+ type BrainExportResult =
74
+ | {
75
+ recordingId: string;
76
+ status: "exported";
77
+ captureId: string;
78
+ }
79
+ | {
80
+ recordingId: string;
81
+ status: "quarantined";
82
+ sensitivityReceiptId: string;
83
+ sensitivityDisposition: string;
84
+ }
85
+ | {
86
+ recordingId: string;
87
+ status: "skipped" | "failed";
88
+ reason: string;
89
+ };
90
+
91
+ const brainIngestResponseSchema = z
92
+ .object({
93
+ ok: z.boolean(),
94
+ capture: z
95
+ .object({
96
+ id: z.string().min(1),
97
+ })
98
+ .passthrough()
99
+ .nullable(),
100
+ sensitivityReceipt: z
101
+ .object({
102
+ id: z.string().min(1),
103
+ disposition: z.string().min(1),
104
+ })
105
+ .passthrough()
106
+ .optional(),
107
+ })
108
+ .passthrough();
109
+
46
110
  function safeJsonParse<T>(value: string | null | undefined, fallback: T): T {
47
111
  if (!value) return fallback;
48
112
  try {
@@ -65,198 +129,468 @@ function normalizeEndpoint(value: string | undefined): string | null {
65
129
  }
66
130
 
67
131
  function resolveSourceUrl(id: string) {
68
- return `/r/${id}`;
132
+ return new URL(
133
+ `/r/${encodeURIComponent(id)}`,
134
+ getAppProductionUrl(),
135
+ ).toString();
69
136
  }
70
137
 
71
- export default defineAction({
72
- description:
73
- "Best-effort export of a Clips recording transcript and meeting metadata to a configured Brain generic ingest endpoint.",
74
- schema: z.object({
75
- recordingId: z.string().describe("Recording ID to export to Brain"),
76
- }),
77
- run: async (args) => {
78
- const access = await assertAccess("recording", args.recordingId, "editor");
79
- const credentialContext = getCredentialContext();
80
- if (!credentialContext) {
81
- return {
82
- recordingId: args.recordingId,
83
- status: "skipped" as const,
84
- reason: "missing-request-context",
85
- };
86
- }
138
+ function encodeBackfillCursor(cursor: BackfillCursor) {
139
+ return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
140
+ }
87
141
 
88
- const ingestUrl = normalizeEndpoint(
89
- await resolveCredential("BRAIN_INGEST_URL", credentialContext),
90
- );
91
- if (!ingestUrl) {
92
- return {
93
- recordingId: args.recordingId,
94
- status: "skipped" as const,
95
- reason: "not-configured",
96
- };
142
+ function decodeBackfillCursor(value: string): BackfillCursor {
143
+ try {
144
+ const parsed = JSON.parse(
145
+ Buffer.from(value, "base64url").toString("utf8"),
146
+ ) as Partial<BackfillCursor>;
147
+ if (
148
+ parsed.version !== 1 ||
149
+ typeof parsed.lookbackDays !== "number" ||
150
+ !Number.isInteger(parsed.lookbackDays) ||
151
+ parsed.lookbackDays < 1 ||
152
+ parsed.lookbackDays > 90 ||
153
+ typeof parsed.since !== "string" ||
154
+ !Number.isFinite(Date.parse(parsed.since)) ||
155
+ typeof parsed.until !== "string" ||
156
+ !Number.isFinite(Date.parse(parsed.until)) ||
157
+ typeof parsed.afterCreatedAt !== "string" ||
158
+ !Number.isFinite(Date.parse(parsed.afterCreatedAt)) ||
159
+ typeof parsed.afterId !== "string" ||
160
+ !parsed.afterId
161
+ ) {
162
+ throw new Error("invalid cursor fields");
97
163
  }
98
- const ingestToken = await resolveCredential(
99
- "BRAIN_INGEST_TOKEN",
100
- credentialContext,
164
+ return parsed as BackfillCursor;
165
+ } catch {
166
+ throw new Error(
167
+ "Invalid Clips Brain backfill cursor. Start a new backfill without cursor.",
101
168
  );
169
+ }
170
+ }
102
171
 
103
- const db = getDb();
104
- const recording = access.resource as typeof schema.recordings.$inferSelect;
105
- const [transcript] = await db
106
- .select()
107
- .from(schema.recordingTranscripts)
108
- .where(eq(schema.recordingTranscripts.recordingId, args.recordingId))
109
- .limit(1);
172
+ async function resolveBrainDestination(): Promise<
173
+ | { destination: BrainDestination; reason?: never }
174
+ | { destination?: never; reason: string }
175
+ > {
176
+ const credentialContext = getCredentialContext();
177
+ if (!credentialContext) return { reason: "missing-request-context" };
110
178
 
111
- const segments = normalizeTranscriptSegments({
112
- segments: parseTranscriptSegments(transcript?.segmentsJson),
113
- fullText: transcript?.fullText,
114
- durationMs: recording.durationMs,
115
- });
116
- const transcriptText =
117
- transcript?.fullText?.trim() ||
118
- segments
119
- .map((segment) => segment.text.trim())
120
- .join(" ")
121
- .trim();
122
- if (!transcriptText) {
123
- return {
124
- recordingId: args.recordingId,
125
- status: "skipped" as const,
126
- reason: "empty-transcript",
127
- };
128
- }
179
+ const ingestUrl = normalizeEndpoint(
180
+ await resolveCredential("BRAIN_INGEST_URL", credentialContext),
181
+ );
182
+ if (!ingestUrl) return { reason: "missing-ingest-url" };
183
+
184
+ const ingestToken = (
185
+ await resolveCredential("BRAIN_INGEST_TOKEN", credentialContext)
186
+ )?.trim();
187
+ if (!ingestToken) return { reason: "missing-ingest-token" };
188
+
189
+ return { destination: { ingestUrl, ingestToken } };
190
+ }
191
+
192
+ async function buildPayload(
193
+ recording: Recording,
194
+ transcript: RecordingTranscript,
195
+ ): Promise<RawCapturePayload | null> {
196
+ const segments = normalizeTranscriptSegments({
197
+ segments: parseTranscriptSegments(transcript.segmentsJson),
198
+ fullText: transcript.fullText,
199
+ durationMs: recording.durationMs,
200
+ });
201
+ const transcriptText =
202
+ transcript.fullText?.trim() ||
203
+ segments
204
+ .map((segment) => segment.text.trim())
205
+ .join(" ")
206
+ .trim();
207
+ if (!transcriptText) return null;
129
208
 
130
- const [meeting] = await db
131
- .select()
132
- .from(schema.meetings)
133
- .where(eq(schema.meetings.recordingId, args.recordingId))
134
- .limit(1);
135
- const participants = meeting
136
- ? await db
209
+ const db = getDb();
210
+ const [meeting] = await db
211
+ .select()
212
+ .from(schema.meetings)
213
+ .where(
214
+ and(
215
+ eq(schema.meetings.recordingId, recording.id),
216
+ accessFilter(schema.meetings, schema.meetingShares),
217
+ ),
218
+ )
219
+ .limit(1);
220
+ const [participants, tags] = await Promise.all([
221
+ meeting
222
+ ? db
137
223
  .select()
138
224
  .from(schema.meetingParticipants)
139
225
  .where(eq(schema.meetingParticipants.meetingId, meeting.id))
140
226
  .orderBy(asc(schema.meetingParticipants.createdAt))
141
- : [];
142
- const tags = await db
227
+ : Promise.resolve([]),
228
+ db
143
229
  .select({ tag: schema.recordingTags.tag })
144
230
  .from(schema.recordingTags)
145
- .where(eq(schema.recordingTags.recordingId, args.recordingId))
146
- .orderBy(asc(schema.recordingTags.tag));
231
+ .where(eq(schema.recordingTags.recordingId, recording.id))
232
+ .orderBy(asc(schema.recordingTags.tag)),
233
+ ]);
147
234
 
148
- const payload: RawCapturePayload = {
149
- sourceKey: "clips",
150
- externalId: `clips:recording:${recording.id}`,
151
- title: meeting?.title || recording.title || "Untitled recording",
152
- participants: participants.map((participant) => ({
153
- email: participant.email || undefined,
154
- name: participant.name || undefined,
155
- role: participant.isOrganizer ? "organizer" : "participant",
156
- })),
157
- occurredAt:
158
- meeting?.actualStart ||
159
- meeting?.scheduledStart ||
160
- recording.createdAt ||
161
- new Date().toISOString(),
162
- transcript: transcriptText,
163
- segments,
164
- sourceUrl: resolveSourceUrl(recording.id),
165
- tags: tags.map((row) => row.tag).filter(Boolean),
166
- raw: {
167
- recording: {
168
- id: recording.id,
169
- organizationId: recording.organizationId,
170
- title: recording.title,
171
- description: recording.description,
172
- durationMs: recording.durationMs,
173
- createdAt: recording.createdAt,
174
- updatedAt: recording.updatedAt,
175
- sourceAppName: recording.sourceAppName,
176
- sourceWindowTitle: recording.sourceWindowTitle,
177
- spaceIds: safeJsonParse<string[]>(recording.spaceIds, []),
178
- chapters: safeJsonParse<Array<Record<string, unknown>>>(
179
- recording.chaptersJson,
180
- [],
181
- ),
182
- },
183
- meeting: meeting
184
- ? {
185
- id: meeting.id,
186
- title: meeting.title,
187
- platform: meeting.platform,
188
- source: meeting.source,
189
- scheduledStart: meeting.scheduledStart,
190
- scheduledEnd: meeting.scheduledEnd,
191
- actualStart: meeting.actualStart,
192
- actualEnd: meeting.actualEnd,
193
- joinUrl: meeting.joinUrl,
194
- calendarEventId: meeting.calendarEventId,
195
- summaryMd: meeting.summaryMd,
196
- bullets: safeJsonParse<Array<Record<string, unknown>>>(
197
- meeting.bulletsJson,
198
- [],
199
- ),
200
- actionItems: safeJsonParse<Array<Record<string, unknown>>>(
201
- meeting.actionItemsJson,
202
- [],
203
- ),
204
- }
205
- : null,
206
- transcript: {
207
- status: transcript?.status ?? null,
208
- language: transcript?.language ?? null,
209
- updatedAt: transcript?.updatedAt ?? null,
210
- },
235
+ return {
236
+ sourceKey: "clips",
237
+ externalId: `clips:recording:${recording.id}`,
238
+ title: meeting?.title || recording.title || "Untitled recording",
239
+ participants: participants.map((participant) => ({
240
+ email: participant.email || undefined,
241
+ name: participant.name || undefined,
242
+ role: participant.isOrganizer ? "organizer" : "participant",
243
+ })),
244
+ occurredAt:
245
+ meeting?.actualStart ||
246
+ meeting?.scheduledStart ||
247
+ recording.createdAt ||
248
+ new Date().toISOString(),
249
+ transcript: transcriptText,
250
+ segments,
251
+ sourceUrl: resolveSourceUrl(recording.id),
252
+ tags: tags.map((row) => row.tag).filter(Boolean),
253
+ raw: {
254
+ recording: {
255
+ id: recording.id,
256
+ organizationId: recording.organizationId,
257
+ title: recording.title,
258
+ description: recording.description,
259
+ durationMs: recording.durationMs,
260
+ createdAt: recording.createdAt,
261
+ updatedAt: recording.updatedAt,
262
+ sourceAppName: recording.sourceAppName,
263
+ sourceWindowTitle: recording.sourceWindowTitle,
264
+ spaceIds: safeJsonParse<string[]>(recording.spaceIds, []),
265
+ chapters: safeJsonParse<Array<Record<string, unknown>>>(
266
+ recording.chaptersJson,
267
+ [],
268
+ ),
269
+ },
270
+ meeting: meeting
271
+ ? {
272
+ id: meeting.id,
273
+ title: meeting.title,
274
+ platform: meeting.platform,
275
+ source: meeting.source,
276
+ scheduledStart: meeting.scheduledStart,
277
+ scheduledEnd: meeting.scheduledEnd,
278
+ actualStart: meeting.actualStart,
279
+ actualEnd: meeting.actualEnd,
280
+ joinUrl: meeting.joinUrl,
281
+ calendarEventId: meeting.calendarEventId,
282
+ summaryMd: meeting.summaryMd,
283
+ bullets: safeJsonParse<Array<Record<string, unknown>>>(
284
+ meeting.bulletsJson,
285
+ [],
286
+ ),
287
+ actionItems: safeJsonParse<Array<Record<string, unknown>>>(
288
+ meeting.actionItemsJson,
289
+ [],
290
+ ),
291
+ }
292
+ : null,
293
+ transcript: {
294
+ status: transcript.status,
295
+ language: transcript.language,
296
+ updatedAt: transcript.updatedAt,
211
297
  },
298
+ },
299
+ };
300
+ }
301
+
302
+ export async function interpretBrainResponse(
303
+ recordingId: string,
304
+ response: Response,
305
+ ): Promise<BrainExportResult> {
306
+ if (!response.ok) {
307
+ return {
308
+ recordingId,
309
+ status: "failed",
310
+ reason: `brain-ingest-http-${response.status}`,
212
311
  };
312
+ }
313
+
314
+ const body = await response.json().catch(() => null);
315
+ const parsed = brainIngestResponseSchema.safeParse(body);
316
+ if (!parsed.success || !parsed.data.ok) {
317
+ return {
318
+ recordingId,
319
+ status: "failed",
320
+ reason: "brain-ingest-invalid-response",
321
+ };
322
+ }
323
+ if (parsed.data.capture) {
324
+ return {
325
+ recordingId,
326
+ status: "exported",
327
+ captureId: parsed.data.capture.id,
328
+ };
329
+ }
330
+ if (parsed.data.sensitivityReceipt) {
331
+ return {
332
+ recordingId,
333
+ status: "quarantined",
334
+ sensitivityReceiptId: parsed.data.sensitivityReceipt.id,
335
+ sensitivityDisposition: parsed.data.sensitivityReceipt.disposition,
336
+ };
337
+ }
338
+ return {
339
+ recordingId,
340
+ status: "failed",
341
+ reason: "brain-ingest-empty-response",
342
+ };
343
+ }
213
344
 
214
- const controller = new AbortController();
215
- const timeout = setTimeout(() => controller.abort(), 10_000);
216
- try {
217
- const response = await fetch(ingestUrl, {
345
+ async function exportRecording(
346
+ recording: Recording,
347
+ transcript: RecordingTranscript,
348
+ destination: BrainDestination,
349
+ ): Promise<BrainExportResult> {
350
+ const payload = await buildPayload(recording, transcript);
351
+ if (!payload) {
352
+ return {
353
+ recordingId: recording.id,
354
+ status: "skipped",
355
+ reason: "empty-transcript",
356
+ };
357
+ }
358
+
359
+ try {
360
+ const response = await ssrfSafeFetch(
361
+ destination.ingestUrl,
362
+ {
218
363
  method: "POST",
219
364
  headers: {
220
365
  "content-type": "application/json",
221
- ...(ingestToken ? { authorization: `Bearer ${ingestToken}` } : {}),
366
+ authorization: `Bearer ${destination.ingestToken}`,
222
367
  },
223
368
  body: JSON.stringify(payload),
224
- signal: controller.signal,
225
- });
226
- if (!response.ok) {
227
- console.warn("[clips] Brain export failed", {
369
+ signal: AbortSignal.timeout(10_000),
370
+ },
371
+ { maxRedirects: 0 },
372
+ );
373
+ return interpretBrainResponse(recording.id, response);
374
+ } catch (err) {
375
+ const reason =
376
+ (err as Error)?.name === "TimeoutError" ||
377
+ (err as Error)?.name === "AbortError"
378
+ ? "brain-ingest-timeout"
379
+ : "brain-ingest-request-failed";
380
+ console.warn("[clips] Brain export failed", {
381
+ recordingId: recording.id,
382
+ reason,
383
+ error: (err as Error)?.message ?? String(err),
384
+ });
385
+ return { recordingId: recording.id, status: "failed", reason };
386
+ }
387
+ }
388
+
389
+ async function mapWithConcurrency<T, R>(
390
+ values: T[],
391
+ concurrency: number,
392
+ map: (value: T) => Promise<R>,
393
+ ): Promise<R[]> {
394
+ const results = new Array<R>(values.length);
395
+ let nextIndex = 0;
396
+ const workers = Array.from(
397
+ { length: Math.min(concurrency, values.length) },
398
+ async () => {
399
+ while (nextIndex < values.length) {
400
+ const index = nextIndex;
401
+ nextIndex += 1;
402
+ results[index] = await map(values[index]!);
403
+ }
404
+ },
405
+ );
406
+ await Promise.all(workers);
407
+ return results;
408
+ }
409
+
410
+ async function backfillRecordings(
411
+ destination: BrainDestination,
412
+ options: {
413
+ lookbackDays: number;
414
+ limit: number;
415
+ concurrency: number;
416
+ cursor?: string;
417
+ },
418
+ ) {
419
+ const ownerEmail = getCurrentOwnerEmail();
420
+ const organizationId = await getActiveOrganizationId();
421
+ if (!organizationId) {
422
+ throw new Error(
423
+ "Select an organization before backfilling Clips to Brain.",
424
+ );
425
+ }
426
+
427
+ const cursor = options.cursor ? decodeBackfillCursor(options.cursor) : null;
428
+ const lookbackDays = cursor?.lookbackDays ?? options.lookbackDays;
429
+ const until = cursor?.until ?? new Date().toISOString();
430
+ const since =
431
+ cursor?.since ??
432
+ new Date(
433
+ Date.parse(until) - lookbackDays * 24 * 60 * 60 * 1000,
434
+ ).toISOString();
435
+ const continuation = cursor
436
+ ? or(
437
+ gt(schema.recordings.createdAt, cursor.afterCreatedAt),
438
+ and(
439
+ eq(schema.recordings.createdAt, cursor.afterCreatedAt),
440
+ gt(schema.recordings.id, cursor.afterId),
441
+ ),
442
+ )
443
+ : undefined;
444
+ const rows = await getDb()
445
+ .select({
446
+ recording: schema.recordings,
447
+ transcript: schema.recordingTranscripts,
448
+ })
449
+ .from(schema.recordings)
450
+ .innerJoin(
451
+ schema.recordingTranscripts,
452
+ eq(schema.recordingTranscripts.recordingId, schema.recordings.id),
453
+ )
454
+ .where(
455
+ and(
456
+ accessFilter(schema.recordings, schema.recordingShares),
457
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
458
+ eq(schema.recordings.organizationId, organizationId),
459
+ isNull(schema.recordings.trashedAt),
460
+ gte(schema.recordings.createdAt, since),
461
+ lte(schema.recordings.createdAt, until),
462
+ continuation,
463
+ eq(schema.recordingTranscripts.status, "ready"),
464
+ sql`TRIM(${schema.recordingTranscripts.fullText}) <> ''`,
465
+ ),
466
+ )
467
+ .orderBy(asc(schema.recordings.createdAt), asc(schema.recordings.id))
468
+ .limit(options.limit + 1);
469
+ const truncated = rows.length > options.limit;
470
+ const candidates = rows.slice(0, options.limit);
471
+ const results = await mapWithConcurrency(
472
+ candidates,
473
+ options.concurrency,
474
+ ({ recording, transcript }) =>
475
+ exportRecording(recording, transcript, destination),
476
+ );
477
+ const lastCandidate = candidates[candidates.length - 1]?.recording;
478
+ const nextCursor =
479
+ truncated && lastCandidate
480
+ ? encodeBackfillCursor({
481
+ version: 1,
482
+ lookbackDays,
483
+ since,
484
+ until,
485
+ afterCreatedAt: lastCandidate.createdAt,
486
+ afterId: lastCandidate.id,
487
+ })
488
+ : null;
489
+
490
+ return {
491
+ mode: "backfill" as const,
492
+ organizationId,
493
+ ownerEmail,
494
+ since,
495
+ until,
496
+ lookbackDays,
497
+ limit: options.limit,
498
+ truncated,
499
+ nextCursor,
500
+ candidateCount: candidates.length,
501
+ attempted: results.length,
502
+ exported: results.filter((result) => result.status === "exported").length,
503
+ quarantined: results.filter((result) => result.status === "quarantined")
504
+ .length,
505
+ skipped: results.filter((result) => result.status === "skipped").length,
506
+ failed: results.filter((result) => result.status === "failed").length,
507
+ results,
508
+ };
509
+ }
510
+
511
+ export default defineAction({
512
+ description:
513
+ "Export one Clips recording transcript to Brain, or omit recordingId to backfill a bounded active-organization cohort with ready transcripts.",
514
+ schema: z.object({
515
+ recordingId: z
516
+ .string()
517
+ .min(1)
518
+ .optional()
519
+ .describe("One recording to export. Omit to run a bounded backfill."),
520
+ lookbackDays: z.coerce
521
+ .number()
522
+ .int()
523
+ .min(1)
524
+ .max(90)
525
+ .default(DEFAULT_LOOKBACK_DAYS),
526
+ limit: z.coerce
527
+ .number()
528
+ .int()
529
+ .min(1)
530
+ .max(100)
531
+ .default(DEFAULT_BACKFILL_LIMIT),
532
+ concurrency: z.coerce
533
+ .number()
534
+ .int()
535
+ .min(1)
536
+ .max(8)
537
+ .default(DEFAULT_CONCURRENCY),
538
+ cursor: z
539
+ .string()
540
+ .min(1)
541
+ .optional()
542
+ .describe(
543
+ "Stable continuation cursor returned by the previous backfill page.",
544
+ ),
545
+ }),
546
+ run: async (args) => {
547
+ if (args.recordingId) {
548
+ const access = await assertAccess(
549
+ "recording",
550
+ args.recordingId,
551
+ "editor",
552
+ );
553
+ const destinationResult = await resolveBrainDestination();
554
+ if (!destinationResult.destination) {
555
+ return {
228
556
  recordingId: args.recordingId,
229
- status: response.status,
230
- statusText: response.statusText,
231
- });
557
+ status: "skipped" as const,
558
+ reason: destinationResult.reason,
559
+ };
560
+ }
561
+ const [transcript] = await getDb()
562
+ .select()
563
+ .from(schema.recordingTranscripts)
564
+ .where(eq(schema.recordingTranscripts.recordingId, args.recordingId))
565
+ .limit(1);
566
+ if (!transcript) {
232
567
  return {
233
568
  recordingId: args.recordingId,
234
- status: "failed" as const,
235
- reason: `brain-ingest-http-${response.status}`,
569
+ status: "skipped" as const,
570
+ reason: "empty-transcript",
236
571
  };
237
572
  }
238
- } catch (err) {
239
- const reason =
240
- (err as Error)?.name === "AbortError"
241
- ? "brain-ingest-timeout"
242
- : "brain-ingest-request-failed";
243
- console.warn("[clips] Brain export failed", {
244
- recordingId: args.recordingId,
245
- reason,
246
- error: (err as Error)?.message ?? String(err),
247
- });
573
+ return exportRecording(
574
+ access.resource as Recording,
575
+ transcript,
576
+ destinationResult.destination,
577
+ );
578
+ }
579
+
580
+ const destinationResult = await resolveBrainDestination();
581
+ if (!destinationResult.destination) {
248
582
  return {
249
- recordingId: args.recordingId,
250
- status: "failed" as const,
251
- reason,
583
+ mode: "backfill" as const,
584
+ attempted: 0,
585
+ exported: 0,
586
+ quarantined: 0,
587
+ skipped: 0,
588
+ failed: 0,
589
+ status: "skipped" as const,
590
+ reason: destinationResult.reason,
591
+ results: [],
252
592
  };
253
- } finally {
254
- clearTimeout(timeout);
255
593
  }
256
-
257
- return {
258
- recordingId: args.recordingId,
259
- status: "exported" as const,
260
- };
594
+ return backfillRecordings(destinationResult.destination, args);
261
595
  },
262
596
  });