@agent-native/core 0.111.4 → 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 (574) hide show
  1. package/corpus/README.md +4 -2
  2. package/corpus/core/CHANGELOG.md +34 -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 +8 -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 +35 -15
  11. package/corpus/core/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
  12. package/corpus/core/src/cli/code-agent-runs.ts +70 -36
  13. package/corpus/core/src/cli/codex-cli-participant.ts +496 -0
  14. package/corpus/core/src/cli/multi-frontier-runs.ts +1952 -0
  15. package/corpus/core/src/client/chat/ChatHistoryList.tsx +7 -404
  16. package/corpus/core/src/coding-tools/index.ts +27 -8
  17. package/corpus/core/src/embeddings/index.ts +233 -0
  18. package/corpus/core/src/index.ts +2 -0
  19. package/corpus/core/src/integrations/index.ts +10 -0
  20. package/corpus/core/src/integrations/slack-oauth.ts +1 -0
  21. package/corpus/core/src/integrations/webhook-delivery.ts +55 -0
  22. package/corpus/core/src/notifications/channels.ts +4 -10
  23. package/corpus/core/src/provider-api/actions/custom-provider-registration.ts +197 -0
  24. package/corpus/core/src/provider-api/actions/delete-staged-dataset.ts +3 -42
  25. package/corpus/core/src/provider-api/actions/list-staged-datasets.ts +3 -45
  26. package/corpus/core/src/provider-api/actions/provider-api-audit.ts +88 -0
  27. package/corpus/core/src/provider-api/actions/provider-api.ts +460 -0
  28. package/corpus/core/src/provider-api/actions/query-staged-dataset.ts +3 -140
  29. package/corpus/core/src/provider-api/actions/staged-datasets.ts +359 -0
  30. package/corpus/core/src/provider-api/gong.ts +108 -0
  31. package/corpus/core/src/scripts/docs/source-search.ts +2 -1
  32. package/corpus/core/src/search/index.ts +413 -0
  33. package/corpus/core/src/server/google-oauth-credentials.ts +80 -3
  34. package/corpus/core/src/server/index.ts +6 -0
  35. package/corpus/core/src/styles/chat-history-list.css +1 -313
  36. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  37. package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  38. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  39. package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  40. package/corpus/core/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  41. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  42. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  43. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  44. package/corpus/core/src/templates/default/AGENTS.md +25 -18
  45. package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  46. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  47. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  48. package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  49. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  50. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  51. package/corpus/core/src/templates/headless/AGENTS.md +7 -0
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  54. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  55. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  56. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  57. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  58. package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -0
  59. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  60. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -12
  61. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  62. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +11 -2
  63. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  64. package/corpus/templates/analytics/actions/delete-staged-dataset.ts +3 -23
  65. package/corpus/templates/analytics/actions/list-staged-datasets.ts +3 -27
  66. package/corpus/templates/analytics/actions/provider-api-catalog.ts +16 -19
  67. package/corpus/templates/analytics/actions/provider-api-docs.ts +3 -5
  68. package/corpus/templates/analytics/actions/provider-api-request.ts +162 -185
  69. package/corpus/templates/analytics/actions/query-staged-dataset.ts +3 -47
  70. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +52 -161
  71. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -12
  72. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  73. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +11 -2
  74. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  75. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +63 -160
  76. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -12
  77. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +48 -5
  78. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +46 -14
  79. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  80. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +11 -2
  81. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  82. package/corpus/templates/brain/README.md +34 -3
  83. package/corpus/templates/brain/actions/_schemas.ts +13 -7
  84. package/corpus/templates/brain/actions/approve-proposal.ts +2 -0
  85. package/corpus/templates/brain/actions/claim-distillation.ts +24 -27
  86. package/corpus/templates/brain/actions/create-source.ts +1 -1
  87. package/corpus/templates/brain/actions/delete-staged-dataset.ts +3 -24
  88. package/corpus/templates/brain/actions/get-knowledge.ts +6 -2
  89. package/corpus/templates/brain/actions/get-pilot-report.ts +44 -8
  90. package/corpus/templates/brain/actions/import-capture.ts +21 -9
  91. package/corpus/templates/brain/actions/import-transcript.ts +42 -14
  92. package/corpus/templates/brain/actions/list-captures.ts +10 -1
  93. package/corpus/templates/brain/actions/list-distillation-queue.ts +39 -14
  94. package/corpus/templates/brain/actions/list-projects.ts +51 -0
  95. package/corpus/templates/brain/actions/list-proposals.ts +80 -4
  96. package/corpus/templates/brain/actions/list-sources.ts +17 -3
  97. package/corpus/templates/brain/actions/list-staged-datasets.ts +3 -28
  98. package/corpus/templates/brain/actions/manage-project.ts +118 -0
  99. package/corpus/templates/brain/actions/mark-capture-distilled.ts +68 -20
  100. package/corpus/templates/brain/actions/navigate.ts +1 -0
  101. package/corpus/templates/brain/actions/provider-api-catalog.ts +5 -11
  102. package/corpus/templates/brain/actions/provider-api-docs.ts +3 -5
  103. package/corpus/templates/brain/actions/provider-api-request.ts +3 -37
  104. package/corpus/templates/brain/actions/query-staged-dataset.ts +3 -48
  105. package/corpus/templates/brain/actions/reject-proposal.ts +6 -1
  106. package/corpus/templates/brain/actions/resanitize-captures.ts +159 -33
  107. package/corpus/templates/brain/actions/review-proposal.ts +2 -0
  108. package/corpus/templates/brain/actions/search-everything.ts +10 -1
  109. package/corpus/templates/brain/actions/search-knowledge.ts +1 -1
  110. package/corpus/templates/brain/actions/set-settings.ts +36 -1
  111. package/corpus/templates/brain/actions/update-proposal.ts +2 -0
  112. package/corpus/templates/brain/actions/update-source.ts +5 -1
  113. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +48 -154
  114. package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +18 -9
  115. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +3 -0
  116. package/corpus/templates/brain/app/i18n-data.ts +731 -2
  117. package/corpus/templates/brain/app/lib/brain.ts +88 -1
  118. package/corpus/templates/brain/app/lib/ingest-handoff.ts +35 -0
  119. package/corpus/templates/brain/app/routes/ops.tsx +47 -0
  120. package/corpus/templates/brain/app/routes/review.tsx +41 -2
  121. package/corpus/templates/brain/app/routes/search.tsx +33 -2
  122. package/corpus/templates/brain/app/routes/settings.tsx +158 -0
  123. package/corpus/templates/brain/app/routes/sources.tsx +31 -0
  124. package/corpus/templates/brain/changelog/2026-07-19-added-private-source-safe-semantic-search.md +6 -0
  125. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-can-now-join-configured-public-channels-befo.md +6 -0
  126. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-continue-across-the-configured-number-of.md +6 -0
  127. package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-start-with-a-safe-four-week-window-and-r.md +6 -0
  128. package/corpus/templates/brain/jobs/process-ingest-queue.ts +270 -36
  129. package/corpus/templates/brain/package.json +1 -0
  130. package/corpus/templates/brain/server/db/index.ts +18 -0
  131. package/corpus/templates/brain/server/db/schema.ts +197 -1
  132. package/corpus/templates/brain/server/jobs/distillation-queue.ts +12 -5
  133. package/corpus/templates/brain/server/lib/audiences.ts +708 -0
  134. package/corpus/templates/brain/server/lib/brain-health.ts +251 -38
  135. package/corpus/templates/brain/server/lib/brain.ts +877 -50
  136. package/corpus/templates/brain/server/lib/capture-sanitization.ts +217 -53
  137. package/corpus/templates/brain/server/lib/connectors.ts +609 -72
  138. package/corpus/templates/brain/server/lib/demo.ts +17 -4
  139. package/corpus/templates/brain/server/lib/hybrid-search.ts +363 -0
  140. package/corpus/templates/brain/server/lib/ingest-queue.ts +104 -0
  141. package/corpus/templates/brain/server/lib/privacy-readiness.ts +24 -0
  142. package/corpus/templates/brain/server/lib/search-index-contracts.ts +72 -0
  143. package/corpus/templates/brain/server/lib/search-index.ts +714 -0
  144. package/corpus/templates/brain/server/lib/search.ts +120 -4
  145. package/corpus/templates/brain/server/lib/sensitivity-policy.ts +174 -0
  146. package/corpus/templates/brain/server/lib/slack-channel-membership.ts +44 -0
  147. package/corpus/templates/brain/server/lib/slack-events.ts +281 -0
  148. package/corpus/templates/brain/server/onboarding.ts +29 -0
  149. package/corpus/templates/brain/server/plugins/db.ts +204 -0
  150. package/corpus/templates/brain/server/register-secrets.ts +46 -0
  151. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +36 -16
  152. package/corpus/templates/brain/server/routes/api/_agent-native/brain/slack-events.post.ts +28 -0
  153. package/corpus/templates/brain/shared/slack-source-config.ts +60 -0
  154. package/corpus/templates/brain/shared/types.ts +10 -0
  155. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -12
  156. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  157. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +11 -2
  158. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  159. package/corpus/templates/calendar/actions/delete-staged-dataset.ts +3 -24
  160. package/corpus/templates/calendar/actions/list-staged-datasets.ts +3 -28
  161. package/corpus/templates/calendar/actions/provider-api-catalog.ts +5 -11
  162. package/corpus/templates/calendar/actions/provider-api-docs.ts +3 -5
  163. package/corpus/templates/calendar/actions/provider-api-request.ts +3 -37
  164. package/corpus/templates/calendar/actions/query-staged-dataset.ts +3 -48
  165. package/corpus/templates/calendar/server/handlers/gong.ts +6 -61
  166. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -8
  167. package/corpus/templates/calendar/server/lib/google-calendar.ts +7 -45
  168. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  169. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  170. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  171. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  172. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  173. package/corpus/templates/chat/AGENTS.md +5 -2
  174. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  175. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -12
  176. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  177. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +11 -2
  178. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  179. package/corpus/templates/clips/AGENTS.md +11 -0
  180. package/corpus/templates/clips/actions/export-to-brain.ts +505 -171
  181. package/corpus/templates/clips/changelog/2026-07-19-ready-clips-transcripts-can-be-backfilled-to-brain-for-a-bou.md +6 -0
  182. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -1
  183. package/corpus/templates/clips/server/lib/google-calendar-client.ts +12 -22
  184. package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +9 -6
  185. package/corpus/templates/clips/server/lib/slack-oauth.ts +12 -47
  186. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +2 -6
  187. package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +4 -4
  188. package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -12
  189. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  190. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +11 -2
  191. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  192. package/corpus/templates/content/actions/delete-staged-dataset.ts +3 -24
  193. package/corpus/templates/content/actions/list-staged-datasets.ts +3 -28
  194. package/corpus/templates/content/actions/provider-api-catalog.ts +5 -11
  195. package/corpus/templates/content/actions/provider-api-docs.ts +3 -5
  196. package/corpus/templates/content/actions/provider-api-request.ts +3 -37
  197. package/corpus/templates/content/actions/query-staged-dataset.ts +3 -48
  198. package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -12
  199. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  200. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +11 -2
  201. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  202. package/corpus/templates/design/actions/delete-staged-dataset.ts +4 -23
  203. package/corpus/templates/design/actions/list-staged-datasets.ts +4 -27
  204. package/corpus/templates/design/actions/provider-api-catalog.ts +15 -18
  205. package/corpus/templates/design/actions/provider-api-docs.ts +57 -48
  206. package/corpus/templates/design/actions/provider-api-request.ts +140 -164
  207. package/corpus/templates/design/actions/query-staged-dataset.ts +4 -47
  208. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -12
  209. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  210. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +11 -2
  211. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  212. package/corpus/templates/dispatch/actions/delete-staged-dataset.ts +4 -23
  213. package/corpus/templates/dispatch/actions/list-staged-datasets.ts +4 -30
  214. package/corpus/templates/dispatch/actions/provider-api-catalog.ts +15 -18
  215. package/corpus/templates/dispatch/actions/provider-api-docs.ts +57 -48
  216. package/corpus/templates/dispatch/actions/provider-api-request.ts +138 -163
  217. package/corpus/templates/dispatch/actions/query-staged-dataset.ts +4 -47
  218. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -12
  219. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  220. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +11 -2
  221. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  222. package/corpus/templates/forms/server/lib/integrations.ts +23 -22
  223. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  224. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  225. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -12
  226. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  227. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +11 -2
  228. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  229. package/corpus/templates/mail/actions/delete-staged-dataset.ts +4 -23
  230. package/corpus/templates/mail/actions/list-staged-datasets.ts +4 -27
  231. package/corpus/templates/mail/actions/provider-api-catalog.ts +15 -18
  232. package/corpus/templates/mail/actions/provider-api-docs.ts +57 -48
  233. package/corpus/templates/mail/actions/provider-api-request.ts +146 -173
  234. package/corpus/templates/mail/actions/query-staged-dataset.ts +4 -47
  235. package/corpus/templates/mail/server/handlers/gong.ts +6 -61
  236. package/corpus/templates/mail/server/lib/google-auth.ts +10 -13
  237. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -12
  238. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  239. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +11 -2
  240. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  241. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +47 -156
  242. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -12
  243. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  244. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +11 -2
  245. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  246. package/corpus/templates/slides/actions/delete-staged-dataset.ts +4 -23
  247. package/corpus/templates/slides/actions/list-staged-datasets.ts +4 -27
  248. package/corpus/templates/slides/actions/provider-api-catalog.ts +15 -18
  249. package/corpus/templates/slides/actions/provider-api-docs.ts +57 -48
  250. package/corpus/templates/slides/actions/provider-api-request.ts +145 -172
  251. package/corpus/templates/slides/actions/query-staged-dataset.ts +4 -47
  252. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +11 -36
  253. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -12
  254. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +10 -0
  255. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  256. package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +11 -2
  257. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  258. package/corpus/toolkit/CHANGELOG.md +125 -0
  259. package/corpus/toolkit/README.md +60 -0
  260. package/corpus/toolkit/export-snapshot.json +35 -0
  261. package/corpus/toolkit/migration-manifest.json +4 -0
  262. package/corpus/toolkit/package.json +281 -0
  263. package/corpus/toolkit/scripts/finalize-build.mjs +55 -0
  264. package/corpus/toolkit/src/app-shell/header-actions.tsx +84 -0
  265. package/corpus/toolkit/src/app-shell/index.ts +1 -0
  266. package/corpus/toolkit/src/chat-history/ChatHistoryList.tsx +469 -0
  267. package/corpus/toolkit/src/chat-history/index.ts +7 -0
  268. package/corpus/toolkit/src/chat-history.css +314 -0
  269. package/corpus/toolkit/src/collab-ui/AgentPresenceChip.tsx +69 -0
  270. package/corpus/toolkit/src/collab-ui/LiveCursorOverlay.tsx +241 -0
  271. package/corpus/toolkit/src/collab-ui/PresenceBar.tsx +346 -0
  272. package/corpus/toolkit/src/collab-ui/RecentEditHighlights.tsx +225 -0
  273. package/corpus/toolkit/src/collab-ui/RemoteSelectionRings.tsx +227 -0
  274. package/corpus/toolkit/src/collab-ui/agent-identity.ts +16 -0
  275. package/corpus/toolkit/src/collab-ui/index.ts +39 -0
  276. package/corpus/toolkit/src/collab-ui/lead-client.ts +32 -0
  277. package/corpus/toolkit/src/collab-ui/types.ts +96 -0
  278. package/corpus/toolkit/src/composer/AgentComposerFrame.tsx +59 -0
  279. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +983 -0
  280. package/corpus/toolkit/src/composer/MentionPopover.tsx +412 -0
  281. package/corpus/toolkit/src/composer/PastedTextChip.tsx +150 -0
  282. package/corpus/toolkit/src/composer/PromptComposer.tsx +718 -0
  283. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +856 -0
  284. package/corpus/toolkit/src/composer/TiptapComposer.tsx +2936 -0
  285. package/corpus/toolkit/src/composer/VoiceButton.tsx +255 -0
  286. package/corpus/toolkit/src/composer/asset-picker-url.ts +80 -0
  287. package/corpus/toolkit/src/composer/attachment-accept.ts +71 -0
  288. package/corpus/toolkit/src/composer/draft-key.ts +7 -0
  289. package/corpus/toolkit/src/composer/extensions/FileReference.tsx +55 -0
  290. package/corpus/toolkit/src/composer/extensions/MentionReference.tsx +93 -0
  291. package/corpus/toolkit/src/composer/extensions/SkillReference.tsx +56 -0
  292. package/corpus/toolkit/src/composer/index.ts +79 -0
  293. package/corpus/toolkit/src/composer/pasted-text.ts +160 -0
  294. package/corpus/toolkit/src/composer/prompt-attachments.ts +93 -0
  295. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +64 -0
  296. package/corpus/toolkit/src/composer/realtime-voice-transcript.ts +134 -0
  297. package/corpus/toolkit/src/composer/runtime-adapters.tsx +343 -0
  298. package/corpus/toolkit/src/composer/types.ts +66 -0
  299. package/corpus/toolkit/src/composer/use-file-search.ts +64 -0
  300. package/corpus/toolkit/src/composer/use-mention-search.ts +90 -0
  301. package/corpus/toolkit/src/composer/use-skills.ts +45 -0
  302. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +2117 -0
  303. package/corpus/toolkit/src/composer/useVoiceDictation.ts +1191 -0
  304. package/corpus/toolkit/src/context-ui/ContextMeter.tsx +101 -0
  305. package/corpus/toolkit/src/context-ui/ContextSegmentRow.tsx +184 -0
  306. package/corpus/toolkit/src/context-ui/ContextTreemap.tsx +125 -0
  307. package/corpus/toolkit/src/context-ui/ContextXRayPanel.tsx +377 -0
  308. package/corpus/toolkit/src/context-ui/format.ts +42 -0
  309. package/corpus/toolkit/src/context-ui/index.ts +15 -0
  310. package/corpus/toolkit/src/context-ui/types.ts +43 -0
  311. package/corpus/toolkit/src/design-tweaks/index.ts +1 -0
  312. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +1084 -0
  313. package/corpus/toolkit/src/editor/BubbleToolbar.tsx +253 -0
  314. package/corpus/toolkit/src/editor/CodeBlockNode.tsx +254 -0
  315. package/corpus/toolkit/src/editor/DragHandle.ts +1437 -0
  316. package/corpus/toolkit/src/editor/ImageExtension.ts +306 -0
  317. package/corpus/toolkit/src/editor/RegistryBlockContext.tsx +107 -0
  318. package/corpus/toolkit/src/editor/RegistryBlockNode.tsx +652 -0
  319. package/corpus/toolkit/src/editor/RichMarkdownEditor.tsx +81 -0
  320. package/corpus/toolkit/src/editor/RunId.ts +61 -0
  321. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +321 -0
  322. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +310 -0
  323. package/corpus/toolkit/src/editor/extensions.ts +402 -0
  324. package/corpus/toolkit/src/editor/gfmDoc.ts +92 -0
  325. package/corpus/toolkit/src/editor/index.ts +93 -0
  326. package/corpus/toolkit/src/editor/registrySlashCommands.ts +131 -0
  327. package/corpus/toolkit/src/editor/surgical-apply.ts +192 -0
  328. package/corpus/toolkit/src/editor/useCollabReconcile.ts +589 -0
  329. package/corpus/toolkit/src/editor.css +510 -0
  330. package/corpus/toolkit/src/hooks/index.ts +2 -0
  331. package/corpus/toolkit/src/hooks/use-mobile.tsx +21 -0
  332. package/corpus/toolkit/src/hooks/use-toast.ts +188 -0
  333. package/corpus/toolkit/src/index.ts +8 -0
  334. package/corpus/toolkit/src/onboarding/index.ts +1 -0
  335. package/corpus/toolkit/src/provider.tsx +47 -0
  336. package/corpus/toolkit/src/sharing/VisibilityBadge.tsx +37 -0
  337. package/corpus/toolkit/src/sharing/index.ts +4 -0
  338. package/corpus/toolkit/src/styles.css +21 -0
  339. package/corpus/toolkit/src/ui/accordion.tsx +56 -0
  340. package/corpus/toolkit/src/ui/alert-dialog.tsx +141 -0
  341. package/corpus/toolkit/src/ui/alert.tsx +59 -0
  342. package/corpus/toolkit/src/ui/aspect-ratio.tsx +5 -0
  343. package/corpus/toolkit/src/ui/avatar.tsx +48 -0
  344. package/corpus/toolkit/src/ui/badge.tsx +37 -0
  345. package/corpus/toolkit/src/ui/breadcrumb.tsx +115 -0
  346. package/corpus/toolkit/src/ui/button.tsx +78 -0
  347. package/corpus/toolkit/src/ui/calendar.tsx +214 -0
  348. package/corpus/toolkit/src/ui/card.tsx +86 -0
  349. package/corpus/toolkit/src/ui/carousel.tsx +260 -0
  350. package/corpus/toolkit/src/ui/chart.tsx +375 -0
  351. package/corpus/toolkit/src/ui/checkbox.tsx +28 -0
  352. package/corpus/toolkit/src/ui/collapsible.tsx +9 -0
  353. package/corpus/toolkit/src/ui/command.tsx +174 -0
  354. package/corpus/toolkit/src/ui/context-menu.tsx +202 -0
  355. package/corpus/toolkit/src/ui/date-picker.tsx +64 -0
  356. package/corpus/toolkit/src/ui/dialog.tsx +146 -0
  357. package/corpus/toolkit/src/ui/drawer.tsx +126 -0
  358. package/corpus/toolkit/src/ui/dropdown-menu.tsx +218 -0
  359. package/corpus/toolkit/src/ui/form.tsx +177 -0
  360. package/corpus/toolkit/src/ui/hover-card.tsx +27 -0
  361. package/corpus/toolkit/src/ui/index.ts +52 -0
  362. package/corpus/toolkit/src/ui/input-otp.tsx +69 -0
  363. package/corpus/toolkit/src/ui/input.tsx +22 -0
  364. package/corpus/toolkit/src/ui/label.tsx +24 -0
  365. package/corpus/toolkit/src/ui/logo.tsx +36 -0
  366. package/corpus/toolkit/src/ui/menubar.tsx +235 -0
  367. package/corpus/toolkit/src/ui/navigation-menu.tsx +128 -0
  368. package/corpus/toolkit/src/ui/pagination.tsx +121 -0
  369. package/corpus/toolkit/src/ui/popover.tsx +44 -0
  370. package/corpus/toolkit/src/ui/progress.tsx +26 -0
  371. package/corpus/toolkit/src/ui/radio-group.tsx +42 -0
  372. package/corpus/toolkit/src/ui/resizable.tsx +43 -0
  373. package/corpus/toolkit/src/ui/scroll-area.tsx +46 -0
  374. package/corpus/toolkit/src/ui/select.tsx +158 -0
  375. package/corpus/toolkit/src/ui/separator.tsx +29 -0
  376. package/corpus/toolkit/src/ui/sheet.tsx +160 -0
  377. package/corpus/toolkit/src/ui/sidebar.tsx +777 -0
  378. package/corpus/toolkit/src/ui/skeleton.tsx +18 -0
  379. package/corpus/toolkit/src/ui/slider.tsx +26 -0
  380. package/corpus/toolkit/src/ui/sonner.tsx +58 -0
  381. package/corpus/toolkit/src/ui/spinner.tsx +17 -0
  382. package/corpus/toolkit/src/ui/switch.tsx +27 -0
  383. package/corpus/toolkit/src/ui/table.tsx +117 -0
  384. package/corpus/toolkit/src/ui/tabs.tsx +53 -0
  385. package/corpus/toolkit/src/ui/textarea.tsx +23 -0
  386. package/corpus/toolkit/src/ui/toast.tsx +127 -0
  387. package/corpus/toolkit/src/ui/toaster.tsx +33 -0
  388. package/corpus/toolkit/src/ui/toggle-group.tsx +59 -0
  389. package/corpus/toolkit/src/ui/toggle.tsx +43 -0
  390. package/corpus/toolkit/src/ui/tooltip.tsx +53 -0
  391. package/corpus/toolkit/src/ui/use-toast.ts +3 -0
  392. package/corpus/toolkit/src/utils.ts +8 -0
  393. package/corpus/toolkit/tsconfig.json +23 -0
  394. package/dist/cli/atomic-json-file.d.ts +30 -0
  395. package/dist/cli/atomic-json-file.d.ts.map +1 -0
  396. package/dist/cli/atomic-json-file.js +240 -0
  397. package/dist/cli/atomic-json-file.js.map +1 -0
  398. package/dist/cli/claude-code-participant.d.ts +77 -0
  399. package/dist/cli/claude-code-participant.d.ts.map +1 -0
  400. package/dist/cli/claude-code-participant.js +355 -0
  401. package/dist/cli/claude-code-participant.js.map +1 -0
  402. package/dist/cli/code-agent-executor.d.ts +8 -9
  403. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  404. package/dist/cli/code-agent-executor.js +27 -13
  405. package/dist/cli/code-agent-executor.js.map +1 -1
  406. package/dist/cli/code-agent-runs.concurrent-worker.d.ts +2 -0
  407. package/dist/cli/code-agent-runs.concurrent-worker.d.ts.map +1 -0
  408. package/dist/cli/code-agent-runs.concurrent-worker.js +32 -0
  409. package/dist/cli/code-agent-runs.concurrent-worker.js.map +1 -0
  410. package/dist/cli/code-agent-runs.d.ts +5 -0
  411. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  412. package/dist/cli/code-agent-runs.js +51 -33
  413. package/dist/cli/code-agent-runs.js.map +1 -1
  414. package/dist/cli/codex-cli-participant.d.ts +77 -0
  415. package/dist/cli/codex-cli-participant.d.ts.map +1 -0
  416. package/dist/cli/codex-cli-participant.js +354 -0
  417. package/dist/cli/codex-cli-participant.js.map +1 -0
  418. package/dist/cli/multi-frontier-runs.d.ts +246 -0
  419. package/dist/cli/multi-frontier-runs.d.ts.map +1 -0
  420. package/dist/cli/multi-frontier-runs.js +1247 -0
  421. package/dist/cli/multi-frontier-runs.js.map +1 -0
  422. package/dist/client/chat/ChatHistoryList.d.ts +1 -87
  423. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -1
  424. package/dist/client/chat/ChatHistoryList.js +1 -82
  425. package/dist/client/chat/ChatHistoryList.js.map +1 -1
  426. package/dist/coding-tools/index.d.ts +1 -0
  427. package/dist/coding-tools/index.d.ts.map +1 -1
  428. package/dist/coding-tools/index.js +25 -7
  429. package/dist/coding-tools/index.js.map +1 -1
  430. package/dist/collab/awareness.d.ts +2 -2
  431. package/dist/collab/awareness.d.ts.map +1 -1
  432. package/dist/embeddings/index.d.ts +24 -0
  433. package/dist/embeddings/index.d.ts.map +1 -0
  434. package/dist/embeddings/index.js +172 -0
  435. package/dist/embeddings/index.js.map +1 -0
  436. package/dist/index.d.ts +2 -0
  437. package/dist/index.d.ts.map +1 -1
  438. package/dist/index.js +2 -0
  439. package/dist/index.js.map +1 -1
  440. package/dist/integrations/index.d.ts +2 -1
  441. package/dist/integrations/index.d.ts.map +1 -1
  442. package/dist/integrations/index.js +2 -1
  443. package/dist/integrations/index.js.map +1 -1
  444. package/dist/integrations/slack-manifest.d.ts +1 -1
  445. package/dist/integrations/slack-oauth.d.ts +1 -1
  446. package/dist/integrations/slack-oauth.d.ts.map +1 -1
  447. package/dist/integrations/slack-oauth.js +1 -0
  448. package/dist/integrations/slack-oauth.js.map +1 -1
  449. package/dist/integrations/webhook-delivery.d.ts +23 -0
  450. package/dist/integrations/webhook-delivery.d.ts.map +1 -0
  451. package/dist/integrations/webhook-delivery.js +32 -0
  452. package/dist/integrations/webhook-delivery.js.map +1 -0
  453. package/dist/notifications/channels.d.ts.map +1 -1
  454. package/dist/notifications/channels.js +4 -9
  455. package/dist/notifications/channels.js.map +1 -1
  456. package/dist/notifications/routes.d.ts +3 -3
  457. package/dist/progress/routes.d.ts +1 -1
  458. package/dist/provider-api/actions/custom-provider-registration.d.ts +136 -0
  459. package/dist/provider-api/actions/custom-provider-registration.d.ts.map +1 -0
  460. package/dist/provider-api/actions/custom-provider-registration.js +154 -0
  461. package/dist/provider-api/actions/custom-provider-registration.js.map +1 -0
  462. package/dist/provider-api/actions/delete-staged-dataset.d.ts +1 -1
  463. package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -1
  464. package/dist/provider-api/actions/delete-staged-dataset.js +3 -34
  465. package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -1
  466. package/dist/provider-api/actions/list-staged-datasets.d.ts +2 -3
  467. package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -1
  468. package/dist/provider-api/actions/list-staged-datasets.js +3 -40
  469. package/dist/provider-api/actions/list-staged-datasets.js.map +1 -1
  470. package/dist/provider-api/actions/provider-api-audit.d.ts +7 -0
  471. package/dist/provider-api/actions/provider-api-audit.d.ts.map +1 -0
  472. package/dist/provider-api/actions/provider-api-audit.js +74 -0
  473. package/dist/provider-api/actions/provider-api-audit.js.map +1 -0
  474. package/dist/provider-api/actions/provider-api.d.ts +370 -0
  475. package/dist/provider-api/actions/provider-api.d.ts.map +1 -0
  476. package/dist/provider-api/actions/provider-api.js +280 -0
  477. package/dist/provider-api/actions/provider-api.js.map +1 -0
  478. package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
  479. package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
  480. package/dist/provider-api/actions/query-staged-dataset.js +3 -116
  481. package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
  482. package/dist/provider-api/actions/staged-datasets.d.ts +190 -0
  483. package/dist/provider-api/actions/staged-datasets.d.ts.map +1 -0
  484. package/dist/provider-api/actions/staged-datasets.js +210 -0
  485. package/dist/provider-api/actions/staged-datasets.js.map +1 -0
  486. package/dist/provider-api/gong.d.ts +44 -0
  487. package/dist/provider-api/gong.d.ts.map +1 -0
  488. package/dist/provider-api/gong.js +56 -0
  489. package/dist/provider-api/gong.js.map +1 -0
  490. package/dist/resources/handlers.d.ts +1 -1
  491. package/dist/scripts/docs/source-search.d.ts +2 -1
  492. package/dist/scripts/docs/source-search.d.ts.map +1 -1
  493. package/dist/scripts/docs/source-search.js +2 -1
  494. package/dist/scripts/docs/source-search.js.map +1 -1
  495. package/dist/search/index.d.ts +88 -0
  496. package/dist/search/index.d.ts.map +1 -0
  497. package/dist/search/index.js +267 -0
  498. package/dist/search/index.js.map +1 -0
  499. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  500. package/dist/server/google-oauth-credentials.d.ts +35 -0
  501. package/dist/server/google-oauth-credentials.d.ts.map +1 -1
  502. package/dist/server/google-oauth-credentials.js +48 -2
  503. package/dist/server/google-oauth-credentials.js.map +1 -1
  504. package/dist/server/index.d.ts +1 -1
  505. package/dist/server/index.d.ts.map +1 -1
  506. package/dist/server/index.js +1 -1
  507. package/dist/server/index.js.map +1 -1
  508. package/dist/styles/chat-history-list.css +1 -313
  509. package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  510. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  511. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  512. package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  513. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  514. package/dist/templates/chat/AGENTS.md +5 -2
  515. package/dist/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  516. package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -12
  517. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  518. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  519. package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  520. package/dist/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  521. package/dist/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  522. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  523. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  524. package/dist/templates/default/AGENTS.md +25 -18
  525. package/dist/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  526. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  527. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  528. package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  529. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  530. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  531. package/dist/templates/headless/AGENTS.md +7 -0
  532. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  533. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  534. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  535. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  536. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  537. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  538. package/dist/templates/workspace-core/AGENTS.md +4 -0
  539. package/dist/templates/workspace-root/AGENTS.md +4 -0
  540. package/docs/AGENTS.md +7 -0
  541. package/docs/content/integrations.mdx +63 -2
  542. package/docs/design/provider-integration-authority.md +117 -0
  543. package/package.json +9 -2
  544. package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -12
  545. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +10 -0
  546. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  547. package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +11 -2
  548. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  549. package/src/templates/chat/AGENTS.md +5 -2
  550. package/src/templates/chat/app/components/layout/Sidebar.tsx +51 -157
  551. package/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
  552. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +10 -0
  553. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  554. package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
  555. package/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
  556. package/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
  557. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +11 -2
  558. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  559. package/src/templates/default/AGENTS.md +25 -18
  560. package/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
  561. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +10 -0
  562. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  563. package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
  564. package/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
  565. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  566. package/src/templates/headless/AGENTS.md +7 -0
  567. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
  568. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +10 -0
  569. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  570. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +102 -0
  571. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +11 -2
  572. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
  573. package/src/templates/workspace-core/AGENTS.md +4 -0
  574. package/src/templates/workspace-root/AGENTS.md +4 -0
@@ -1,20 +1,27 @@
1
1
  import { getCredentialContext } from "@agent-native/core/server";
2
2
  import { accessFilter, assertAccess } from "@agent-native/core/sharing";
3
- import { and, desc, eq } from "drizzle-orm";
3
+ import { and, desc, eq, inArray } from "drizzle-orm";
4
4
 
5
5
  import type {
6
6
  BrainCaptureKind,
7
7
  BrainSourceProvider,
8
8
  } from "../../shared/types.js";
9
9
  import { getDb, schema } from "../db/index.js";
10
+ import { listAccessibleAudienceIds } from "./audiences.js";
10
11
  import {
12
+ BrainCaptureBlockedError,
11
13
  createCapture,
12
14
  nanoid,
13
15
  nowIso,
14
16
  parseJson,
17
+ readBrainSettings,
15
18
  serializeCapture,
16
19
  stableJson,
17
20
  } from "./brain.js";
21
+ import {
22
+ ensureSlackPublicChannelMembership,
23
+ type SlackChannelJoinResponse,
24
+ } from "./slack-channel-membership.js";
18
25
  import {
19
26
  inspectSourceCredentialAvailability,
20
27
  resolveSourceCredential,
@@ -31,6 +38,19 @@ export interface ConnectorSyncResult {
31
38
  stats?: Record<string, unknown>;
32
39
  }
33
40
 
41
+ async function createConnectorCapture(
42
+ values: Parameters<typeof createCapture>[0],
43
+ ) {
44
+ try {
45
+ return { capture: await createCapture(values), blocked: false };
46
+ } catch (error) {
47
+ if (error instanceof BrainCaptureBlockedError) {
48
+ return { capture: null, blocked: true };
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+
34
54
  export interface SlackPilotOptions {
35
55
  readHistory?: boolean;
36
56
  channelRefs?: string[];
@@ -145,6 +165,7 @@ interface SlackChannelCursor {
145
165
 
146
166
  interface SlackSyncCursor {
147
167
  channels?: Record<string, SlackChannelCursor>;
168
+ publicChannelOffset?: number;
148
169
  retry?: RetryCursor;
149
170
  lastRunAt?: string;
150
171
  }
@@ -164,6 +185,8 @@ interface SlackChannel {
164
185
  is_channel?: boolean;
165
186
  is_group?: boolean;
166
187
  is_archived?: boolean;
188
+ is_private?: boolean;
189
+ is_member?: boolean;
167
190
  }
168
191
 
169
192
  interface SlackMessage {
@@ -175,6 +198,7 @@ interface SlackMessage {
175
198
  text?: string;
176
199
  ts?: string;
177
200
  thread_ts?: string;
201
+ reactions?: Array<{ name?: string; count?: number }>;
178
202
  }
179
203
 
180
204
  interface SlackHistoryResponse {
@@ -183,6 +207,10 @@ interface SlackHistoryResponse {
183
207
  response_metadata?: { next_cursor?: string };
184
208
  }
185
209
 
210
+ interface SlackRepliesResponse {
211
+ messages?: SlackMessage[];
212
+ }
213
+
186
214
  interface SlackInfoResponse {
187
215
  channel?: SlackChannel;
188
216
  }
@@ -192,6 +220,19 @@ interface SlackListResponse {
192
220
  response_metadata?: { next_cursor?: string };
193
221
  }
194
222
 
223
+ interface SlackMembersResponse {
224
+ members?: string[];
225
+ response_metadata?: { next_cursor?: string };
226
+ }
227
+
228
+ interface SlackUserInfoResponse {
229
+ user?: { profile?: { email?: string } };
230
+ }
231
+
232
+ type SlackUserEmailCache = Map<string, string | null>;
233
+
234
+ const SLACK_USER_LOOKUP_CONCURRENCY = 4;
235
+
195
236
  interface SlackPermalinkResponse {
196
237
  permalink?: string;
197
238
  }
@@ -702,10 +743,6 @@ function readableJson(value: unknown): string {
702
743
  return JSON.stringify(value, null, 2);
703
744
  }
704
745
 
705
- function slackUserLabel(message: SlackMessage): string {
706
- return message.username ?? message.user ?? message.bot_id ?? "unknown";
707
- }
708
-
709
746
  function slackDirectConversationFromRef(
710
747
  channelRef: string,
711
748
  ): SlackChannel | null {
@@ -726,32 +763,120 @@ function isUsableSlackChannel(channel: SlackChannel): boolean {
726
763
  );
727
764
  }
728
765
 
729
- function normalizeSlackMessageContent(
730
- channel: SlackChannel,
731
- message: SlackMessage,
732
- ): string {
733
- const label = slackUserLabel(message);
734
- const time = isoFromSlackTs(message.ts) ?? message.ts ?? "unknown time";
735
- const thread =
736
- message.thread_ts && message.thread_ts !== message.ts
737
- ? `\nThread: ${message.thread_ts}`
738
- : "";
739
- return [
740
- `Slack #${channel.name ?? channel.id} at ${time}`,
741
- `User: ${label}${thread}`,
742
- "",
743
- message.text ?? "",
744
- ].join("\n");
766
+ function slackReactionCount(message: SlackMessage) {
767
+ return (message.reactions ?? []).reduce(
768
+ (total, reaction) => total + Math.max(0, Number(reaction.count) || 0),
769
+ 0,
770
+ );
771
+ }
772
+
773
+ function isSlackTextMessage(message: SlackMessage) {
774
+ return (
775
+ message.type === "message" &&
776
+ typeof message.text === "string" &&
777
+ message.text.trim().length > 0 &&
778
+ typeof message.ts === "string"
779
+ );
780
+ }
781
+
782
+ export interface SlackThreadCapture {
783
+ externalId: string;
784
+ title: string;
785
+ content: string;
786
+ capturedAt: string;
787
+ metadata: Record<string, unknown>;
788
+ }
789
+
790
+ /**
791
+ * Builds the only Slack representation Brain persists. It deliberately omits
792
+ * Slack user ids, display names, and the raw Events/Web API payload.
793
+ */
794
+ export function normalizeSlackThreadCapture(input: {
795
+ channel: SlackChannel;
796
+ messages: SlackMessage[];
797
+ permalink?: string | null;
798
+ syncRunId?: string;
799
+ }): SlackThreadCapture | null {
800
+ const messages = input.messages
801
+ .filter(isSlackTextMessage)
802
+ .sort((left, right) => Number(left.ts) - Number(right.ts));
803
+ const root = messages[0];
804
+ if (!root?.ts) return null;
805
+
806
+ let offset = 0;
807
+ const safeSegments = messages.map((message, index) => {
808
+ const capturedAt = isoFromSlackTs(message.ts) ?? message.ts!;
809
+ const text = `${capturedAt}\n${message.text!.trim()}`;
810
+ const startOffset = offset;
811
+ offset += text.length;
812
+ if (index < messages.length - 1) offset += 2;
813
+ return {
814
+ id: `slack:${input.channel.id}:${message.ts}`,
815
+ capturedAt,
816
+ sourceUrl: input.permalink ?? undefined,
817
+ text,
818
+ startOffset,
819
+ endOffset: startOffset + text.length,
820
+ reactionCount: slackReactionCount(message),
821
+ };
822
+ });
823
+ const content = safeSegments.map((segment) => segment.text).join("\n\n");
824
+ const threadTs = root.thread_ts ?? root.ts;
825
+ return {
826
+ externalId: `slack:${input.channel.id}:${threadTs}`,
827
+ title: `#${input.channel.name ?? input.channel.id} thread ${isoFromSlackTs(threadTs) ?? threadTs}`,
828
+ content,
829
+ capturedAt: isoFromSlackTs(threadTs) ?? nowIso(),
830
+ metadata: {
831
+ provider: "slack",
832
+ connector: "slack",
833
+ ...(input.syncRunId ? { syncRunId: input.syncRunId } : {}),
834
+ channelId: input.channel.id,
835
+ channelName: input.channel.name,
836
+ threadTs,
837
+ messageCount: safeSegments.length,
838
+ reactionCount: safeSegments.reduce(
839
+ (total, segment) => total + segment.reactionCount,
840
+ 0,
841
+ ),
842
+ sourceUrl: input.permalink ?? null,
843
+ permalink: input.permalink ?? null,
844
+ // These offsets are against `content`, the safe persisted capture, never
845
+ // against a provider payload.
846
+ safeSegments,
847
+ },
848
+ };
745
849
  }
746
850
 
747
851
  async function slackApi<T>(
748
852
  token: string,
749
853
  method: string,
750
854
  params: Record<string, string | number | boolean | null | undefined> = {},
855
+ options: {
856
+ httpMethod?: "GET" | "POST";
857
+ toleratedErrors?: readonly string[];
858
+ } = {},
751
859
  ): Promise<T> {
752
- const url = buildUrl(`https://slack.com/api/${method}`, params);
860
+ const httpMethod = options.httpMethod ?? "GET";
861
+ const url = buildUrl(
862
+ `https://slack.com/api/${method}`,
863
+ httpMethod === "GET" ? params : {},
864
+ );
865
+ const body = new URLSearchParams();
866
+ if (httpMethod === "POST") {
867
+ for (const [key, value] of Object.entries(params)) {
868
+ if (value !== null && value !== undefined) body.set(key, String(value));
869
+ }
870
+ }
753
871
  const response = await fetch(url.toString(), {
754
- headers: { Authorization: `Bearer ${token}` },
872
+ method: httpMethod,
873
+ headers: {
874
+ Authorization: `Bearer ${token}`,
875
+ ...(httpMethod === "POST"
876
+ ? { "Content-Type": "application/x-www-form-urlencoded" }
877
+ : {}),
878
+ },
879
+ ...(httpMethod === "POST" ? { body } : {}),
755
880
  });
756
881
  if (response.status === 429) {
757
882
  throw new ConnectorRateLimitError(
@@ -773,6 +898,9 @@ async function slackApi<T>(
773
898
  if (data.error === "ratelimited") {
774
899
  throw new ConnectorRateLimitError("slack", method, 60);
775
900
  }
901
+ if (data.error && options.toleratedErrors?.includes(data.error)) {
902
+ return data as T;
903
+ }
776
904
  const details = [
777
905
  data.needed ? `needed: ${data.needed}` : null,
778
906
  data.provided ? `provided: ${data.provided}` : null,
@@ -810,6 +938,60 @@ async function resolveSlackChannelByName(
810
938
  return null;
811
939
  }
812
940
 
941
+ function matchesPublicChannelExclusion(
942
+ channel: SlackChannel,
943
+ patterns: string[],
944
+ ) {
945
+ return patterns.some((pattern) => {
946
+ try {
947
+ const expression = new RegExp(pattern, "i");
948
+ return expression.test(channel.id) || expression.test(channel.name ?? "");
949
+ } catch {
950
+ return false;
951
+ }
952
+ });
953
+ }
954
+
955
+ async function listPublicSlackChannels(
956
+ token: string,
957
+ exclusionPatterns: string[],
958
+ ): Promise<{ channels: SlackChannel[]; excluded: number }> {
959
+ const channels: SlackChannel[] = [];
960
+ const seenCursors = new Set<string>();
961
+ let cursor: string | undefined;
962
+ let excluded = 0;
963
+ for (;;) {
964
+ const page = await slackApi<SlackListResponse>(
965
+ token,
966
+ "conversations.list",
967
+ {
968
+ types: "public_channel",
969
+ exclude_archived: true,
970
+ limit: 200,
971
+ cursor,
972
+ },
973
+ );
974
+ for (const channel of page.channels ?? []) {
975
+ if (
976
+ channel.is_archived ||
977
+ channel.is_private ||
978
+ channel.is_channel === false
979
+ ) {
980
+ continue;
981
+ }
982
+ if (matchesPublicChannelExclusion(channel, exclusionPatterns)) {
983
+ excluded += 1;
984
+ continue;
985
+ }
986
+ channels.push(channel);
987
+ }
988
+ cursor = page.response_metadata?.next_cursor;
989
+ if (!cursor || seenCursors.has(cursor)) break;
990
+ seenCursors.add(cursor);
991
+ }
992
+ return { channels, excluded };
993
+ }
994
+
813
995
  async function resolveSlackChannel(
814
996
  token: string,
815
997
  channelRef: string,
@@ -848,6 +1030,121 @@ async function slackPermalink(
848
1030
  return data.permalink ?? null;
849
1031
  }
850
1032
 
1033
+ async function slackPrivateChannelMemberEmails(
1034
+ token: string,
1035
+ channelId: string,
1036
+ userEmailCache: SlackUserEmailCache,
1037
+ ): Promise<string[] | null> {
1038
+ const memberIds = new Set<string>();
1039
+ const seenCursors = new Set<string>();
1040
+ let cursor: string | undefined;
1041
+ while (true) {
1042
+ const response = await slackApi<SlackMembersResponse>(
1043
+ token,
1044
+ "conversations.members",
1045
+ { channel: channelId, limit: 1_000, cursor },
1046
+ );
1047
+ for (const memberId of response.members ?? []) {
1048
+ if (memberId) memberIds.add(memberId);
1049
+ }
1050
+ const nextCursor = response.response_metadata?.next_cursor?.trim();
1051
+ if (!nextCursor) break;
1052
+ if (seenCursors.has(nextCursor)) return null;
1053
+ seenCursors.add(nextCursor);
1054
+ cursor = nextCursor;
1055
+ }
1056
+ const userIds = Array.from(memberIds);
1057
+ if (!userIds.length) return null;
1058
+ if (
1059
+ userIds.some(
1060
+ (userId) => userEmailCache.has(userId) && !userEmailCache.get(userId),
1061
+ )
1062
+ ) {
1063
+ return null;
1064
+ }
1065
+
1066
+ const uncachedUserIds = userIds.filter(
1067
+ (userId) => !userEmailCache.has(userId),
1068
+ );
1069
+ for (
1070
+ let offset = 0;
1071
+ offset < uncachedUserIds.length;
1072
+ offset += SLACK_USER_LOOKUP_CONCURRENCY
1073
+ ) {
1074
+ const batch = uncachedUserIds.slice(
1075
+ offset,
1076
+ offset + SLACK_USER_LOOKUP_CONCURRENCY,
1077
+ );
1078
+ await Promise.all(
1079
+ batch.map(async (userId) => {
1080
+ const user = await slackApi<SlackUserInfoResponse>(
1081
+ token,
1082
+ "users.info",
1083
+ { user: userId },
1084
+ );
1085
+ const email = user.user?.profile?.email?.trim().toLowerCase() ?? null;
1086
+ userEmailCache.set(userId, email || null);
1087
+ }),
1088
+ );
1089
+ if (batch.some((userId) => !userEmailCache.get(userId))) return null;
1090
+ }
1091
+
1092
+ const emails: string[] = [];
1093
+ for (const userId of userIds) {
1094
+ const email = userEmailCache.get(userId);
1095
+ if (!email) return null;
1096
+ emails.push(email);
1097
+ }
1098
+ return Array.from(new Set(emails)).sort();
1099
+ }
1100
+
1101
+ function configuredSlackChannelIds(config: Record<string, unknown>) {
1102
+ return new Set(
1103
+ slackChannelRefsFromConfig(config).filter((value) =>
1104
+ /^[CG][A-Z0-9]+$/i.test(value),
1105
+ ),
1106
+ );
1107
+ }
1108
+
1109
+ async function createSlackThreadCapture(input: {
1110
+ source: SourceRow;
1111
+ channel: SlackChannel;
1112
+ messages: SlackMessage[];
1113
+ permalink: string | null;
1114
+ syncRunId?: string;
1115
+ memberEmails?: string[] | null;
1116
+ }) {
1117
+ const normalized = normalizeSlackThreadCapture({
1118
+ channel: input.channel,
1119
+ messages: input.messages,
1120
+ permalink: input.permalink,
1121
+ syncRunId: input.syncRunId,
1122
+ });
1123
+ if (!normalized) return { capture: null, blocked: false };
1124
+ try {
1125
+ const capture = await createCapture({
1126
+ sourceId: input.source.id,
1127
+ externalId: normalized.externalId,
1128
+ title: normalized.title,
1129
+ kind: "message",
1130
+ content: normalized.content,
1131
+ capturedAt: normalized.capturedAt,
1132
+ metadata: normalized.metadata,
1133
+ audience: {
1134
+ kind: input.channel.is_private ? "slack-private-channel" : "org",
1135
+ memberEmails: input.memberEmails ?? undefined,
1136
+ upstreamRefHash: input.channel.id,
1137
+ },
1138
+ });
1139
+ return { capture, blocked: false };
1140
+ } catch (error) {
1141
+ if (error instanceof BrainCaptureBlockedError) {
1142
+ return { capture: null, blocked: true };
1143
+ }
1144
+ throw error;
1145
+ }
1146
+ }
1147
+
851
1148
  export async function testSlackConnection(
852
1149
  options: {
853
1150
  channelRefs?: string[];
@@ -1659,10 +1956,32 @@ async function githubRefsFromLinkedSources(
1659
1956
  Number(options.stats.linkedSourcesSkipped) + 1;
1660
1957
  continue;
1661
1958
  }
1959
+ const audienceIds = await listAccessibleAudienceIds([sourceId]);
1960
+ if (!audienceIds.length) continue;
1662
1961
  const captures = await db
1663
- .select()
1962
+ .select({
1963
+ id: schema.brainRawCaptures.id,
1964
+ title: schema.brainRawCaptures.title,
1965
+ content: schema.brainRawCaptures.content,
1966
+ metadataJson: schema.brainRawCaptures.metadataJson,
1967
+ })
1664
1968
  .from(schema.brainRawCaptures)
1665
- .where(eq(schema.brainRawCaptures.sourceId, sourceId))
1969
+ .innerJoin(
1970
+ schema.brainCaptureAudiences,
1971
+ eq(schema.brainCaptureAudiences.captureId, schema.brainRawCaptures.id),
1972
+ )
1973
+ .innerJoin(
1974
+ schema.brainAudiences,
1975
+ eq(schema.brainAudiences.id, schema.brainCaptureAudiences.audienceId),
1976
+ )
1977
+ .where(
1978
+ and(
1979
+ eq(schema.brainRawCaptures.sourceId, sourceId),
1980
+ eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
1981
+ inArray(schema.brainCaptureAudiences.audienceId, audienceIds),
1982
+ eq(schema.brainAudiences.kind, "org"),
1983
+ ),
1984
+ )
1666
1985
  .orderBy(desc(schema.brainRawCaptures.capturedAt))
1667
1986
  .limit(options.captureLimit);
1668
1987
 
@@ -1675,7 +1994,6 @@ async function githubRefsFromLinkedSources(
1675
1994
  capture.metadataJson,
1676
1995
  {},
1677
1996
  );
1678
- const raw = objectValue(metadata.raw);
1679
1997
  const linkedFrom = {
1680
1998
  sourceId,
1681
1999
  captureId: capture.id,
@@ -1684,7 +2002,6 @@ async function githubRefsFromLinkedSources(
1684
2002
  };
1685
2003
  const candidates = [
1686
2004
  capture.content,
1687
- typeof raw.text === "string" ? raw.text : "",
1688
2005
  typeof metadata.sourceUrl === "string" ? metadata.sourceUrl : "",
1689
2006
  typeof metadata.permalink === "string" ? metadata.permalink : "",
1690
2007
  ].join("\n");
@@ -1741,10 +2058,11 @@ async function syncFromConfiguredItems(
1741
2058
  const config = parseJson<Record<string, unknown>>(source.configJson, {});
1742
2059
  const items = transcriptItems(config);
1743
2060
  const captures = [];
2061
+ let sensitivityBlocked = 0;
1744
2062
 
1745
2063
  try {
1746
2064
  for (const item of items) {
1747
- const capture = await createCapture({
2065
+ const captureResult = await createConnectorCapture({
1748
2066
  sourceId: source.id,
1749
2067
  externalId: item.externalId,
1750
2068
  title: item.title,
@@ -1757,9 +2075,14 @@ async function syncFromConfiguredItems(
1757
2075
  syncRunId: runId,
1758
2076
  },
1759
2077
  });
1760
- captures.push(serializeCapture(capture));
2078
+ if (captureResult.capture)
2079
+ captures.push(serializeCapture(captureResult.capture));
2080
+ if (captureResult.blocked) sensitivityBlocked += 1;
1761
2081
  }
1762
- await finishRun(runId, "success", { capturesCreated: captures.length });
2082
+ await finishRun(runId, "success", {
2083
+ capturesCreated: captures.length,
2084
+ sensitivityBlocked,
2085
+ });
1763
2086
  await getDb()
1764
2087
  .update(schema.brainSources)
1765
2088
  .set({
@@ -1841,6 +2164,12 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1841
2164
  ],
1842
2165
  "slack",
1843
2166
  );
2167
+ const includePublicChannels = configuredBoolean(
2168
+ config,
2169
+ ["includePublicChannels"],
2170
+ false,
2171
+ "slack",
2172
+ );
1844
2173
  const limit = configuredNumber(config, ["historyLimit", "limit"], 15, {
1845
2174
  min: 1,
1846
2175
  max: 15,
@@ -1877,11 +2206,19 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1877
2206
  const captures = [];
1878
2207
  const stats: Record<string, unknown> = {
1879
2208
  configuredChannels: channelRefs.length,
2209
+ includePublicChannels,
2210
+ discoveredPublicChannels: 0,
2211
+ excludedPublicChannels: 0,
2212
+ eligibleChannels: 0,
1880
2213
  scannedChannels: 0,
1881
2214
  rejectedChannels: 0,
1882
2215
  missingChannels: 0,
1883
2216
  messagesSeen: 0,
1884
2217
  capturesCreated: 0,
2218
+ threadsFetched: 0,
2219
+ sensitivityBlocked: 0,
2220
+ publicChannelsJoined: 0,
2221
+ publicChannelsAlreadyJoined: 0,
1885
2222
  rateLimited: false,
1886
2223
  };
1887
2224
 
@@ -1892,15 +2229,17 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1892
2229
  "slack",
1893
2230
  config,
1894
2231
  );
1895
- if (!channelRefs.length) {
2232
+ if (!channelRefs.length && !includePublicChannels) {
1896
2233
  throw new Error(
1897
- "Slack source must configure channelIds, channels, or allowedChannels",
2234
+ "Slack source must configure channelIds, channels, allowedChannels, or includePublicChannels",
1898
2235
  );
1899
2236
  }
1900
2237
 
1901
- let permalinkCalls = 0;
1902
-
1903
- for (const channelRef of channelRefs.slice(0, maxChannels)) {
2238
+ const settings = await readBrainSettings();
2239
+ const publicExclusionPatterns =
2240
+ settings.publicChannelExclusionPatterns ?? [];
2241
+ const channelsById = new Map<string, SlackChannel>();
2242
+ for (const channelRef of channelRefs) {
1904
2243
  const channel = await resolveSlackChannel(token, channelRef);
1905
2244
  if (!channel) {
1906
2245
  stats.missingChannels = Number(stats.missingChannels) + 1;
@@ -1910,6 +2249,87 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1910
2249
  stats.rejectedChannels = Number(stats.rejectedChannels) + 1;
1911
2250
  continue;
1912
2251
  }
2252
+ const isExplicitId = /^[CG][A-Z0-9]+$/i.test(channelRef);
2253
+ if (channel.is_private && !isExplicitId) {
2254
+ stats.rejectedChannels = Number(stats.rejectedChannels) + 1;
2255
+ continue;
2256
+ }
2257
+ if (
2258
+ !channel.is_private &&
2259
+ matchesPublicChannelExclusion(channel, publicExclusionPatterns)
2260
+ ) {
2261
+ stats.excludedPublicChannels = Number(stats.excludedPublicChannels) + 1;
2262
+ continue;
2263
+ }
2264
+ channelsById.set(channel.id, channel);
2265
+ }
2266
+ if (includePublicChannels) {
2267
+ const discovered = await listPublicSlackChannels(
2268
+ token,
2269
+ publicExclusionPatterns,
2270
+ );
2271
+ stats.discoveredPublicChannels = discovered.channels.length;
2272
+ stats.excludedPublicChannels =
2273
+ Number(stats.excludedPublicChannels) + discovered.excluded;
2274
+ for (const channel of discovered.channels) {
2275
+ channelsById.set(channel.id, channel);
2276
+ }
2277
+ }
2278
+ const channels = [...channelsById.values()];
2279
+ stats.eligibleChannels = channels.length;
2280
+ const userEmailCache: SlackUserEmailCache = new Map();
2281
+
2282
+ const channelsToScan = includePublicChannels
2283
+ ? (() => {
2284
+ const ordered = [...channels].sort((left, right) =>
2285
+ left.id.localeCompare(right.id),
2286
+ );
2287
+ if (!ordered.length) return ordered;
2288
+ const offset =
2289
+ Math.max(0, cursor.publicChannelOffset ?? 0) % ordered.length;
2290
+ const rotated = [
2291
+ ...ordered.slice(offset),
2292
+ ...ordered.slice(0, offset),
2293
+ ];
2294
+ nextCursor.publicChannelOffset =
2295
+ (offset + Math.min(maxChannels, rotated.length)) % ordered.length;
2296
+ return rotated.slice(0, maxChannels);
2297
+ })()
2298
+ : channels.slice(0, maxChannels);
2299
+
2300
+ let permalinkCalls = 0;
2301
+
2302
+ for (const channel of channelsToScan) {
2303
+ const membership = await ensureSlackPublicChannelMembership(
2304
+ channel,
2305
+ (channelId) =>
2306
+ slackApi<SlackChannelJoinResponse>(
2307
+ token,
2308
+ "conversations.join",
2309
+ { channel: channelId },
2310
+ {
2311
+ httpMethod: "POST",
2312
+ toleratedErrors: ["already_in_channel"],
2313
+ },
2314
+ ),
2315
+ );
2316
+ if (membership === "joined") {
2317
+ stats.publicChannelsJoined = Number(stats.publicChannelsJoined) + 1;
2318
+ } else if (membership === "already_member") {
2319
+ stats.publicChannelsAlreadyJoined =
2320
+ Number(stats.publicChannelsAlreadyJoined) + 1;
2321
+ }
2322
+ const privateMemberEmails = channel.is_private
2323
+ ? await slackPrivateChannelMemberEmails(
2324
+ token,
2325
+ channel.id,
2326
+ userEmailCache,
2327
+ )
2328
+ : null;
2329
+ if (channel.is_private && !privateMemberEmails?.length) {
2330
+ stats.rejectedChannels = Number(stats.rejectedChannels) + 1;
2331
+ continue;
2332
+ }
1913
2333
 
1914
2334
  stats.scannedChannels = Number(stats.scannedChannels) + 1;
1915
2335
  const channelCursor = nextCursor.channels?.[channel.id] ?? {};
@@ -1939,13 +2359,7 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1939
2359
  "conversations.history",
1940
2360
  params,
1941
2361
  );
1942
- const messages = (data.messages ?? []).filter(
1943
- (message) =>
1944
- message.type === "message" &&
1945
- typeof message.text === "string" &&
1946
- message.text.trim() &&
1947
- typeof message.ts === "string",
1948
- );
2362
+ const messages = (data.messages ?? []).filter(isSlackTextMessage);
1949
2363
  stats.messagesSeen = Number(stats.messagesSeen) + messages.length;
1950
2364
 
1951
2365
  const newest = newestSlackTs(messages);
@@ -1953,42 +2367,42 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
1953
2367
  channelCursor.pendingLatestTs = newest;
1954
2368
  }
1955
2369
 
2370
+ const fetchedThreadTs = new Set<string>();
1956
2371
  for (const message of messages) {
2372
+ const threadTs = message.thread_ts ?? message.ts;
2373
+ if (!threadTs || fetchedThreadTs.has(threadTs)) continue;
2374
+ fetchedThreadTs.add(threadTs);
1957
2375
  let permalink: string | null = null;
1958
2376
  if (permalinkCalls < permalinkLimit) {
1959
- permalink = await slackPermalink(token, channel.id, message.ts);
2377
+ permalink = await slackPermalink(token, channel.id, threadTs);
1960
2378
  permalinkCalls += 1;
1961
2379
  }
1962
- const capture = await createCapture({
1963
- sourceId: source.id,
1964
- externalId: `slack:${channel.id}:${message.ts}`,
1965
- title: `#${channel.name ?? channel.id} message ${isoFromSlackTs(message.ts) ?? message.ts}`,
1966
- kind: "message",
1967
- content: normalizeSlackMessageContent(channel, message),
1968
- capturedAt: isoFromSlackTs(message.ts) ?? nowIso(),
1969
- metadata: {
1970
- provider: "slack",
1971
- connector: "slack",
1972
- syncRunId: runId,
1973
- channelId: channel.id,
1974
- channelName: channel.name,
1975
- ts: message.ts,
1976
- threadTs: message.thread_ts,
1977
- user: message.user,
1978
- username: message.username,
1979
- botId: message.bot_id,
1980
- sourceUrl: permalink,
1981
- permalink,
1982
- raw: message,
1983
- },
2380
+ const thread = await slackApi<SlackRepliesResponse>(
2381
+ token,
2382
+ "conversations.replies",
2383
+ { channel: channel.id, ts: threadTs, limit: 1_000 },
2384
+ );
2385
+ stats.threadsFetched = Number(stats.threadsFetched) + 1;
2386
+ const captureResult = await createSlackThreadCapture({
2387
+ source,
2388
+ channel,
2389
+ messages: thread.messages?.length ? thread.messages : [message],
2390
+ permalink,
2391
+ syncRunId: runId,
2392
+ memberEmails: privateMemberEmails,
1984
2393
  });
1985
- captures.push(serializeCapture(capture));
2394
+ if (captureResult.capture)
2395
+ captures.push(serializeCapture(captureResult.capture));
2396
+ if (captureResult.blocked) {
2397
+ stats.sensitivityBlocked = Number(stats.sensitivityBlocked) + 1;
2398
+ }
1986
2399
  }
1987
2400
 
1988
2401
  const nextPage = data.response_metadata?.next_cursor;
1989
2402
  if (data.has_more && nextPage) {
1990
2403
  channelCursor.pageCursor = nextPage;
1991
2404
  nextCursor.channels![channel.id] = channelCursor;
2405
+ if (page + 1 < pagesPerChannel) continue;
1992
2406
  break;
1993
2407
  }
1994
2408
 
@@ -2076,6 +2490,117 @@ async function syncSlack(source: SourceRow): Promise<ConnectorSyncResult> {
2076
2490
  }
2077
2491
  }
2078
2492
 
2493
+ export async function refreshSlackThreadCapture(
2494
+ source: SourceRow,
2495
+ payloadJson: string,
2496
+ ) {
2497
+ const payload = parseJson<Record<string, unknown>>(payloadJson, {});
2498
+ const channelId =
2499
+ typeof payload.channelId === "string" ? payload.channelId.trim() : "";
2500
+ const threadTs =
2501
+ typeof payload.threadTs === "string" ? payload.threadTs.trim() : "";
2502
+ if (!/^[CG][A-Z0-9]+$/i.test(channelId) || !slackTsFromDateish(threadTs)) {
2503
+ throw new Error("Slack thread refresh requires a channelId and threadTs");
2504
+ }
2505
+
2506
+ const config = parseJson<Record<string, unknown>>(source.configJson, {});
2507
+ const includePublicChannels = configuredBoolean(
2508
+ config,
2509
+ ["includePublicChannels"],
2510
+ false,
2511
+ "slack",
2512
+ );
2513
+ const explicitlyConfigured = configuredSlackChannelIds(config).has(channelId);
2514
+ if (
2515
+ !explicitlyConfigured &&
2516
+ !(includePublicChannels && channelId.startsWith("C"))
2517
+ ) {
2518
+ throw new Error(
2519
+ `Slack thread refresh rejected for channel ${channelId}: it is not in this source's public or explicit channel scope`,
2520
+ );
2521
+ }
2522
+ const runId = await createRun(source);
2523
+ try {
2524
+ const token = await requireConnectorCredential(
2525
+ "SLACK_BOT_TOKEN",
2526
+ "Slack",
2527
+ "slack",
2528
+ config,
2529
+ );
2530
+ const channel = await resolveSlackChannel(token, channelId);
2531
+ if (!channel || !isUsableSlackChannel(channel)) {
2532
+ throw new Error(`Slack channel ${channelId} is unavailable for refresh`);
2533
+ }
2534
+ if (
2535
+ !channel.is_private &&
2536
+ matchesPublicChannelExclusion(
2537
+ channel,
2538
+ (await readBrainSettings()).publicChannelExclusionPatterns ?? [],
2539
+ )
2540
+ ) {
2541
+ throw new Error(
2542
+ `Slack thread refresh rejected for channel ${channelId}: it matches a Brain public-channel exclusion`,
2543
+ );
2544
+ }
2545
+ const memberEmails = channel.is_private
2546
+ ? await slackPrivateChannelMemberEmails(token, channel.id, new Map())
2547
+ : null;
2548
+ if (channel.is_private && !memberEmails?.length) {
2549
+ throw new Error(
2550
+ `Slack private channel ${channel.id} has no resolvable member emails; refusing to refresh without an ACL`,
2551
+ );
2552
+ }
2553
+ const [thread, permalink] = await Promise.all([
2554
+ slackApi<SlackRepliesResponse>(token, "conversations.replies", {
2555
+ channel: channel.id,
2556
+ ts: threadTs,
2557
+ limit: 1_000,
2558
+ }),
2559
+ slackPermalink(token, channel.id, threadTs),
2560
+ ]);
2561
+ const captureResult = await createSlackThreadCapture({
2562
+ source,
2563
+ channel,
2564
+ messages: thread.messages ?? [],
2565
+ permalink,
2566
+ syncRunId: runId,
2567
+ memberEmails,
2568
+ });
2569
+ const stats = {
2570
+ channelId: channel.id,
2571
+ threadTs,
2572
+ threadsFetched: 1,
2573
+ capturesCreated: captureResult.capture ? 1 : 0,
2574
+ sensitivityBlocked: captureResult.blocked ? 1 : 0,
2575
+ };
2576
+ await finishRun(runId, "success", stats);
2577
+ await getDb()
2578
+ .update(schema.brainSources)
2579
+ .set({
2580
+ lastSyncedAt: nowIso(),
2581
+ lastError: null,
2582
+ status: "active",
2583
+ updatedAt: nowIso(),
2584
+ })
2585
+ .where(
2586
+ and(
2587
+ accessFilter(schema.brainSources, schema.brainSourceShares),
2588
+ eq(schema.brainSources.id, source.id),
2589
+ ),
2590
+ );
2591
+ return {
2592
+ runId,
2593
+ capture: captureResult.capture
2594
+ ? serializeCapture(captureResult.capture)
2595
+ : null,
2596
+ };
2597
+ } catch (error) {
2598
+ const message = error instanceof Error ? error.message : String(error);
2599
+ await finishRun(runId, "error", { capturesCreated: 0 }, message);
2600
+ throw error;
2601
+ }
2602
+ }
2603
+
2079
2604
  async function syncGranola(source: SourceRow): Promise<ConnectorSyncResult> {
2080
2605
  const config = parseJson<Record<string, unknown>>(source.configJson, {});
2081
2606
  if (isFixtureConfig(config) || transcriptItems(config).length > 0) {
@@ -2146,7 +2671,7 @@ async function syncGranola(source: SourceRow): Promise<ConnectorSyncResult> {
2146
2671
  ...listed,
2147
2672
  ...note,
2148
2673
  });
2149
- const capture = await createCapture({
2674
+ const captureResult = await createConnectorCapture({
2150
2675
  sourceId: source.id,
2151
2676
  externalId: normalized.externalId,
2152
2677
  title: normalized.title,
@@ -2159,7 +2684,11 @@ async function syncGranola(source: SourceRow): Promise<ConnectorSyncResult> {
2159
2684
  syncRunId: runId,
2160
2685
  },
2161
2686
  });
2162
- captures.push(serializeCapture(capture));
2687
+ if (captureResult.capture)
2688
+ captures.push(serializeCapture(captureResult.capture));
2689
+ if (captureResult.blocked) {
2690
+ stats.sensitivityBlocked = Number(stats.sensitivityBlocked ?? 0) + 1;
2691
+ }
2163
2692
 
2164
2693
  const candidateUpdatedAt = note.updated_at ?? listed.updated_at;
2165
2694
  if (
@@ -2449,7 +2978,7 @@ async function syncGitHub(source: SourceRow): Promise<ConnectorSyncResult> {
2449
2978
  ref.linkedFrom,
2450
2979
  );
2451
2980
  importedExternalIds.add(normalized.externalId);
2452
- const capture = await createCapture({
2981
+ const captureResult = await createConnectorCapture({
2453
2982
  sourceId: source.id,
2454
2983
  externalId: normalized.externalId,
2455
2984
  title: normalized.title,
@@ -2461,7 +2990,11 @@ async function syncGitHub(source: SourceRow): Promise<ConnectorSyncResult> {
2461
2990
  syncRunId: runId,
2462
2991
  },
2463
2992
  });
2464
- captures.push(serializeCapture(capture));
2993
+ if (captureResult.capture)
2994
+ captures.push(serializeCapture(captureResult.capture));
2995
+ if (captureResult.blocked) {
2996
+ stats.sensitivityBlocked = Number(stats.sensitivityBlocked ?? 0) + 1;
2997
+ }
2465
2998
  stats.itemsSeen = Number(stats.itemsSeen) + 1;
2466
2999
  stats.linkedRefsImported = Number(stats.linkedRefsImported) + 1;
2467
3000
  }
@@ -2511,7 +3044,7 @@ async function syncGitHub(source: SourceRow): Promise<ConnectorSyncResult> {
2511
3044
 
2512
3045
  const normalized = normalizeGitHubIssue(repo, item, context);
2513
3046
  importedExternalIds.add(normalized.externalId);
2514
- const capture = await createCapture({
3047
+ const captureResult = await createConnectorCapture({
2515
3048
  sourceId: source.id,
2516
3049
  externalId: normalized.externalId,
2517
3050
  title: normalized.title,
@@ -2523,7 +3056,11 @@ async function syncGitHub(source: SourceRow): Promise<ConnectorSyncResult> {
2523
3056
  syncRunId: runId,
2524
3057
  },
2525
3058
  });
2526
- captures.push(serializeCapture(capture));
3059
+ if (captureResult.capture)
3060
+ captures.push(serializeCapture(captureResult.capture));
3061
+ if (captureResult.blocked) {
3062
+ stats.sensitivityBlocked = Number(stats.sensitivityBlocked ?? 0) + 1;
3063
+ }
2527
3064
 
2528
3065
  const candidateUpdatedAt = item.updated_at;
2529
3066
  if (