@agent-native/core 0.79.16 → 0.79.18

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 (344) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/docs/content/file-uploads.md +11 -4
  4. package/corpus/core/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
  5. package/corpus/core/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
  6. package/corpus/core/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
  7. package/corpus/core/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
  8. package/corpus/core/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
  9. package/corpus/core/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
  10. package/corpus/core/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
  11. package/corpus/core/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
  12. package/corpus/core/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
  13. package/corpus/core/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
  14. package/corpus/core/docs/content/multi-app-workspace.md +3 -3
  15. package/corpus/core/docs/content/template-clips.md +14 -5
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/agent/engine/builder-engine.ts +8 -3
  18. package/corpus/core/src/cli/mcp.ts +11 -6
  19. package/corpus/core/src/client/AssistantChat.tsx +6 -2
  20. package/corpus/core/src/client/chat/markdown-renderer.tsx +1 -0
  21. package/corpus/core/src/client/index.ts +7 -1
  22. package/corpus/core/src/client/org/OrgSwitcher.tsx +5 -5
  23. package/corpus/core/src/client/org/TeamPage.tsx +11 -3
  24. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +216 -0
  25. package/corpus/core/src/client/settings/index.ts +5 -0
  26. package/corpus/core/src/file-upload/index.ts +1 -0
  27. package/corpus/core/src/file-upload/registry.ts +22 -6
  28. package/corpus/core/src/file-upload/types.ts +6 -1
  29. package/corpus/core/src/integrations/adapters/email.ts +26 -18
  30. package/corpus/core/src/integrations/adapters/slack.ts +8 -7
  31. package/corpus/core/src/integrations/adapters/telegram.ts +9 -11
  32. package/corpus/core/src/integrations/adapters/whatsapp.ts +12 -9
  33. package/corpus/core/src/integrations/plugin.ts +79 -13
  34. package/corpus/core/src/onboarding/default-steps.ts +6 -3
  35. package/corpus/core/src/onboarding/types.ts +1 -1
  36. package/corpus/core/src/org/handlers.ts +1 -1
  37. package/corpus/core/src/server/agent-chat-plugin.ts +4 -7
  38. package/corpus/core/src/server/better-auth-instance.ts +19 -62
  39. package/corpus/core/src/server/core-routes-plugin.ts +93 -180
  40. package/corpus/core/src/server/create-server.ts +55 -116
  41. package/corpus/core/src/server/email-actions.ts +3 -6
  42. package/corpus/core/src/server/email.ts +52 -17
  43. package/corpus/core/src/server/index.ts +2 -1
  44. package/corpus/core/src/server/scoped-key-storage.ts +223 -0
  45. package/corpus/core/src/shared/agent-env.ts +3 -3
  46. package/corpus/core/src/sharing/actions/share-resource.ts +1 -1
  47. package/corpus/core/src/styles/agent-native.css +55 -3
  48. package/corpus/core/src/templates/workspace-root/AGENTS.md +3 -2
  49. package/corpus/core/src/templates/workspace-root/README.md +1 -1
  50. package/corpus/core/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  51. package/corpus/core/src/terminal/pty-server.ts +3 -38
  52. package/corpus/core/src/triggers/dispatcher.ts +1 -2
  53. package/corpus/templates/.retired/issues/package.json +7 -0
  54. package/corpus/templates/.retired/meeting-notes/package.json +7 -0
  55. package/corpus/templates/.retired/recruiting/package.json +7 -0
  56. package/corpus/templates/.retired/scheduling/package.json +7 -0
  57. package/corpus/templates/.retired/voice/package.json +7 -0
  58. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  59. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +0 -17
  60. package/corpus/templates/analytics/app/i18n/zh-TW.ts +7 -1
  61. package/corpus/templates/analytics/app/i18n-data.ts +71 -2
  62. package/corpus/templates/analytics/app/pages/Settings.tsx +120 -73
  63. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +0 -11
  64. package/corpus/templates/analytics/app/routes/catalog.tsx +33 -176
  65. package/corpus/templates/analytics/app/routes/team.tsx +3 -2
  66. package/corpus/templates/analytics/changelog/2026-06-26-dashboard-templates-are-tucked-into-settings-and-show-simple.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-06-26-the-sessions-page-shows-a-single-page-title-instead-of-repea.md +6 -0
  68. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +37 -28
  69. package/corpus/templates/assets/app/routes/settings.tsx +65 -43
  70. package/corpus/templates/assets/app/routes/team.tsx +2 -10
  71. package/corpus/templates/brain/app/routes/settings.tsx +390 -358
  72. package/corpus/templates/brain/app/routes/team.tsx +2 -8
  73. package/corpus/templates/calendar/actions/db-connect.ts +5 -29
  74. package/corpus/templates/calendar/app/components/CloudUpgrade.tsx +8 -51
  75. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +2 -1
  76. package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +4 -2
  77. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +0 -2
  78. package/corpus/templates/calendar/app/pages/Settings.tsx +304 -271
  79. package/corpus/templates/calendar/app/routes/_app.team.tsx +2 -17
  80. package/corpus/templates/calendar/server/handlers/google-auth.ts +22 -9
  81. package/corpus/templates/calendar/server/lib/booking-emails.ts +1 -1
  82. package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +1 -1
  83. package/corpus/templates/calendar/server/lib/google-calendar.ts +48 -28
  84. package/corpus/templates/chat/app/components/layout/Header.tsx +0 -1
  85. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -2
  86. package/corpus/templates/chat/app/routes/settings.tsx +54 -54
  87. package/corpus/templates/chat/app/routes/team.tsx +2 -10
  88. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +7 -2
  89. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +1 -1
  90. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +7 -0
  91. package/corpus/templates/clips/AGENTS.md +8 -0
  92. package/corpus/templates/clips/actions/invite-member.ts +1 -1
  93. package/corpus/templates/clips/actions/request-transcript.ts +1 -1
  94. package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
  95. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -0
  96. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +629 -643
  97. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +219 -155
  98. package/corpus/templates/clips/changelog/2026-06-26-long-desktop-recordings-are-checkpointed-so-macos-finalizati.md +6 -0
  99. package/corpus/templates/clips/changelog/2026-06-26-non-mac-desktop-recordings-wait-for-upload-transcription.md +6 -0
  100. package/corpus/templates/clips/changelog/2026-06-26-s3-compatible-storage-keys-now-save-securely-from-clips-sett.md +6 -0
  101. package/corpus/templates/clips/changelog/2026-06-26-shared-clip-links-guide-agents-through-processing-transcripts.md +6 -0
  102. package/corpus/templates/clips/changelog/2026-06-26-the-clip-details-sidebar-now-collapses-into-a-slide-out-pane.md +6 -0
  103. package/corpus/templates/clips/changelog/2026-06-26-the-clips-library-now-matches-the-shared-recessed-shell-styl.md +6 -0
  104. package/corpus/templates/clips/desktop/src/lib/recorder.ts +55 -3
  105. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +217 -5
  106. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
  107. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +176 -5
  108. package/corpus/templates/clips/server/lib/public-agent-context.ts +31 -0
  109. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +60 -14
  110. package/corpus/templates/clips/server/lib/video-storage.ts +37 -9
  111. package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +5 -0
  112. package/corpus/templates/content/app/components/layout/Header.tsx +0 -1
  113. package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +1 -0
  114. package/corpus/templates/content/app/routes/_app.settings.tsx +54 -55
  115. package/corpus/templates/content/app/routes/_app.team.tsx +2 -28
  116. package/corpus/templates/content/server/lib/notion.ts +43 -11
  117. package/corpus/templates/content/server/routes/api/notion/auth-url.get.ts +2 -2
  118. package/corpus/templates/content/server/routes/api/notion/status.get.ts +3 -3
  119. package/corpus/templates/design/app/routes/settings.tsx +60 -38
  120. package/corpus/templates/design/app/routes/team.tsx +2 -10
  121. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +2 -2
  122. package/corpus/templates/dispatch/app/routes/settings.tsx +70 -55
  123. package/corpus/templates/dispatch/app/routes/team.tsx +5 -1
  124. package/corpus/templates/forms/actions/db-connect.ts +7 -36
  125. package/corpus/templates/forms/actions/navigate.ts +2 -2
  126. package/corpus/templates/forms/app/components/CloudUpgrade.tsx +8 -47
  127. package/corpus/templates/forms/app/components/layout/Header.tsx +0 -1
  128. package/corpus/templates/forms/app/components/layout/Layout.tsx +42 -25
  129. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +35 -76
  130. package/corpus/templates/forms/app/global.css +9 -25
  131. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +4 -4
  132. package/corpus/templates/forms/app/i18n/ar-SA.ts +1 -0
  133. package/corpus/templates/forms/app/i18n/de-DE.ts +1 -0
  134. package/corpus/templates/forms/app/i18n/en-US.ts +1 -0
  135. package/corpus/templates/forms/app/i18n/es-ES.ts +1 -0
  136. package/corpus/templates/forms/app/i18n/fr-FR.ts +1 -0
  137. package/corpus/templates/forms/app/i18n/hi-IN.ts +1 -0
  138. package/corpus/templates/forms/app/i18n/ja-JP.ts +1 -0
  139. package/corpus/templates/forms/app/i18n/ko-KR.ts +1 -0
  140. package/corpus/templates/forms/app/i18n/pt-BR.ts +1 -0
  141. package/corpus/templates/forms/app/i18n/zh-CN.ts +1 -0
  142. package/corpus/templates/forms/app/i18n/zh-TW.ts +1 -0
  143. package/corpus/templates/forms/app/lib/route-prewarm.ts +9 -8
  144. package/corpus/templates/forms/app/pages/AskPage.tsx +70 -0
  145. package/corpus/templates/forms/app/pages/FormsListPage.tsx +74 -81
  146. package/corpus/templates/forms/app/root.tsx +1 -1
  147. package/corpus/templates/forms/app/routes/_app.ask.tsx +14 -0
  148. package/corpus/templates/forms/app/routes/_app.settings.tsx +54 -54
  149. package/corpus/templates/forms/app/routes/_app.team.tsx +2 -9
  150. package/corpus/templates/forms/app/routes/_index.tsx +21 -130
  151. package/corpus/templates/forms/changelog/2026-06-26-ask-forms-opens-as-a-standard-app-tab-with-a-simpler-sidebar.md +6 -0
  152. package/corpus/templates/forms/changelog/2026-06-26-forms-are-shown-in-a-tighter-row-list-with-a-denser-sidebar.md +6 -0
  153. package/corpus/templates/forms/shared/navigation.ts +4 -2
  154. package/corpus/templates/macros/app/routes/settings.tsx +54 -54
  155. package/corpus/templates/macros/app/routes/team.tsx +2 -10
  156. package/corpus/templates/mail/actions/helpers.ts +2 -2
  157. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +2 -1
  158. package/corpus/templates/mail/app/pages/SettingsPage.tsx +5 -5
  159. package/corpus/templates/mail/app/routes/team.tsx +3 -2
  160. package/corpus/templates/mail/server/handlers/emails.ts +3 -2
  161. package/corpus/templates/mail/server/handlers/google-auth.ts +19 -12
  162. package/corpus/templates/mail/server/lib/automation-engine.ts +3 -2
  163. package/corpus/templates/mail/server/lib/email-state.ts +2 -3
  164. package/corpus/templates/mail/server/lib/google-auth.ts +28 -11
  165. package/corpus/templates/mail/server/lib/jobs.ts +3 -2
  166. package/corpus/templates/mail/server/onboarding.ts +9 -6
  167. package/corpus/templates/mail/server/routes/api/attachments.get.ts +3 -3
  168. package/corpus/templates/plan/actions/request-plan-access.ts +1 -1
  169. package/corpus/templates/plan/app/components/layout/Header.tsx +0 -1
  170. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +4 -2
  171. package/corpus/templates/plan/app/lib/route-prewarm.ts +0 -4
  172. package/corpus/templates/plan/app/routes/settings.tsx +75 -73
  173. package/corpus/templates/plan/app/routes/team.tsx +2 -8
  174. package/corpus/templates/plan/server/lib/comment-notifications.ts +1 -1
  175. package/corpus/templates/slides/actions/check-image-gen.ts +2 -1
  176. package/corpus/templates/slides/actions/db-connect.ts +7 -45
  177. package/corpus/templates/slides/actions/edit-image.ts +6 -3
  178. package/corpus/templates/slides/actions/generate-image-api.ts +1 -1
  179. package/corpus/templates/slides/actions/generate-image.ts +2 -2
  180. package/corpus/templates/slides/actions/generate-slides-ai.ts +5 -3
  181. package/corpus/templates/slides/actions/image-gen-status.ts +4 -3
  182. package/corpus/templates/slides/actions/image-search.ts +6 -3
  183. package/corpus/templates/slides/actions/search-images.ts +6 -3
  184. package/corpus/templates/slides/app/components/CloudUpgrade.tsx +8 -51
  185. package/corpus/templates/slides/app/components/layout/Header.tsx +0 -1
  186. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -2
  187. package/corpus/templates/slides/app/routes/settings.tsx +54 -54
  188. package/corpus/templates/slides/app/routes/team.tsx +2 -12
  189. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +9 -9
  190. package/corpus/templates/slides/server/handlers/image-gen.ts +5 -1
  191. package/corpus/templates/slides/server/handlers/image-providers/gemini.ts +9 -1
  192. package/corpus/templates/slides/server/handlers/image-providers/index.ts +18 -8
  193. package/corpus/templates/slides/server/handlers/image-providers/openai.ts +10 -4
  194. package/corpus/templates/slides/server/handlers/image-providers/types.ts +1 -0
  195. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +59 -34
  196. package/corpus/templates/slides/server/register-secrets.ts +60 -0
  197. package/corpus/templates/videos/actions/db-connect.ts +7 -45
  198. package/corpus/templates/videos/app/components/CloudUpgrade.tsx +8 -51
  199. package/corpus/templates/videos/app/components/Sidebar.tsx +1 -9
  200. package/corpus/templates/videos/app/components/layout/Header.tsx +0 -1
  201. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -3
  202. package/corpus/templates/videos/app/routes/settings.tsx +54 -54
  203. package/corpus/templates/videos/app/routes/team.tsx +2 -8
  204. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  205. package/dist/agent/engine/builder-engine.js +8 -3
  206. package/dist/agent/engine/builder-engine.js.map +1 -1
  207. package/dist/cli/mcp.d.ts.map +1 -1
  208. package/dist/cli/mcp.js +3 -4
  209. package/dist/cli/mcp.js.map +1 -1
  210. package/dist/client/AssistantChat.d.ts.map +1 -1
  211. package/dist/client/AssistantChat.js +6 -2
  212. package/dist/client/AssistantChat.js.map +1 -1
  213. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  214. package/dist/client/chat/markdown-renderer.js +1 -0
  215. package/dist/client/chat/markdown-renderer.js.map +1 -1
  216. package/dist/client/index.d.ts +1 -1
  217. package/dist/client/index.d.ts.map +1 -1
  218. package/dist/client/index.js +1 -1
  219. package/dist/client/index.js.map +1 -1
  220. package/dist/client/org/OrgSwitcher.d.ts +3 -3
  221. package/dist/client/org/OrgSwitcher.js +2 -2
  222. package/dist/client/org/OrgSwitcher.js.map +1 -1
  223. package/dist/client/org/TeamPage.d.ts +6 -1
  224. package/dist/client/org/TeamPage.d.ts.map +1 -1
  225. package/dist/client/org/TeamPage.js +2 -2
  226. package/dist/client/org/TeamPage.js.map +1 -1
  227. package/dist/client/settings/SettingsTabsPage.d.ts +27 -0
  228. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -0
  229. package/dist/client/settings/SettingsTabsPage.js +108 -0
  230. package/dist/client/settings/SettingsTabsPage.js.map +1 -0
  231. package/dist/client/settings/index.d.ts +1 -0
  232. package/dist/client/settings/index.d.ts.map +1 -1
  233. package/dist/client/settings/index.js +1 -0
  234. package/dist/client/settings/index.js.map +1 -1
  235. package/dist/collab/awareness.d.ts +2 -2
  236. package/dist/collab/awareness.d.ts.map +1 -1
  237. package/dist/collab/routes.d.ts +1 -1
  238. package/dist/file-upload/index.d.ts +1 -1
  239. package/dist/file-upload/index.d.ts.map +1 -1
  240. package/dist/file-upload/index.js +1 -1
  241. package/dist/file-upload/index.js.map +1 -1
  242. package/dist/file-upload/registry.d.ts +1 -0
  243. package/dist/file-upload/registry.d.ts.map +1 -1
  244. package/dist/file-upload/registry.js +24 -6
  245. package/dist/file-upload/registry.js.map +1 -1
  246. package/dist/file-upload/types.d.ts +6 -1
  247. package/dist/file-upload/types.d.ts.map +1 -1
  248. package/dist/file-upload/types.js.map +1 -1
  249. package/dist/integrations/adapters/email.d.ts.map +1 -1
  250. package/dist/integrations/adapters/email.js +18 -18
  251. package/dist/integrations/adapters/email.js.map +1 -1
  252. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  253. package/dist/integrations/adapters/slack.js +8 -7
  254. package/dist/integrations/adapters/slack.js.map +1 -1
  255. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  256. package/dist/integrations/adapters/telegram.js +9 -9
  257. package/dist/integrations/adapters/telegram.js.map +1 -1
  258. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  259. package/dist/integrations/adapters/whatsapp.js +10 -9
  260. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  261. package/dist/integrations/plugin.d.ts.map +1 -1
  262. package/dist/integrations/plugin.js +47 -13
  263. package/dist/integrations/plugin.js.map +1 -1
  264. package/dist/notifications/routes.d.ts +1 -1
  265. package/dist/onboarding/default-steps.d.ts.map +1 -1
  266. package/dist/onboarding/default-steps.js +6 -5
  267. package/dist/onboarding/default-steps.js.map +1 -1
  268. package/dist/onboarding/types.d.ts +1 -1
  269. package/dist/onboarding/types.d.ts.map +1 -1
  270. package/dist/onboarding/types.js.map +1 -1
  271. package/dist/org/handlers.js +1 -1
  272. package/dist/org/handlers.js.map +1 -1
  273. package/dist/progress/routes.d.ts +1 -1
  274. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  275. package/dist/server/agent-chat-plugin.js +4 -6
  276. package/dist/server/agent-chat-plugin.js.map +1 -1
  277. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  278. package/dist/server/better-auth-instance.d.ts.map +1 -1
  279. package/dist/server/better-auth-instance.js +17 -56
  280. package/dist/server/better-auth-instance.js.map +1 -1
  281. package/dist/server/core-routes-plugin.d.ts +1 -1
  282. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  283. package/dist/server/core-routes-plugin.js +67 -153
  284. package/dist/server/core-routes-plugin.js.map +1 -1
  285. package/dist/server/create-server.d.ts +3 -9
  286. package/dist/server/create-server.d.ts.map +1 -1
  287. package/dist/server/create-server.js +37 -91
  288. package/dist/server/create-server.js.map +1 -1
  289. package/dist/server/email-actions.d.ts +2 -3
  290. package/dist/server/email-actions.d.ts.map +1 -1
  291. package/dist/server/email-actions.js +3 -6
  292. package/dist/server/email-actions.js.map +1 -1
  293. package/dist/server/email.d.ts +3 -3
  294. package/dist/server/email.d.ts.map +1 -1
  295. package/dist/server/email.js +40 -19
  296. package/dist/server/email.js.map +1 -1
  297. package/dist/server/index.d.ts +3 -3
  298. package/dist/server/index.d.ts.map +1 -1
  299. package/dist/server/index.js +3 -3
  300. package/dist/server/index.js.map +1 -1
  301. package/dist/server/scoped-key-storage.d.ts +22 -0
  302. package/dist/server/scoped-key-storage.d.ts.map +1 -0
  303. package/dist/server/scoped-key-storage.js +152 -0
  304. package/dist/server/scoped-key-storage.js.map +1 -0
  305. package/dist/shared/agent-env.d.ts +3 -3
  306. package/dist/shared/agent-env.d.ts.map +1 -1
  307. package/dist/shared/agent-env.js +3 -3
  308. package/dist/shared/agent-env.js.map +1 -1
  309. package/dist/sharing/actions/share-resource.js +1 -1
  310. package/dist/sharing/actions/share-resource.js.map +1 -1
  311. package/dist/styles/agent-native.css +55 -3
  312. package/dist/templates/workspace-root/AGENTS.md +3 -2
  313. package/dist/templates/workspace-root/README.md +1 -1
  314. package/dist/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  315. package/dist/terminal/pty-server.d.ts.map +1 -1
  316. package/dist/terminal/pty-server.js +3 -36
  317. package/dist/terminal/pty-server.js.map +1 -1
  318. package/dist/triggers/dispatcher.js +1 -1
  319. package/dist/triggers/dispatcher.js.map +1 -1
  320. package/docs/content/file-uploads.md +11 -4
  321. package/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
  322. package/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
  323. package/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
  324. package/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
  325. package/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
  326. package/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
  327. package/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
  328. package/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
  329. package/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
  330. package/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
  331. package/docs/content/multi-app-workspace.md +3 -3
  332. package/docs/content/template-clips.md +14 -5
  333. package/package.json +1 -1
  334. package/src/templates/workspace-root/AGENTS.md +3 -2
  335. package/src/templates/workspace-root/README.md +1 -1
  336. package/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  337. package/corpus/core/src/server/env-var-writes.ts +0 -16
  338. package/corpus/templates/analytics/app/pages/Team.tsx +0 -9
  339. package/corpus/templates/calendar/app/pages/Team.tsx +0 -12
  340. package/corpus/templates/mail/app/pages/Team.tsx +0 -17
  341. package/dist/server/env-var-writes.d.ts +0 -10
  342. package/dist/server/env-var-writes.d.ts.map +0 -1
  343. package/dist/server/env-var-writes.js +0 -18
  344. package/dist/server/env-var-writes.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/telegram.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,aAAa,CAAC;AAuBrB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAwQjD"}
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/telegram.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,aAAa,CAAC;AAuBrB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAqQjD"}
@@ -1,4 +1,5 @@
1
1
  import { getHeader } from "h3";
2
+ import { resolveSecret } from "../../server/credential-provider.js";
2
3
  import { readBody } from "../../server/h3-helpers.js";
3
4
  /** Telegram's max message length */
4
5
  const TELEGRAM_MAX_LENGTH = 4096;
@@ -67,7 +68,7 @@ export function telegramAdapter() {
67
68
  return { handled: false };
68
69
  },
69
70
  async verifyWebhook(event) {
70
- const secret = process.env.TELEGRAM_WEBHOOK_SECRET;
71
+ const secret = await resolveSecret("TELEGRAM_WEBHOOK_SECRET");
71
72
  if (!secret) {
72
73
  if (shouldRefuseWhenSecretMissing()) {
73
74
  if (!_telegramUnverifiedWarned) {
@@ -82,7 +83,7 @@ export function telegramAdapter() {
82
83
  console.warn("[telegram] TELEGRAM_WEBHOOK_SECRET not set — accepting webhook without verification (dev mode)");
83
84
  }
84
85
  // Dev mode: still require the bot token to be configured at all.
85
- return !!process.env.TELEGRAM_BOT_TOKEN;
86
+ return !!(await resolveSecret("TELEGRAM_BOT_TOKEN"));
86
87
  }
87
88
  const headerSecret = getHeader(event, "x-telegram-bot-api-secret-token");
88
89
  if (!headerSecret)
@@ -135,7 +136,7 @@ export function telegramAdapter() {
135
136
  };
136
137
  },
137
138
  async sendResponse(message, context) {
138
- const token = process.env.TELEGRAM_BOT_TOKEN;
139
+ const token = await resolveSecret("TELEGRAM_BOT_TOKEN");
139
140
  if (!token) {
140
141
  console.error("[telegram] TELEGRAM_BOT_TOKEN not configured");
141
142
  return;
@@ -177,7 +178,7 @@ export function telegramAdapter() {
177
178
  }
178
179
  },
179
180
  async sendMessageToTarget(message, target) {
180
- const token = process.env.TELEGRAM_BOT_TOKEN;
181
+ const token = await resolveSecret("TELEGRAM_BOT_TOKEN");
181
182
  if (!token) {
182
183
  console.error("[telegram] TELEGRAM_BOT_TOKEN not configured");
183
184
  return;
@@ -216,11 +217,12 @@ export function telegramAdapter() {
216
217
  return { text: normalized, platformContext: { parse_mode: "Markdown" } };
217
218
  },
218
219
  async getStatus(_baseUrl) {
219
- const hasToken = !!process.env.TELEGRAM_BOT_TOKEN;
220
+ const token = await resolveSecret("TELEGRAM_BOT_TOKEN");
221
+ const hasToken = !!token;
220
222
  let botName;
221
223
  if (hasToken) {
222
224
  try {
223
- const res = await fetch(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/getMe`);
225
+ const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);
224
226
  const data = (await res.json());
225
227
  if (data.ok) {
226
228
  botName = data.result?.username;
@@ -237,9 +239,7 @@ export function telegramAdapter() {
237
239
  hasToken,
238
240
  botUsername: botName,
239
241
  },
240
- error: !hasToken
241
- ? "Set TELEGRAM_BOT_TOKEN in your environment"
242
- : undefined,
242
+ error: !hasToken ? "Save TELEGRAM_BOT_TOKEN in settings" : undefined,
243
243
  };
244
244
  },
245
245
  };
@@ -1 +1 @@
1
- {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../src/integrations/adapters/telegram.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { readBody } from \"../../server/h3-helpers.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\n\n/** Telegram's max message length */\nconst TELEGRAM_MAX_LENGTH = 4096;\n\n/**\n * One-shot warning flag — log once per process when accepting unverified\n * webhooks (M6 in the webhook security audit).\n */\nlet _telegramUnverifiedWarned = false;\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose secret is\n * unset — otherwise an attacker can drive the agent loop with arbitrary\n * messages (C2 in the webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create a Telegram platform adapter.\n *\n * Required env vars:\n * - TELEGRAM_BOT_TOKEN — Bot token from @BotFather\n *\n * Optional env vars:\n * - TELEGRAM_WEBHOOK_SECRET — Secret token for webhook verification\n */\nexport function telegramAdapter(): PlatformAdapter {\n return {\n platform: \"telegram\",\n label: \"Telegram\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"TELEGRAM_BOT_TOKEN\",\n label: \"Telegram Bot Token\",\n required: true,\n helpText:\n \"From @BotFather after `/newbot` — the long token Telegram gives you (looks like `123456:ABC-DEF...`).\",\n },\n {\n key: \"TELEGRAM_WEBHOOK_SECRET\",\n label: \"Telegram Webhook Secret\",\n required: false,\n helpText:\n \"Optional. Any random string — Telegram will echo it on every webhook so dispatch can verify the request came from Telegram.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Pre-read the raw body once and cache on the event context. h3 v2's\n // request body stream can only be consumed once; without this, the\n // later `parseIncomingMessage` call throws \"Body has already been read\"\n // because something upstream (signature check, dedupe, etc.) drains it.\n // Mirrors the Slack adapter's pattern.\n try {\n if (!event.context.__rawBody) {\n const body = await readBody(event);\n event.context.__rawBody = body;\n }\n } catch {\n // If we can't pre-read, parseIncomingMessage will surface the error\n }\n // Telegram has no challenge; we never short-circuit.\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const secret = process.env.TELEGRAM_WEBHOOK_SECRET;\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_telegramUnverifiedWarned) {\n _telegramUnverifiedWarned = true;\n console.error(\n \"[telegram] TELEGRAM_WEBHOOK_SECRET not set — refusing webhook in production. \" +\n \"Set TELEGRAM_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_telegramUnverifiedWarned) {\n _telegramUnverifiedWarned = true;\n console.warn(\n \"[telegram] TELEGRAM_WEBHOOK_SECRET not set — accepting webhook without verification (dev mode)\",\n );\n }\n // Dev mode: still require the bot token to be configured at all.\n return !!process.env.TELEGRAM_BOT_TOKEN;\n }\n\n const headerSecret = getHeader(event, \"x-telegram-bot-api-secret-token\");\n if (!headerSecret) return false;\n\n // Timing-safe comparison\n try {\n const crypto = await import(\"node:crypto\");\n return crypto.timingSafeEqual(\n Buffer.from(secret),\n Buffer.from(headerSecret),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n // Use the pre-cached raw body if available (set by handleVerification).\n // Falls back to readBody for paths that bypass handleVerification.\n const body = (event.context.__rawBody as any) ?? (await readBody(event));\n if (!body) return null;\n\n // Handle regular messages\n const message = body.message || body.edited_message;\n if (!message) return null;\n\n // Only process text messages\n const text = message.text?.trim();\n if (!text) return null;\n\n // Ignore bot commands that we don't handle (e.g., /start is fine)\n // Remove /start command prefix if present\n const cleanText =\n text === \"/start\"\n ? \"Hello! I'm ready to chat.\"\n : text.replace(/^\\/\\w+\\s*/, \"\").trim() || text;\n\n const chat = message.chat;\n const from = message.from;\n\n return {\n platform: \"telegram\",\n externalThreadId: String(chat.id),\n text: cleanText,\n senderName:\n from?.first_name + (from?.last_name ? ` ${from.last_name}` : \"\"),\n senderId: String(from?.id),\n platformContext: {\n chatId: chat.id,\n chatType: chat.type,\n messageId: message.message_id,\n rawText: text,\n fromId: from?.id,\n fromUsername: from?.username,\n },\n timestamp: message.date * 1000,\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const token = process.env.TELEGRAM_BOT_TOKEN;\n if (!token) {\n console.error(\"[telegram] TELEGRAM_BOT_TOKEN not configured\");\n return;\n }\n\n const chatId = context.platformContext.chatId;\n const chunks = splitMessage(message.text, TELEGRAM_MAX_LENGTH);\n\n for (const chunk of chunks) {\n try {\n const res = await fetch(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: chatId,\n text: chunk,\n parse_mode: \"Markdown\",\n }),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n description?: string;\n };\n if (!data.ok) {\n // Retry without Markdown if parsing fails\n if (data.description?.includes(\"parse\")) {\n await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: chatId,\n text: chunk,\n }),\n });\n } else {\n console.error(\"[telegram] sendMessage error:\", data.description);\n }\n }\n } catch (err) {\n console.error(\"[telegram] Failed to send message:\", err);\n }\n }\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const token = process.env.TELEGRAM_BOT_TOKEN;\n if (!token) {\n console.error(\"[telegram] TELEGRAM_BOT_TOKEN not configured\");\n return;\n }\n\n const chunks = splitMessage(message.text, TELEGRAM_MAX_LENGTH);\n for (const chunk of chunks) {\n const body: Record<string, unknown> = {\n chat_id: target.destination,\n text: chunk,\n };\n if (target.threadRef) {\n body.message_thread_id = target.threadRef;\n }\n\n try {\n const res = await fetch(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n description?: string;\n };\n if (!data.ok) {\n throw new Error(data.description || \"sendMessage failed\");\n }\n } catch (err) {\n console.error(\"[telegram] Failed to send proactive message:\", err);\n throw err;\n }\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n // Telegram's legacy Markdown uses single asterisks for bold, not double.\n // `[text](url)` is already supported natively.\n // 's' flag (dotAll) so `.` matches newlines — bold text can span lines.\n const normalized = text.replace(/\\*\\*(.+?)\\*\\*/gs, \"*$1*\");\n return { text: normalized, platformContext: { parse_mode: \"Markdown\" } };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const hasToken = !!process.env.TELEGRAM_BOT_TOKEN;\n\n let botName: string | undefined;\n if (hasToken) {\n try {\n const res = await fetch(\n `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/getMe`,\n );\n const data = (await res.json()) as {\n ok: boolean;\n result?: { username?: string };\n };\n if (data.ok) {\n botName = data.result?.username;\n }\n } catch {}\n }\n\n return {\n platform: \"telegram\",\n label: \"Telegram\",\n enabled: false, // overridden by plugin\n configured: hasToken,\n details: {\n hasToken,\n botUsername: botName,\n },\n error: !hasToken\n ? \"Set TELEGRAM_BOT_TOKEN in your environment\"\n : undefined,\n };\n },\n };\n}\n\n/** Split a message into chunks that fit within the platform's limit */\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../src/integrations/adapters/telegram.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getHeader } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport { readBody } from \"../../server/h3-helpers.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n OutboundTarget,\n} from \"../types.js\";\n\n/** Telegram's max message length */\nconst TELEGRAM_MAX_LENGTH = 4096;\n\n/**\n * One-shot warning flag — log once per process when accepting unverified\n * webhooks (M6 in the webhook security audit).\n */\nlet _telegramUnverifiedWarned = false;\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose secret is\n * unset — otherwise an attacker can drive the agent loop with arbitrary\n * messages (C2 in the webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create a Telegram platform adapter.\n *\n * Required env vars:\n * - TELEGRAM_BOT_TOKEN — Bot token from @BotFather\n *\n * Optional env vars:\n * - TELEGRAM_WEBHOOK_SECRET — Secret token for webhook verification\n */\nexport function telegramAdapter(): PlatformAdapter {\n return {\n platform: \"telegram\",\n label: \"Telegram\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"TELEGRAM_BOT_TOKEN\",\n label: \"Telegram Bot Token\",\n required: true,\n helpText:\n \"From @BotFather after `/newbot` — the long token Telegram gives you (looks like `123456:ABC-DEF...`).\",\n },\n {\n key: \"TELEGRAM_WEBHOOK_SECRET\",\n label: \"Telegram Webhook Secret\",\n required: false,\n helpText:\n \"Optional. Any random string — Telegram will echo it on every webhook so dispatch can verify the request came from Telegram.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n // Pre-read the raw body once and cache on the event context. h3 v2's\n // request body stream can only be consumed once; without this, the\n // later `parseIncomingMessage` call throws \"Body has already been read\"\n // because something upstream (signature check, dedupe, etc.) drains it.\n // Mirrors the Slack adapter's pattern.\n try {\n if (!event.context.__rawBody) {\n const body = await readBody(event);\n event.context.__rawBody = body;\n }\n } catch {\n // If we can't pre-read, parseIncomingMessage will surface the error\n }\n // Telegram has no challenge; we never short-circuit.\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const secret = await resolveSecret(\"TELEGRAM_WEBHOOK_SECRET\");\n if (!secret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_telegramUnverifiedWarned) {\n _telegramUnverifiedWarned = true;\n console.error(\n \"[telegram] TELEGRAM_WEBHOOK_SECRET not set — refusing webhook in production. \" +\n \"Set TELEGRAM_WEBHOOK_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_telegramUnverifiedWarned) {\n _telegramUnverifiedWarned = true;\n console.warn(\n \"[telegram] TELEGRAM_WEBHOOK_SECRET not set — accepting webhook without verification (dev mode)\",\n );\n }\n // Dev mode: still require the bot token to be configured at all.\n return !!(await resolveSecret(\"TELEGRAM_BOT_TOKEN\"));\n }\n\n const headerSecret = getHeader(event, \"x-telegram-bot-api-secret-token\");\n if (!headerSecret) return false;\n\n // Timing-safe comparison\n try {\n const crypto = await import(\"node:crypto\");\n return crypto.timingSafeEqual(\n Buffer.from(secret),\n Buffer.from(headerSecret),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n // Use the pre-cached raw body if available (set by handleVerification).\n // Falls back to readBody for paths that bypass handleVerification.\n const body = (event.context.__rawBody as any) ?? (await readBody(event));\n if (!body) return null;\n\n // Handle regular messages\n const message = body.message || body.edited_message;\n if (!message) return null;\n\n // Only process text messages\n const text = message.text?.trim();\n if (!text) return null;\n\n // Ignore bot commands that we don't handle (e.g., /start is fine)\n // Remove /start command prefix if present\n const cleanText =\n text === \"/start\"\n ? \"Hello! I'm ready to chat.\"\n : text.replace(/^\\/\\w+\\s*/, \"\").trim() || text;\n\n const chat = message.chat;\n const from = message.from;\n\n return {\n platform: \"telegram\",\n externalThreadId: String(chat.id),\n text: cleanText,\n senderName:\n from?.first_name + (from?.last_name ? ` ${from.last_name}` : \"\"),\n senderId: String(from?.id),\n platformContext: {\n chatId: chat.id,\n chatType: chat.type,\n messageId: message.message_id,\n rawText: text,\n fromId: from?.id,\n fromUsername: from?.username,\n },\n timestamp: message.date * 1000,\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const token = await resolveSecret(\"TELEGRAM_BOT_TOKEN\");\n if (!token) {\n console.error(\"[telegram] TELEGRAM_BOT_TOKEN not configured\");\n return;\n }\n\n const chatId = context.platformContext.chatId;\n const chunks = splitMessage(message.text, TELEGRAM_MAX_LENGTH);\n\n for (const chunk of chunks) {\n try {\n const res = await fetch(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: chatId,\n text: chunk,\n parse_mode: \"Markdown\",\n }),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n description?: string;\n };\n if (!data.ok) {\n // Retry without Markdown if parsing fails\n if (data.description?.includes(\"parse\")) {\n await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n chat_id: chatId,\n text: chunk,\n }),\n });\n } else {\n console.error(\"[telegram] sendMessage error:\", data.description);\n }\n }\n } catch (err) {\n console.error(\"[telegram] Failed to send message:\", err);\n }\n }\n },\n\n async sendMessageToTarget(\n message: OutgoingMessage,\n target: OutboundTarget,\n ): Promise<void> {\n const token = await resolveSecret(\"TELEGRAM_BOT_TOKEN\");\n if (!token) {\n console.error(\"[telegram] TELEGRAM_BOT_TOKEN not configured\");\n return;\n }\n\n const chunks = splitMessage(message.text, TELEGRAM_MAX_LENGTH);\n for (const chunk of chunks) {\n const body: Record<string, unknown> = {\n chat_id: target.destination,\n text: chunk,\n };\n if (target.threadRef) {\n body.message_thread_id = target.threadRef;\n }\n\n try {\n const res = await fetch(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n },\n );\n const data = (await res.json()) as {\n ok: boolean;\n description?: string;\n };\n if (!data.ok) {\n throw new Error(data.description || \"sendMessage failed\");\n }\n } catch (err) {\n console.error(\"[telegram] Failed to send proactive message:\", err);\n throw err;\n }\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n // Telegram's legacy Markdown uses single asterisks for bold, not double.\n // `[text](url)` is already supported natively.\n // 's' flag (dotAll) so `.` matches newlines — bold text can span lines.\n const normalized = text.replace(/\\*\\*(.+?)\\*\\*/gs, \"*$1*\");\n return { text: normalized, platformContext: { parse_mode: \"Markdown\" } };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const token = await resolveSecret(\"TELEGRAM_BOT_TOKEN\");\n const hasToken = !!token;\n\n let botName: string | undefined;\n if (hasToken) {\n try {\n const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);\n const data = (await res.json()) as {\n ok: boolean;\n result?: { username?: string };\n };\n if (data.ok) {\n botName = data.result?.username;\n }\n } catch {}\n }\n\n return {\n platform: \"telegram\",\n label: \"Telegram\",\n enabled: false, // overridden by plugin\n configured: hasToken,\n details: {\n hasToken,\n botUsername: botName,\n },\n error: !hasToken ? \"Save TELEGRAM_BOT_TOKEN in settings\" : undefined,\n };\n },\n };\n}\n\n/** Split a message into chunks that fit within the platform's limit */\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"whatsapp.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/whatsapp.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,aAAa,CAAC;AAsBrB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,eAAe,CA0PjD"}
1
+ {"version":3,"file":"whatsapp.d.ts","sourceRoot":"","sources":["../../../src/integrations/adapters/whatsapp.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,aAAa,CAAC;AAsBrB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,eAAe,CA4PjD"}
@@ -1,4 +1,5 @@
1
1
  import { getQuery, getHeader, readRawBody as h3ReadRawBody } from "h3";
2
+ import { resolveSecret } from "../../server/credential-provider.js";
2
3
  /** WhatsApp's max message length */
3
4
  const WHATSAPP_MAX_LENGTH = 4096;
4
5
  /**
@@ -82,7 +83,7 @@ export function whatsappAdapter() {
82
83
  const mode = query["hub.mode"];
83
84
  const token = query["hub.verify_token"];
84
85
  const challenge = query["hub.challenge"];
85
- const expected = process.env.WHATSAPP_VERIFY_TOKEN;
86
+ const expected = await resolveSecret("WHATSAPP_VERIFY_TOKEN");
86
87
  if (mode === "subscribe" && expected && typeof token === "string") {
87
88
  // Timing-safe compare so an attacker can't measure character-wise
88
89
  // mismatch latency (H6 in the webhook security audit).
@@ -103,7 +104,7 @@ export function whatsappAdapter() {
103
104
  return { handled: false };
104
105
  },
105
106
  async verifyWebhook(event) {
106
- const appSecret = process.env.WHATSAPP_APP_SECRET;
107
+ const appSecret = await resolveSecret("WHATSAPP_APP_SECRET");
107
108
  if (!appSecret) {
108
109
  if (shouldRefuseWhenSecretMissing()) {
109
110
  if (!_whatsappUnverifiedWarned) {
@@ -118,7 +119,7 @@ export function whatsappAdapter() {
118
119
  console.warn("[whatsapp] WHATSAPP_APP_SECRET not set — accepting webhook without verification (dev mode)");
119
120
  }
120
121
  // Dev mode: still require the access token to be configured at all.
121
- return !!process.env.WHATSAPP_ACCESS_TOKEN;
122
+ return !!(await resolveSecret("WHATSAPP_ACCESS_TOKEN"));
122
123
  }
123
124
  const signature = getHeader(event, "x-hub-signature-256");
124
125
  if (!signature)
@@ -186,8 +187,8 @@ export function whatsappAdapter() {
186
187
  };
187
188
  },
188
189
  async sendResponse(message, context) {
189
- const accessToken = process.env.WHATSAPP_ACCESS_TOKEN;
190
- const phoneNumberId = process.env.WHATSAPP_PHONE_NUMBER_ID;
190
+ const accessToken = await resolveSecret("WHATSAPP_ACCESS_TOKEN");
191
+ const phoneNumberId = await resolveSecret("WHATSAPP_PHONE_NUMBER_ID");
191
192
  if (!accessToken || !phoneNumberId) {
192
193
  console.error("[whatsapp] WHATSAPP_ACCESS_TOKEN or WHATSAPP_PHONE_NUMBER_ID not configured");
193
194
  return;
@@ -224,9 +225,9 @@ export function whatsappAdapter() {
224
225
  return { text, platformContext: {} };
225
226
  },
226
227
  async getStatus(_baseUrl) {
227
- const hasAccessToken = !!process.env.WHATSAPP_ACCESS_TOKEN;
228
- const hasVerifyToken = !!process.env.WHATSAPP_VERIFY_TOKEN;
229
- const hasPhoneNumberId = !!process.env.WHATSAPP_PHONE_NUMBER_ID;
228
+ const hasAccessToken = !!(await resolveSecret("WHATSAPP_ACCESS_TOKEN"));
229
+ const hasVerifyToken = !!(await resolveSecret("WHATSAPP_VERIFY_TOKEN"));
230
+ const hasPhoneNumberId = !!(await resolveSecret("WHATSAPP_PHONE_NUMBER_ID"));
230
231
  const configured = hasAccessToken && hasVerifyToken && hasPhoneNumberId;
231
232
  return {
232
233
  platform: "whatsapp",
@@ -239,7 +240,7 @@ export function whatsappAdapter() {
239
240
  hasPhoneNumberId,
240
241
  },
241
242
  error: !configured
242
- ? "Set WHATSAPP_ACCESS_TOKEN, WHATSAPP_VERIFY_TOKEN, and WHATSAPP_PHONE_NUMBER_ID"
243
+ ? "Save WHATSAPP_ACCESS_TOKEN, WHATSAPP_VERIFY_TOKEN, and WHATSAPP_PHONE_NUMBER_ID in settings"
243
244
  : undefined,
244
245
  };
245
246
  },
@@ -1 +1 @@
1
- {"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../../src/integrations/adapters/whatsapp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getQuery, getHeader, readRawBody as h3ReadRawBody } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n} from \"../types.js\";\n\n/** WhatsApp's max message length */\nconst WHATSAPP_MAX_LENGTH = 4096;\n\n/**\n * One-shot warning flag — log once per process when accepting unverified\n * webhooks (M6 in the webhook security audit).\n */\nlet _whatsappUnverifiedWarned = false;\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose signature can't\n * be verified (C2 in the webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create a WhatsApp Cloud API platform adapter.\n *\n * Required env vars:\n * - WHATSAPP_ACCESS_TOKEN — Permanent access token from Meta\n * - WHATSAPP_VERIFY_TOKEN — Custom token for webhook verification\n * - WHATSAPP_PHONE_NUMBER_ID — Phone number ID from Meta dashboard\n *\n * Optional env vars:\n * - WHATSAPP_APP_SECRET — App secret for signature verification\n */\nexport function whatsappAdapter(): PlatformAdapter {\n return {\n platform: \"whatsapp\",\n label: \"WhatsApp\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"WHATSAPP_ACCESS_TOKEN\",\n label: \"WhatsApp Access Token\",\n required: true,\n helpText:\n \"From your Meta app → WhatsApp → API Setup → Permanent access token. Generate one under System Users for production use.\",\n },\n {\n key: \"WHATSAPP_VERIFY_TOKEN\",\n label: \"WhatsApp Verify Token\",\n required: true,\n helpText:\n \"Any random string you choose. You'll paste the same value into Meta's webhook configuration so Meta can confirm dispatch owns the URL.\",\n },\n {\n key: \"WHATSAPP_PHONE_NUMBER_ID\",\n label: \"WhatsApp Phone Number ID\",\n required: true,\n helpText:\n \"From your Meta app → WhatsApp → API Setup. The numeric Phone number ID (not the actual phone number).\",\n },\n {\n key: \"WHATSAPP_APP_SECRET\",\n label: \"WhatsApp App Secret\",\n required: false,\n helpText:\n \"Optional. From Meta App Dashboard → Basic Settings → App Secret. Enables HMAC signature verification on inbound webhooks.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n const method = event.node?.req?.method || \"POST\";\n\n // For POST flows, pre-cache the raw body so verifyWebhook (HMAC) and\n // parseIncomingMessage don't both try to consume the request body\n // stream — h3 v2's body stream is consume-once, so a second read\n // hangs (M3 in the webhook security audit). Reads raw bytes; never\n // re-stringifies a parsed body, since Meta computes HMAC over the\n // exact bytes it sent (M2 in the audit).\n if (method === \"POST\") {\n try {\n await readRawBody(event);\n } catch {\n // Surfaces in verifyWebhook / parseIncomingMessage if it actually matters.\n }\n return { handled: false };\n }\n\n // GET: WhatsApp's challenge handshake.\n const query = getQuery(event);\n const mode = query[\"hub.mode\"];\n const token = query[\"hub.verify_token\"];\n const challenge = query[\"hub.challenge\"];\n const expected = process.env.WHATSAPP_VERIFY_TOKEN;\n\n if (mode === \"subscribe\" && expected && typeof token === \"string\") {\n // Timing-safe compare so an attacker can't measure character-wise\n // mismatch latency (H6 in the webhook security audit).\n const a = Buffer.from(String(token));\n const b = Buffer.from(String(expected));\n if (a.length === b.length) {\n try {\n const crypto = await import(\"node:crypto\");\n if (crypto.timingSafeEqual(a, b)) {\n return { handled: true, response: challenge };\n }\n } catch {\n // fall through\n }\n }\n }\n\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const appSecret = process.env.WHATSAPP_APP_SECRET;\n if (!appSecret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_whatsappUnverifiedWarned) {\n _whatsappUnverifiedWarned = true;\n console.error(\n \"[whatsapp] WHATSAPP_APP_SECRET not set — refusing webhook in production. \" +\n \"Set WHATSAPP_APP_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_whatsappUnverifiedWarned) {\n _whatsappUnverifiedWarned = true;\n console.warn(\n \"[whatsapp] WHATSAPP_APP_SECRET not set — accepting webhook without verification (dev mode)\",\n );\n }\n // Dev mode: still require the access token to be configured at all.\n return !!process.env.WHATSAPP_ACCESS_TOKEN;\n }\n\n const signature = getHeader(event, \"x-hub-signature-256\");\n if (!signature) return false;\n\n const body = await readRawBody(event);\n const crypto = await import(\"node:crypto\");\n const expectedSignature =\n \"sha256=\" +\n crypto.createHmac(\"sha256\", appSecret).update(body).digest(\"hex\");\n\n try {\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n // Always read via the cached raw body so HMAC and parse see identical\n // bytes — h3 v2's body stream is consume-once, and re-stringifying a\n // parsed body breaks Meta's signature check (M2/M3 in the audit).\n const raw = await readRawBody(event);\n if (!raw) return null;\n let body: any;\n try {\n body = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!body) return null;\n\n // WhatsApp Cloud API webhook payload structure\n const entry = body.entry?.[0];\n if (!entry) return null;\n\n const changes = entry.changes?.[0];\n if (!changes || changes.field !== \"messages\") return null;\n\n const value = changes.value;\n const message = value?.messages?.[0];\n if (!message) return null;\n\n // Only handle text messages\n if (message.type !== \"text\") return null;\n const text = message.text?.body?.trim();\n if (!text) return null;\n\n const contact = value.contacts?.[0];\n const from = message.from; // Phone number\n\n return {\n platform: \"whatsapp\",\n externalThreadId: from,\n text,\n senderName: contact?.profile?.name,\n senderId: from,\n platformContext: {\n phoneNumberId: value.metadata?.phone_number_id,\n displayPhoneNumber: value.metadata?.display_phone_number,\n messageId: message.id,\n from,\n timestamp: message.timestamp,\n },\n timestamp: parseInt(message.timestamp, 10) * 1000,\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const accessToken = process.env.WHATSAPP_ACCESS_TOKEN;\n const phoneNumberId = process.env.WHATSAPP_PHONE_NUMBER_ID;\n if (!accessToken || !phoneNumberId) {\n console.error(\n \"[whatsapp] WHATSAPP_ACCESS_TOKEN or WHATSAPP_PHONE_NUMBER_ID not configured\",\n );\n return;\n }\n\n const to = context.senderId;\n const chunks = splitMessage(message.text, WHATSAPP_MAX_LENGTH);\n\n for (const chunk of chunks) {\n try {\n const res = await fetch(\n `https://graph.facebook.com/v21.0/${phoneNumberId}/messages`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"text\",\n text: { body: chunk },\n }),\n },\n );\n if (!res.ok) {\n const data = await res.json().catch(() => ({}));\n console.error(\"[whatsapp] sendMessage error:\", data);\n }\n } catch (err) {\n console.error(\"[whatsapp] Failed to send message:\", err);\n }\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const hasAccessToken = !!process.env.WHATSAPP_ACCESS_TOKEN;\n const hasVerifyToken = !!process.env.WHATSAPP_VERIFY_TOKEN;\n const hasPhoneNumberId = !!process.env.WHATSAPP_PHONE_NUMBER_ID;\n const configured = hasAccessToken && hasVerifyToken && hasPhoneNumberId;\n\n return {\n platform: \"whatsapp\",\n label: \"WhatsApp\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasAccessToken,\n hasVerifyToken,\n hasPhoneNumberId,\n },\n error: !configured\n ? \"Set WHATSAPP_ACCESS_TOKEN, WHATSAPP_VERIFY_TOKEN, and WHATSAPP_PHONE_NUMBER_ID\"\n : undefined,\n };\n },\n };\n}\n\n/**\n * Read the raw request body as a string and cache it on the event context.\n *\n * Reads raw bytes from the request stream, NEVER `JSON.stringify`s a parsed\n * body — Meta's signature is computed over the exact bytes Meta sent\n * (M2/M3 in the webhook security audit). h3 v2's body stream is consume-once\n * so we cache the raw string after the first read.\n */\nasync function readRawBody(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await h3ReadRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
1
+ {"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../../src/integrations/adapters/whatsapp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getQuery, getHeader, readRawBody as h3ReadRawBody } from \"h3\";\n\nimport type { EnvKeyConfig } from \"../../server/create-server.js\";\nimport { resolveSecret } from \"../../server/credential-provider.js\";\nimport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n} from \"../types.js\";\n\n/** WhatsApp's max message length */\nconst WHATSAPP_MAX_LENGTH = 4096;\n\n/**\n * One-shot warning flag — log once per process when accepting unverified\n * webhooks (M6 in the webhook security audit).\n */\nlet _whatsappUnverifiedWarned = false;\n\n/**\n * Returns true when the deployment is running in production mode and the\n * operator has NOT explicitly opted into accepting unverified webhooks for\n * local testing. In production we MUST refuse webhooks whose signature can't\n * be verified (C2 in the webhook security audit).\n */\nfunction shouldRefuseWhenSecretMissing(): boolean {\n if (process.env.AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS === \"1\") return false;\n return process.env.NODE_ENV === \"production\";\n}\n\n/**\n * Create a WhatsApp Cloud API platform adapter.\n *\n * Required env vars:\n * - WHATSAPP_ACCESS_TOKEN — Permanent access token from Meta\n * - WHATSAPP_VERIFY_TOKEN — Custom token for webhook verification\n * - WHATSAPP_PHONE_NUMBER_ID — Phone number ID from Meta dashboard\n *\n * Optional env vars:\n * - WHATSAPP_APP_SECRET — App secret for signature verification\n */\nexport function whatsappAdapter(): PlatformAdapter {\n return {\n platform: \"whatsapp\",\n label: \"WhatsApp\",\n\n getRequiredEnvKeys(): EnvKeyConfig[] {\n return [\n {\n key: \"WHATSAPP_ACCESS_TOKEN\",\n label: \"WhatsApp Access Token\",\n required: true,\n helpText:\n \"From your Meta app → WhatsApp → API Setup → Permanent access token. Generate one under System Users for production use.\",\n },\n {\n key: \"WHATSAPP_VERIFY_TOKEN\",\n label: \"WhatsApp Verify Token\",\n required: true,\n helpText:\n \"Any random string you choose. You'll paste the same value into Meta's webhook configuration so Meta can confirm dispatch owns the URL.\",\n },\n {\n key: \"WHATSAPP_PHONE_NUMBER_ID\",\n label: \"WhatsApp Phone Number ID\",\n required: true,\n helpText:\n \"From your Meta app → WhatsApp → API Setup. The numeric Phone number ID (not the actual phone number).\",\n },\n {\n key: \"WHATSAPP_APP_SECRET\",\n label: \"WhatsApp App Secret\",\n required: false,\n helpText:\n \"Optional. From Meta App Dashboard → Basic Settings → App Secret. Enables HMAC signature verification on inbound webhooks.\",\n },\n ];\n },\n\n async handleVerification(\n event: H3Event,\n ): Promise<{ handled: boolean; response?: unknown }> {\n const method = event.node?.req?.method || \"POST\";\n\n // For POST flows, pre-cache the raw body so verifyWebhook (HMAC) and\n // parseIncomingMessage don't both try to consume the request body\n // stream — h3 v2's body stream is consume-once, so a second read\n // hangs (M3 in the webhook security audit). Reads raw bytes; never\n // re-stringifies a parsed body, since Meta computes HMAC over the\n // exact bytes it sent (M2 in the audit).\n if (method === \"POST\") {\n try {\n await readRawBody(event);\n } catch {\n // Surfaces in verifyWebhook / parseIncomingMessage if it actually matters.\n }\n return { handled: false };\n }\n\n // GET: WhatsApp's challenge handshake.\n const query = getQuery(event);\n const mode = query[\"hub.mode\"];\n const token = query[\"hub.verify_token\"];\n const challenge = query[\"hub.challenge\"];\n const expected = await resolveSecret(\"WHATSAPP_VERIFY_TOKEN\");\n\n if (mode === \"subscribe\" && expected && typeof token === \"string\") {\n // Timing-safe compare so an attacker can't measure character-wise\n // mismatch latency (H6 in the webhook security audit).\n const a = Buffer.from(String(token));\n const b = Buffer.from(String(expected));\n if (a.length === b.length) {\n try {\n const crypto = await import(\"node:crypto\");\n if (crypto.timingSafeEqual(a, b)) {\n return { handled: true, response: challenge };\n }\n } catch {\n // fall through\n }\n }\n }\n\n return { handled: false };\n },\n\n async verifyWebhook(event: H3Event): Promise<boolean> {\n const appSecret = await resolveSecret(\"WHATSAPP_APP_SECRET\");\n if (!appSecret) {\n if (shouldRefuseWhenSecretMissing()) {\n if (!_whatsappUnverifiedWarned) {\n _whatsappUnverifiedWarned = true;\n console.error(\n \"[whatsapp] WHATSAPP_APP_SECRET not set — refusing webhook in production. \" +\n \"Set WHATSAPP_APP_SECRET, or set AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1 for local testing only.\",\n );\n }\n return false;\n }\n if (!_whatsappUnverifiedWarned) {\n _whatsappUnverifiedWarned = true;\n console.warn(\n \"[whatsapp] WHATSAPP_APP_SECRET not set — accepting webhook without verification (dev mode)\",\n );\n }\n // Dev mode: still require the access token to be configured at all.\n return !!(await resolveSecret(\"WHATSAPP_ACCESS_TOKEN\"));\n }\n\n const signature = getHeader(event, \"x-hub-signature-256\");\n if (!signature) return false;\n\n const body = await readRawBody(event);\n const crypto = await import(\"node:crypto\");\n const expectedSignature =\n \"sha256=\" +\n crypto.createHmac(\"sha256\", appSecret).update(body).digest(\"hex\");\n\n try {\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature),\n );\n } catch {\n return false;\n }\n },\n\n async parseIncomingMessage(\n event: H3Event,\n ): Promise<IncomingMessage | null> {\n // Always read via the cached raw body so HMAC and parse see identical\n // bytes — h3 v2's body stream is consume-once, and re-stringifying a\n // parsed body breaks Meta's signature check (M2/M3 in the audit).\n const raw = await readRawBody(event);\n if (!raw) return null;\n let body: any;\n try {\n body = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!body) return null;\n\n // WhatsApp Cloud API webhook payload structure\n const entry = body.entry?.[0];\n if (!entry) return null;\n\n const changes = entry.changes?.[0];\n if (!changes || changes.field !== \"messages\") return null;\n\n const value = changes.value;\n const message = value?.messages?.[0];\n if (!message) return null;\n\n // Only handle text messages\n if (message.type !== \"text\") return null;\n const text = message.text?.body?.trim();\n if (!text) return null;\n\n const contact = value.contacts?.[0];\n const from = message.from; // Phone number\n\n return {\n platform: \"whatsapp\",\n externalThreadId: from,\n text,\n senderName: contact?.profile?.name,\n senderId: from,\n platformContext: {\n phoneNumberId: value.metadata?.phone_number_id,\n displayPhoneNumber: value.metadata?.display_phone_number,\n messageId: message.id,\n from,\n timestamp: message.timestamp,\n },\n timestamp: parseInt(message.timestamp, 10) * 1000,\n };\n },\n\n async sendResponse(\n message: OutgoingMessage,\n context: IncomingMessage,\n ): Promise<void> {\n const accessToken = await resolveSecret(\"WHATSAPP_ACCESS_TOKEN\");\n const phoneNumberId = await resolveSecret(\"WHATSAPP_PHONE_NUMBER_ID\");\n if (!accessToken || !phoneNumberId) {\n console.error(\n \"[whatsapp] WHATSAPP_ACCESS_TOKEN or WHATSAPP_PHONE_NUMBER_ID not configured\",\n );\n return;\n }\n\n const to = context.senderId;\n const chunks = splitMessage(message.text, WHATSAPP_MAX_LENGTH);\n\n for (const chunk of chunks) {\n try {\n const res = await fetch(\n `https://graph.facebook.com/v21.0/${phoneNumberId}/messages`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"text\",\n text: { body: chunk },\n }),\n },\n );\n if (!res.ok) {\n const data = await res.json().catch(() => ({}));\n console.error(\"[whatsapp] sendMessage error:\", data);\n }\n } catch (err) {\n console.error(\"[whatsapp] Failed to send message:\", err);\n }\n }\n },\n\n formatAgentResponse(text: string): OutgoingMessage {\n return { text, platformContext: {} };\n },\n\n async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {\n const hasAccessToken = !!(await resolveSecret(\"WHATSAPP_ACCESS_TOKEN\"));\n const hasVerifyToken = !!(await resolveSecret(\"WHATSAPP_VERIFY_TOKEN\"));\n const hasPhoneNumberId = !!(await resolveSecret(\n \"WHATSAPP_PHONE_NUMBER_ID\",\n ));\n const configured = hasAccessToken && hasVerifyToken && hasPhoneNumberId;\n\n return {\n platform: \"whatsapp\",\n label: \"WhatsApp\",\n enabled: false, // overridden by plugin\n configured,\n details: {\n hasAccessToken,\n hasVerifyToken,\n hasPhoneNumberId,\n },\n error: !configured\n ? \"Save WHATSAPP_ACCESS_TOKEN, WHATSAPP_VERIFY_TOKEN, and WHATSAPP_PHONE_NUMBER_ID in settings\"\n : undefined,\n };\n },\n };\n}\n\n/**\n * Read the raw request body as a string and cache it on the event context.\n *\n * Reads raw bytes from the request stream, NEVER `JSON.stringify`s a parsed\n * body — Meta's signature is computed over the exact bytes Meta sent\n * (M2/M3 in the webhook security audit). h3 v2's body stream is consume-once\n * so we cache the raw string after the first read.\n */\nasync function readRawBody(event: H3Event): Promise<string> {\n const cached = event.context.__rawBody;\n if (typeof cached === \"string\") return cached;\n const raw = (await h3ReadRawBody(event)) ?? \"\";\n event.context.__rawBody = raw;\n return raw;\n}\n\nfunction splitMessage(text: string, maxLength: number): string[] {\n if (text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIdx = remaining.lastIndexOf(\"\\n\", maxLength);\n if (splitIdx <= 0) splitIdx = remaining.lastIndexOf(\" \", maxLength);\n if (splitIdx <= 0) splitIdx = maxLength;\n chunks.push(remaining.slice(0, splitIdx));\n remaining = remaining.slice(splitIdx).trimStart();\n }\n return chunks;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AAwEA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AAGpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA0F9D,KAAK,yBAAyB,GAAG;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAIF,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsFlC;AA+ND;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,CA+nChB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,gBAA6B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AA0EA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AAGpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA0F9D,KAAK,yBAAyB,GAAG;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAUF,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsFlC;AA+ND;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,CAyrChB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,gBAA6B,CAAC"}
@@ -1,13 +1,15 @@
1
1
  import { defineEventHandler, setResponseStatus, getMethod, getQuery } from "h3";
2
2
  import { getRequestHeader } from "h3";
3
3
  import { createRemoteJWKSet, jwtVerify } from "jose";
4
- import { getOrgContext } from "../org/context.js";
4
+ import { getOrgContext, resolveOrgIdForEmail } from "../org/context.js";
5
5
  import { loadResourcesForPrompt } from "../server/agent-chat-plugin.js";
6
6
  import { withConfiguredAppBasePath } from "../server/app-base-path.js";
7
7
  import { getSession } from "../server/auth.js";
8
8
  import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
9
+ import { resolveSecret } from "../server/credential-provider.js";
9
10
  import { getH3App, markDefaultPluginProvided, } from "../server/framework-request-handler.js";
10
11
  import { readBody } from "../server/h3-helpers.js";
12
+ import { runWithRequestContext } from "../server/request-context.js";
11
13
  import { processA2AContinuationById, processDueA2AContinuations, } from "./a2a-continuation-processor.js";
12
14
  import { failA2AContinuation } from "./a2a-continuations-store.js";
13
15
  import { emailAdapter } from "./adapters/email.js";
@@ -407,7 +409,7 @@ export function createIntegrationsPlugin(options) {
407
409
  // init can leave us with an empty string forever. The getter
408
410
  // re-resolves on every webhook so freshly-set secrets work without
409
411
  // a redeploy.
410
- const getApiKey = () => options?.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
412
+ const getApiKey = () => options?.apiKey ?? "";
411
413
  // Build the system prompt
412
414
  const baseSystemPrompt = options?.systemPrompt ?? INTEGRATION_SYSTEM_PROMPT;
413
415
  // Resolve actions — auto-include call-agent so the integration agent can
@@ -451,6 +453,29 @@ export function createIntegrationsPlugin(options) {
451
453
  orgId: orgCtx?.orgId ?? session.orgId ?? null,
452
454
  };
453
455
  }
456
+ function toCredentialContext(ctx, opts) {
457
+ return {
458
+ userEmail: ctx.ownerEmail,
459
+ ...(ctx.orgId ? { orgId: ctx.orgId } : {}),
460
+ ...(opts?.isIntegrationCaller ? { isIntegrationCaller: true } : {}),
461
+ };
462
+ }
463
+ async function credentialContextForIntegrationConfig(config) {
464
+ const ownerEmail = typeof config?.owner === "string" ? config.owner.trim() : "";
465
+ if (!ownerEmail)
466
+ return null;
467
+ const orgId = await resolveOrgIdForEmail(ownerEmail).catch(() => null);
468
+ return {
469
+ userEmail: ownerEmail,
470
+ ...(orgId ? { orgId } : {}),
471
+ isIntegrationCaller: true,
472
+ };
473
+ }
474
+ async function withCredentialContext(context, fn) {
475
+ if (!context)
476
+ return fn();
477
+ return runWithRequestContext(context, fn);
478
+ }
454
479
  async function requireRemoteDevice(event) {
455
480
  const token = extractBearerToken(getRequestHeader(event, "authorization"));
456
481
  const device = await authenticateRemoteDeviceToken(token);
@@ -503,12 +528,14 @@ export function createIntegrationsPlugin(options) {
503
528
  setResponseStatus(event, 405);
504
529
  return { error: "Method not allowed" };
505
530
  }
506
- if (!(await requireSession(event)))
507
- return { error: "unauthorized" };
508
531
  const baseUrl = getBaseUrl(event);
532
+ const ctx = await requireSessionContext(event);
533
+ if (!ctx)
534
+ return { error: "unauthorized" };
535
+ const credentialContext = toCredentialContext(ctx);
509
536
  const statuses = [];
510
537
  for (const adapter of adapters) {
511
- const status = await adapter.getStatus(baseUrl);
538
+ const status = await withCredentialContext(credentialContext, () => adapter.getStatus(baseUrl));
512
539
  const config = await getIntegrationConfig(adapter.platform);
513
540
  status.enabled = !!config?.configData?.enabled;
514
541
  status.webhookUrl = `${baseUrl}${P}/${adapter.platform}/webhook`;
@@ -1170,10 +1197,11 @@ export function createIntegrationsPlugin(options) {
1170
1197
  }
1171
1198
  // ─── GET /:platform/status ─────────────────────────────
1172
1199
  if (action === "status" && method === "GET") {
1173
- if (!(await requireSession(event)))
1200
+ const ctx = await requireSessionContext(event);
1201
+ if (!ctx)
1174
1202
  return { error: "unauthorized" };
1175
1203
  const baseUrl = getBaseUrl(event);
1176
- const status = await adapter.getStatus(baseUrl);
1204
+ const status = await withCredentialContext(toCredentialContext(ctx), () => adapter.getStatus(baseUrl));
1177
1205
  const config = await getIntegrationConfig(platform);
1178
1206
  status.enabled = !!config?.configData?.enabled;
1179
1207
  status.webhookUrl = `${baseUrl}${P}/${platform}/webhook`;
@@ -1228,13 +1256,14 @@ export function createIntegrationsPlugin(options) {
1228
1256
  });
1229
1257
  return "ok";
1230
1258
  }
1259
+ const config = await getIntegrationConfig(platform);
1260
+ const credentialContext = await credentialContextForIntegrationConfig(config);
1231
1261
  // Handle platform verification challenges (e.g. Slack url_verification)
1232
1262
  // before checking enable state or parsing the message.
1233
- const verification = await adapter.handleVerification(event);
1263
+ const verification = await withCredentialContext(credentialContext, () => adapter.handleVerification(event));
1234
1264
  if (verification.handled) {
1235
1265
  return verification.response ?? "ok";
1236
1266
  }
1237
- const config = await getIntegrationConfig(platform);
1238
1267
  if (!config?.configData?.enabled) {
1239
1268
  setResponseStatus(event, 404);
1240
1269
  return { error: `Integration ${platform} is not enabled` };
@@ -1244,12 +1273,12 @@ export function createIntegrationsPlugin(options) {
1244
1273
  // hangs on streaming providers), and that means handleWebhook's
1245
1274
  // own verifyWebhook step is bypassed. Without this call anyone
1246
1275
  // could POST a forged Slack/Telegram/email payload.
1247
- const isValid = await adapter.verifyWebhook(event);
1276
+ const isValid = await withCredentialContext(credentialContext, () => adapter.verifyWebhook(event));
1248
1277
  if (!isValid) {
1249
1278
  setResponseStatus(event, 401);
1250
1279
  return { error: "Invalid webhook signature" };
1251
1280
  }
1252
- const incoming = await adapter.parseIncomingMessage(event);
1281
+ const incoming = await withCredentialContext(credentialContext, () => adapter.parseIncomingMessage(event));
1253
1282
  if (!incoming) {
1254
1283
  setResponseStatus(event, 200);
1255
1284
  return "ok";
@@ -1309,10 +1338,15 @@ export function createIntegrationsPlugin(options) {
1309
1338
  if (platform === "telegram") {
1310
1339
  const baseUrl = getBaseUrl(event);
1311
1340
  const webhookUrl = `${baseUrl}${P}/telegram/webhook`;
1312
- const token = process.env.TELEGRAM_BOT_TOKEN;
1341
+ const ctx = await requireSessionContext(event);
1342
+ if (!ctx)
1343
+ return { error: "unauthorized" };
1344
+ const token = await withCredentialContext(toCredentialContext(ctx), () => resolveSecret("TELEGRAM_BOT_TOKEN"));
1313
1345
  if (!token) {
1314
1346
  setResponseStatus(event, 400);
1315
- return { error: "TELEGRAM_BOT_TOKEN not configured" };
1347
+ return {
1348
+ error: "TELEGRAM_BOT_TOKEN not configured. Save it in settings.",
1349
+ };
1316
1350
  }
1317
1351
  try {
1318
1352
  const res = await fetch(`https://api.telegram.org/bot${token}/setWebhook`, {