@agent-native/core 0.92.11 → 0.93.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 (777) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +45 -0
  3. package/corpus/core/docs/content/automation-connectors.mdx +80 -0
  4. package/corpus/core/docs/content/extensions.mdx +8 -0
  5. package/corpus/core/docs/content/locales/ar-SA/extensions.mdx +8 -0
  6. package/corpus/core/docs/content/locales/ar-SA/messaging.mdx +84 -15
  7. package/corpus/core/docs/content/locales/ar-SA/template-clips.mdx +1 -0
  8. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -0
  9. package/corpus/core/docs/content/locales/de-DE/extensions.mdx +10 -0
  10. package/corpus/core/docs/content/locales/de-DE/messaging.mdx +92 -15
  11. package/corpus/core/docs/content/locales/de-DE/template-clips.mdx +1 -0
  12. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -0
  13. package/corpus/core/docs/content/locales/es-ES/extensions.mdx +10 -0
  14. package/corpus/core/docs/content/locales/es-ES/messaging.mdx +91 -15
  15. package/corpus/core/docs/content/locales/es-ES/template-clips.mdx +1 -0
  16. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -0
  17. package/corpus/core/docs/content/locales/fr-FR/extensions.mdx +10 -0
  18. package/corpus/core/docs/content/locales/fr-FR/messaging.mdx +91 -15
  19. package/corpus/core/docs/content/locales/fr-FR/template-clips.mdx +1 -0
  20. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -0
  21. package/corpus/core/docs/content/locales/hi-IN/extensions.mdx +8 -0
  22. package/corpus/core/docs/content/locales/hi-IN/messaging.mdx +81 -15
  23. package/corpus/core/docs/content/locales/hi-IN/template-clips.mdx +1 -0
  24. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -0
  25. package/corpus/core/docs/content/locales/ja-JP/extensions.mdx +9 -0
  26. package/corpus/core/docs/content/locales/ja-JP/messaging.mdx +81 -15
  27. package/corpus/core/docs/content/locales/ja-JP/template-clips.mdx +1 -0
  28. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -0
  29. package/corpus/core/docs/content/locales/ko-KR/extensions.mdx +8 -0
  30. package/corpus/core/docs/content/locales/ko-KR/messaging.mdx +80 -15
  31. package/corpus/core/docs/content/locales/ko-KR/template-clips.mdx +1 -0
  32. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -0
  33. package/corpus/core/docs/content/locales/pt-BR/extensions.mdx +9 -0
  34. package/corpus/core/docs/content/locales/pt-BR/messaging.mdx +88 -15
  35. package/corpus/core/docs/content/locales/pt-BR/template-clips.mdx +1 -0
  36. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -0
  37. package/corpus/core/docs/content/locales/zh-CN/extensions.mdx +6 -0
  38. package/corpus/core/docs/content/locales/zh-CN/messaging.mdx +78 -15
  39. package/corpus/core/docs/content/locales/zh-CN/template-clips.mdx +1 -0
  40. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -0
  41. package/corpus/core/docs/content/locales/zh-TW/extensions.mdx +6 -0
  42. package/corpus/core/docs/content/locales/zh-TW/messaging.mdx +78 -15
  43. package/corpus/core/docs/content/locales/zh-TW/template-clips.mdx +1 -0
  44. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -0
  45. package/corpus/core/docs/content/messaging.mdx +227 -45
  46. package/corpus/core/docs/content/template-clips.mdx +3 -0
  47. package/corpus/core/docs/content/template-design.mdx +4 -0
  48. package/corpus/core/package.json +5 -1
  49. package/corpus/core/src/a2a/artifact-response.ts +131 -2
  50. package/corpus/core/src/a2a/client.ts +10 -0
  51. package/corpus/core/src/a2a/handlers.ts +40 -76
  52. package/corpus/core/src/a2a/types.ts +2 -0
  53. package/corpus/core/src/agent/durable-background.ts +24 -0
  54. package/corpus/core/src/agent/production-agent.ts +48 -5
  55. package/corpus/core/src/agent/run-manager.ts +171 -20
  56. package/corpus/core/src/agent/run-store.ts +297 -42
  57. package/corpus/core/src/agent/types.ts +23 -0
  58. package/corpus/core/src/audit/record.ts +31 -0
  59. package/corpus/core/src/audit/store.ts +69 -3
  60. package/corpus/core/src/audit/types.ts +13 -0
  61. package/corpus/core/src/automation/index.ts +802 -0
  62. package/corpus/core/src/cli/create.ts +12 -5
  63. package/corpus/core/src/cli/design-connect.ts +596 -36
  64. package/corpus/core/src/client/AgentPanel.tsx +51 -6
  65. package/corpus/core/src/client/AssistantChat.tsx +274 -147
  66. package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
  67. package/corpus/core/src/client/RunStuckBanner.tsx +69 -20
  68. package/corpus/core/src/client/agent-chat-adapter.ts +110 -17
  69. package/corpus/core/src/client/automation.ts +22 -0
  70. package/corpus/core/src/client/chat/run-recovery.tsx +67 -50
  71. package/corpus/core/src/client/composer/TiptapComposer.tsx +8 -0
  72. package/corpus/core/src/client/index.ts +30 -0
  73. package/corpus/core/src/client/integrations/api.ts +295 -0
  74. package/corpus/core/src/client/integrations/index.ts +26 -0
  75. package/corpus/core/src/client/transcription/use-live-transcription.ts +68 -5
  76. package/corpus/core/src/client/use-run-stuck-detection.ts +41 -3
  77. package/corpus/core/src/deploy/build.ts +59 -3
  78. package/corpus/core/src/deploy/workspace-deploy.ts +37 -2
  79. package/corpus/core/src/file-upload/types.ts +2 -0
  80. package/corpus/core/src/index.ts +37 -0
  81. package/corpus/core/src/integrations/a2a-continuations-store.ts +6 -4
  82. package/corpus/core/src/integrations/adapters/discord.ts +362 -0
  83. package/corpus/core/src/integrations/adapters/index.ts +29 -0
  84. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +411 -0
  85. package/corpus/core/src/integrations/adapters/slack.ts +861 -27
  86. package/corpus/core/src/integrations/adapters/telegram.ts +54 -15
  87. package/corpus/core/src/integrations/adapters/whatsapp.ts +41 -17
  88. package/corpus/core/src/integrations/catalog.ts +571 -0
  89. package/corpus/core/src/integrations/controls-store.ts +187 -0
  90. package/corpus/core/src/integrations/index.ts +100 -0
  91. package/corpus/core/src/integrations/installations-store.ts +677 -0
  92. package/corpus/core/src/integrations/integration-memory.ts +187 -0
  93. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +6 -6
  94. package/corpus/core/src/integrations/pending-tasks-store.ts +83 -5
  95. package/corpus/core/src/integrations/plugin.ts +685 -47
  96. package/corpus/core/src/integrations/scope-store.ts +498 -0
  97. package/corpus/core/src/integrations/slack-manifest.ts +78 -0
  98. package/corpus/core/src/integrations/slack-oauth.ts +343 -0
  99. package/corpus/core/src/integrations/types.ts +179 -0
  100. package/corpus/core/src/integrations/usage-budget-store.ts +906 -0
  101. package/corpus/core/src/integrations/webhook-handler.ts +562 -51
  102. package/corpus/core/src/jobs/scheduler.ts +100 -2
  103. package/corpus/core/src/jobs/tools.ts +45 -9
  104. package/corpus/core/src/provider-api/index.ts +72 -1
  105. package/corpus/core/src/resources/handlers.ts +88 -14
  106. package/corpus/core/src/resources/script-helpers.ts +36 -7
  107. package/corpus/core/src/resources/store.ts +59 -52
  108. package/corpus/core/src/scripts/call-agent.ts +107 -1
  109. package/corpus/core/src/server/agent-chat-plugin.ts +294 -119
  110. package/corpus/core/src/server/auth.ts +10 -14
  111. package/corpus/core/src/server/builder-design-systems.ts +16 -1
  112. package/corpus/core/src/server/edge.ts +18 -0
  113. package/corpus/core/src/server/index.ts +26 -0
  114. package/corpus/core/src/server/request-context.ts +18 -0
  115. package/corpus/core/src/server/social-og-image.ts +3 -2
  116. package/corpus/core/src/styles/agent-native.css +62 -44
  117. package/corpus/core/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  118. package/corpus/core/src/templates/default/AGENTS.md +7 -3
  119. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  120. package/corpus/core/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  121. package/corpus/core/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  122. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  123. package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  124. package/corpus/core/src/templates/workspace-core/AGENTS.md +3 -2
  125. package/corpus/core/src/usage/store.ts +37 -2
  126. package/corpus/templates/analytics/.agents/skills/capture-learnings/SKILL.md +22 -9
  127. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +23 -0
  128. package/corpus/templates/analytics/.builder/skills/provider-api/SKILL.md +15 -0
  129. package/corpus/templates/analytics/actions/save-monitor.ts +25 -2
  130. package/corpus/templates/analytics/app/lib/data-sources.ts +28 -0
  131. package/corpus/templates/analytics/changelog/2026-07-10-agent-created-uptime-monitors-now-return-a-direct-link-to-th.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-email-reports-still-arrive-when-screenshot-capture-temporarily-fails.md +6 -0
  133. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +3 -7
  134. package/corpus/templates/analytics/server/lib/credential-keys.ts +12 -0
  135. package/corpus/templates/analytics/server/lib/dashboard-report.ts +5 -1
  136. package/corpus/templates/analytics/server/lib/provider-credentials.ts +2 -0
  137. package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +16 -0
  138. package/corpus/templates/assets/.agents/skills/capture-learnings/SKILL.md +22 -9
  139. package/corpus/templates/brain/.agents/skills/capture-learnings/SKILL.md +22 -9
  140. package/corpus/templates/calendar/.agents/skills/capture-learnings/SKILL.md +22 -9
  141. package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +98 -108
  142. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +97 -90
  143. package/corpus/templates/calendar/changelog/2026-07-10-event-guest-details-and-option-menus-now-use-valid-accessibl.md +6 -0
  144. package/corpus/templates/calendar/changelog/2026-07-10-natural-language-event-phrases-stay-available-as-quick-creat.md +6 -0
  145. package/corpus/templates/chat/.agents/skills/capture-learnings/SKILL.md +22 -9
  146. package/corpus/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  147. package/corpus/templates/clips/.agents/skills/capture-learnings/SKILL.md +22 -9
  148. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +17 -1
  149. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +8 -0
  150. package/corpus/templates/clips/AGENTS.md +8 -3
  151. package/corpus/templates/clips/actions/finalize-recording.ts +63 -23
  152. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -2
  153. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +72 -9
  154. package/corpus/templates/clips/actions/list-recordings.ts +19 -3
  155. package/corpus/templates/clips/actions/navigate.ts +2 -0
  156. package/corpus/templates/clips/actions/reprocess-recording.ts +20 -6
  157. package/corpus/templates/clips/actions/request-transcript.ts +16 -31
  158. package/corpus/templates/clips/actions/view-screen.ts +57 -5
  159. package/corpus/templates/clips/app/components/layout/Header.tsx +1 -0
  160. package/corpus/templates/clips/app/components/library/empty-state.tsx +2 -0
  161. package/corpus/templates/clips/app/components/library/library-grid.tsx +69 -42
  162. package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -1
  163. package/corpus/templates/clips/app/components/library/recording-card.tsx +81 -68
  164. package/corpus/templates/clips/app/components/library/search-bar.tsx +4 -3
  165. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +9 -3
  166. package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -1
  167. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +27 -5
  168. package/corpus/templates/clips/app/hooks/use-library.ts +1 -1
  169. package/corpus/templates/clips/app/hooks/use-navigation-state.ts +10 -2
  170. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  171. package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -1
  172. package/corpus/templates/clips/app/i18n/de-DE.ts +7 -1
  173. package/corpus/templates/clips/app/i18n/en-US.ts +7 -1
  174. package/corpus/templates/clips/app/i18n/es-ES.ts +7 -1
  175. package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -1
  176. package/corpus/templates/clips/app/i18n/hi-IN.ts +7 -1
  177. package/corpus/templates/clips/app/i18n/ja-JP.ts +7 -1
  178. package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -1
  179. package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -1
  180. package/corpus/templates/clips/app/i18n/zh-CN.ts +8 -1
  181. package/corpus/templates/clips/app/i18n/zh-TW.ts +8 -1
  182. package/corpus/templates/clips/app/lib/recording-visibility.ts +70 -0
  183. package/corpus/templates/clips/app/routes/_app.shared.tsx +20 -0
  184. package/corpus/templates/clips/app/routes/download.tsx +24 -8
  185. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +115 -2
  186. package/corpus/templates/clips/app/routes/record.tsx +62 -0
  187. package/corpus/templates/clips/app/routes/share.$shareId.tsx +25 -16
  188. package/corpus/templates/clips/changelog/2026-07-10-clips-desktop-is-now-available-for-linux-with-appimage-debia.md +6 -0
  189. package/corpus/templates/clips/changelog/2026-07-10-clips-downloads-updates-sooner-in-the-background-and-offers-.md +6 -0
  190. package/corpus/templates/clips/changelog/2026-07-10-clips-shared-with-you-now-appear-in-a-searchable-shared-with.md +6 -0
  191. package/corpus/templates/clips/changelog/2026-07-10-clips-with-missing-mobile-video-frames-can-be-repaired-witho.md +6 -0
  192. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  193. package/corpus/templates/clips/changelog/2026-07-10-mobile-camera-recordings-pause-while-backgrounded.md +6 -0
  194. package/corpus/templates/clips/changelog/2026-07-10-mp4-downloads-now-show-progress-immediately-after-the-option.md +6 -0
  195. package/corpus/templates/clips/changelog/2026-07-10-recording-pages-now-open-as-soon-as-you-stop-with-a-share-li.md +6 -0
  196. package/corpus/templates/clips/changelog/2026-07-10-recordings-now-seek-smoothly-downloads-use-the-correct-file-.md +6 -0
  197. package/corpus/templates/clips/changelog/2026-07-10-s3-compatible-storage-now-saves-hosted-recordings-without-re.md +6 -0
  198. package/corpus/templates/clips/desktop/README.md +22 -6
  199. package/corpus/templates/clips/desktop/package.json +1 -1
  200. package/corpus/templates/clips/desktop/src/app.tsx +32 -19
  201. package/corpus/templates/clips/desktop/src/components/ReadinessPanel.tsx +15 -9
  202. package/corpus/templates/clips/desktop/src/lib/finalization-guard.ts +22 -0
  203. package/corpus/templates/clips/desktop/src/lib/permissions.ts +3 -1
  204. package/corpus/templates/clips/desktop/src/lib/recorder.ts +97 -120
  205. package/corpus/templates/clips/desktop/src/lib/updater.ts +26 -4
  206. package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +4 -8
  207. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +1 -1
  208. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
  209. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +11 -7
  210. package/corpus/templates/clips/desktop/src-tauri/tauri.linux.conf.json +11 -0
  211. package/corpus/templates/clips/netlify.toml +3 -0
  212. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +106 -0
  213. package/corpus/templates/clips/server/lib/public-agent-context.ts +9 -1
  214. package/corpus/templates/clips/server/lib/resumable-upload-provider.ts +52 -0
  215. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +433 -85
  216. package/corpus/templates/clips/server/lib/video-remux.ts +114 -0
  217. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +109 -0
  218. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +22 -11
  219. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +4 -2
  220. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +47 -5
  221. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +10 -2
  222. package/corpus/templates/clips/shared/share-loader-response.ts +12 -0
  223. package/corpus/templates/clips/shared/transcript-status.ts +40 -0
  224. package/corpus/templates/content/.agents/skills/capture-learnings/SKILL.md +22 -9
  225. package/corpus/templates/content/.agents/skills/content/SKILL.md +57 -5
  226. package/corpus/templates/content/AGENTS.md +65 -55
  227. package/corpus/templates/content/actions/_builder-cms-read-client.ts +92 -23
  228. package/corpus/templates/content/actions/_database-source-utils.ts +207 -41
  229. package/corpus/templates/content/actions/_property-utils.ts +30 -2
  230. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +316 -121
  231. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  232. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  233. package/corpus/templates/content/actions/submit-content-database-form.ts +497 -0
  234. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  235. package/corpus/templates/content/actions/update-content-database-view.ts +8 -1
  236. package/corpus/templates/content/actions/view-screen.ts +4 -1
  237. package/corpus/templates/content/agent-native.app-skill.json +1 -1
  238. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +250 -64
  239. package/corpus/templates/content/app/components/editor/database/FormView.tsx +460 -0
  240. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
  241. package/corpus/templates/content/app/components/editor/database/settings.tsx +30 -37
  242. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  243. package/corpus/templates/content/app/components/editor/database/view-config.ts +29 -1
  244. package/corpus/templates/content/app/components/ui/textarea.tsx +1 -0
  245. package/corpus/templates/content/app/hooks/use-content-database.ts +22 -0
  246. package/corpus/templates/content/app/i18n/zh-TW.ts +21 -0
  247. package/corpus/templates/content/app/i18n-data.ts +225 -0
  248. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  249. package/corpus/templates/content/changelog/2026-07-10-builder-backed-properties-now-load-current-source-values-as-.md +6 -0
  250. package/corpus/templates/content/changelog/2026-07-10-builder-database-refreshes-now-stay-explicit-and-accurate.md +6 -0
  251. package/corpus/templates/content/changelog/2026-07-10-database-form-views-now-collect-ordered-required-answers-and.md +6 -0
  252. package/corpus/templates/content/changelog/2026-07-10-database-tables-now-use-a-quieter-more-compact-layout-with-c.md +6 -0
  253. package/corpus/templates/content/changelog/2026-07-10-slack-intake-routes-to-content-database-forms.md +6 -0
  254. package/corpus/templates/content/parity/matrix.md +1 -0
  255. package/corpus/templates/content/parity/matrix.ts +18 -0
  256. package/corpus/templates/content/shared/api.ts +28 -1
  257. package/corpus/templates/content/shared/database-form.ts +53 -0
  258. package/corpus/templates/design/.agents/skills/capture-learnings/SKILL.md +22 -9
  259. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +26 -0
  260. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +85 -12
  261. package/corpus/templates/design/AGENTS.md +72 -18
  262. package/corpus/templates/design/DESIGN.md +1 -1
  263. package/corpus/templates/design/DEVELOPING.md +1 -1
  264. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +116 -0
  265. package/corpus/templates/design/actions/create-design-from-template.ts +220 -0
  266. package/corpus/templates/design/actions/create-design-system.ts +92 -16
  267. package/corpus/templates/design/actions/delete-design-template.ts +28 -0
  268. package/corpus/templates/design/actions/delete-file.ts +7 -0
  269. package/corpus/templates/design/actions/edit-design.ts +3 -0
  270. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +137 -5
  271. package/corpus/templates/design/actions/export-pdf.ts +1 -1
  272. package/corpus/templates/design/actions/generate-design.ts +3 -0
  273. package/corpus/templates/design/actions/get-design-snapshot.ts +9 -0
  274. package/corpus/templates/design/actions/get-figma-connection-status.ts +26 -0
  275. package/corpus/templates/design/actions/get-figma-design-context.ts +138 -0
  276. package/corpus/templates/design/actions/get-figma-styles.ts +2 -2
  277. package/corpus/templates/design/actions/import-figma-clipboard.ts +112 -16
  278. package/corpus/templates/design/actions/import-figma-frame.ts +15 -4
  279. package/corpus/templates/design/actions/index-design-system-with-builder.ts +4 -0
  280. package/corpus/templates/design/actions/list-design-templates.ts +118 -0
  281. package/corpus/templates/design/actions/navigate.ts +16 -2
  282. package/corpus/templates/design/actions/provider-api-request.ts +11 -1
  283. package/corpus/templates/design/actions/save-design-as-template.ts +156 -0
  284. package/corpus/templates/design/actions/set-active-breakpoint.ts +13 -4
  285. package/corpus/templates/design/actions/take-design-screenshot.ts +73 -3
  286. package/corpus/templates/design/actions/update-file.ts +39 -22
  287. package/corpus/templates/design/actions/view-screen.ts +36 -1
  288. package/corpus/templates/design/actions/write-local-file.ts +50 -38
  289. package/corpus/templates/design/app/components/design/AutoLayoutSuggestionDialog.tsx +151 -0
  290. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +20 -0
  291. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +251 -22
  292. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +3 -3
  293. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +384 -9
  294. package/corpus/templates/design/app/components/design/EditPanel.tsx +105 -32
  295. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +58 -28
  296. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +6 -0
  297. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +539 -53
  298. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +40 -0
  299. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +1 -0
  300. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +1 -0
  301. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +5 -1
  302. package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
  303. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +3 -1
  304. package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +2 -0
  305. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +19 -0
  306. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +1 -0
  307. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +27 -0
  308. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +137 -81
  309. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +25 -5
  310. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +1 -1
  311. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +220 -3
  312. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +13 -0
  313. package/corpus/templates/design/app/components/design/multi-screen/types.ts +22 -1
  314. package/corpus/templates/design/app/components/design/types.ts +4 -0
  315. package/corpus/templates/design/app/components/design-system/ProductionDesignSystemShowcase.tsx +208 -0
  316. package/corpus/templates/design/app/components/editor/FigmaLinkComposerBubble.tsx +321 -0
  317. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +24 -4
  318. package/corpus/templates/design/app/components/layout/Layout.tsx +16 -2
  319. package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -0
  320. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +72 -0
  321. package/corpus/templates/design/app/global.css +2 -35
  322. package/corpus/templates/design/app/hooks/use-navigation-state.ts +10 -0
  323. package/corpus/templates/design/app/i18n/zh-TW.ts +127 -5
  324. package/corpus/templates/design/app/i18n-breakpoints.ts +209 -0
  325. package/corpus/templates/design/app/i18n-data.ts +1317 -59
  326. package/corpus/templates/design/app/i18n-template-feature.ts +458 -0
  327. package/corpus/templates/design/app/lib/design-clipboard.ts +202 -0
  328. package/corpus/templates/design/app/lib/design-file-upload.ts +87 -0
  329. package/corpus/templates/design/app/lib/design-import.ts +168 -11
  330. package/corpus/templates/design/app/lib/desktop-design-preview.ts +423 -0
  331. package/corpus/templates/design/app/lib/figma-clipboard.ts +74 -8
  332. package/corpus/templates/design/app/lib/figma-connection.ts +161 -0
  333. package/corpus/templates/design/app/lib/figma-svg-copy.ts +814 -22
  334. package/corpus/templates/design/app/lib/figma-url.ts +77 -0
  335. package/corpus/templates/design/app/lib/pending-generation.ts +35 -0
  336. package/corpus/templates/design/app/pages/DesignSystems.tsx +236 -177
  337. package/corpus/templates/design/app/pages/Index.tsx +38 -0
  338. package/corpus/templates/design/app/pages/Templates.tsx +498 -0
  339. package/corpus/templates/design/app/pages/design-editor/auto-layout-suggestion.ts +328 -0
  340. package/corpus/templates/design/app/pages/design-editor/clone-idrefs.ts +113 -0
  341. package/corpus/templates/design/app/pages/design-editor/created-screen-navigation.ts +55 -0
  342. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +32 -0
  343. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +49 -2
  344. package/corpus/templates/design/app/pages/design-editor/export-capture.ts +296 -0
  345. package/corpus/templates/design/app/pages/design-editor/history.ts +101 -2
  346. package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +28 -1
  347. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +34 -0
  348. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +202 -6
  349. package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +92 -0
  350. package/corpus/templates/design/app/pages/design-editor/save-failure.ts +58 -0
  351. package/corpus/templates/design/app/routes/templates.tsx +4 -6
  352. package/corpus/templates/design/changelog/2026-07-10-app-workspaces-now-use-clean-borderless-main-surfaces-throug.md +6 -0
  353. package/corpus/templates/design/changelog/2026-07-10-ask-chat-what-s-in-a-figma-file-or-frame-structure-screensho.md +6 -0
  354. package/corpus/templates/design/changelog/2026-07-10-canvas-edits-now-reject-malformed-html-before-it-can-corrupt.md +6 -0
  355. package/corpus/templates/design/changelog/2026-07-10-complex-designs-now-export-as-valid-secure-svgs-and-crisp-ra.md +6 -0
  356. package/corpus/templates/design/changelog/2026-07-10-complex-figma-masks-arcs-rich-text-transformed-images-and-ad.md +6 -0
  357. package/corpus/templates/design/changelog/2026-07-10-copied-layers-paste-across-designs-and-browser-tabs.md +6 -0
  358. package/corpus/templates/design/changelog/2026-07-10-copy-and-download-editable-figma-svg.md +6 -0
  359. package/corpus/templates/design/changelog/2026-07-10-copying-design-text-now-pastes-readable-text-into-other-apps.md +6 -0
  360. package/corpus/templates/design/changelog/2026-07-10-deleted-screens-can-be-restored-with-undo-and-removed-again-.md +6 -0
  361. package/corpus/templates/design/changelog/2026-07-10-design-changes-now-save-before-switching-apps-and-retry-when.md +6 -0
  362. package/corpus/templates/design/changelog/2026-07-10-design-stays-usable-on-phones-by-keeping-editor-panels-and-f.md +6 -0
  363. package/corpus/templates/design/changelog/2026-07-10-download-all-screens-as-one-multi-page-pdf-from-the-overview.md +6 -0
  364. package/corpus/templates/design/changelog/2026-07-10-duplicated-layers-now-keep-labels-aria-relationships-fragmen.md +6 -0
  365. package/corpus/templates/design/changelog/2026-07-10-fig-import-results-stay-compact.md +6 -0
  366. package/corpus/templates/design/changelog/2026-07-10-figma-imports-now-load-real-fonts-fix-rotated-gradient-dashe.md +6 -0
  367. package/corpus/templates/design/changelog/2026-07-10-figma-links-in-chat-and-the-import-tab-now-offer-secure-one-.md +6 -0
  368. package/corpus/templates/design/changelog/2026-07-10-fixed-cmd-z-not-restoring-content-after-an-agent-driven-desi.md +6 -0
  369. package/corpus/templates/design/changelog/2026-07-10-fixed-download-pdf-being-unreachable-from-the-export-panel-e.md +6 -0
  370. package/corpus/templates/design/changelog/2026-07-10-fixed-dragging-a-layer-out-of-an-auto-layout-row-column-no-l.md +6 -0
  371. package/corpus/templates/design/changelog/2026-07-10-fixed-figma-svg-export-text-wrapping-sizing-and-inner-layer.md +6 -0
  372. package/corpus/templates/design/changelog/2026-07-10-fixed-hiding-or-locking-one-layer-no-longer-hides-or-blocks-.md +6 -0
  373. package/corpus/templates/design/changelog/2026-07-10-fixed-png-screenshots-of-tall-screens-being-cropped-to-one-v.md +6 -0
  374. package/corpus/templates/design/changelog/2026-07-10-fixed-png-svg-exports-capturing-broken-layout-when-webfonts-.md +6 -0
  375. package/corpus/templates/design/changelog/2026-07-10-frame-tool-now-offers-standard-ad-unit-size-presets-medium-r.md +6 -0
  376. package/corpus/templates/design/changelog/2026-07-10-full-view-controls-stay-within-their-frame-instead-of-coveri.md +6 -0
  377. package/corpus/templates/design/changelog/2026-07-10-hover-focus-focus-visible-pressed-and-disabled-states-now-pr.md +6 -0
  378. package/corpus/templates/design/changelog/2026-07-10-layer-drops-now-land-in-the-intended-grid-row-stay-visually-.md +6 -0
  379. package/corpus/templates/design/changelog/2026-07-10-local-code-workspaces-now-keep-unsaved-edits-across-panel-sw.md +6 -0
  380. package/corpus/templates/design/changelog/2026-07-10-local-visual-editing-now-keeps-authenticated-sessions-shared.md +6 -0
  381. package/corpus/templates/design/changelog/2026-07-10-long-running-agent-work-no-longer-shows-a-premature-stuck-wa.md +6 -0
  382. package/corpus/templates/design/changelog/2026-07-10-managed-figma-connections-are-recognized-automatically.md +6 -0
  383. package/corpus/templates/design/changelog/2026-07-10-new-designs-can-open-directly-in-the-editor-without-a-pr.md +6 -0
  384. package/corpus/templates/design/changelog/2026-07-10-new-screens-are-selected-and-revealed-immediately.md +6 -0
  385. package/corpus/templates/design/changelog/2026-07-10-new-text-layers-undo-as-one-safe-creation.md +6 -0
  386. package/corpus/templates/design/changelog/2026-07-10-preview-and-apply-inferred-auto-layout-without-changing-yo.md +5 -0
  387. package/corpus/templates/design/changelog/2026-07-10-print-pdf-exports-and-paper-size-presets-letter-a4-now-match.md +6 -0
  388. package/corpus/templates/design/changelog/2026-07-10-responsive-frame-editing.md +6 -0
  389. package/corpus/templates/design/changelog/2026-07-10-reusable-design-templates.md +6 -0
  390. package/corpus/templates/design/changelog/2026-07-10-screen-overview-frames-now-use-a-clearer-interact-action-wit.md +6 -0
  391. package/corpus/templates/design/changelog/2026-07-10-start-designs-from-source-linked-material-3-carbon-or-primer.md +6 -0
  392. package/corpus/templates/design/changelog/2026-07-10-tweaks-guidance.md +6 -0
  393. package/corpus/templates/design/changelog/2026-07-10-uploaded-fig-files-can-now-become-editable-design-screens-wi.md +6 -0
  394. package/corpus/templates/design/e2e/helpers.ts +3 -3
  395. package/corpus/templates/design/package.json +2 -0
  396. package/corpus/templates/design/scripts/qa-figma-stress-import.ts +96 -0
  397. package/corpus/templates/design/server/db/index.ts +10 -0
  398. package/corpus/templates/design/server/db/schema.ts +44 -0
  399. package/corpus/templates/design/server/handlers/import-design-file.ts +114 -80
  400. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +19 -6
  401. package/corpus/templates/design/server/lib/design-export.ts +240 -3
  402. package/corpus/templates/design/server/lib/design-template-data.ts +89 -0
  403. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +250 -26
  404. package/corpus/templates/design/server/lib/fig-file-decoder.ts +839 -0
  405. package/corpus/templates/design/server/lib/fig-file-import.ts +287 -0
  406. package/corpus/templates/design/server/lib/fig-file-limits.ts +3 -0
  407. package/corpus/templates/design/server/lib/fig-file-to-html.ts +2568 -0
  408. package/corpus/templates/design/server/lib/figma-design-context.ts +359 -0
  409. package/corpus/templates/design/server/lib/figma-node-import.ts +478 -22
  410. package/corpus/templates/design/server/lib/figma-node-to-html.ts +554 -29
  411. package/corpus/templates/design/server/lib/local-figma-qa-upload.ts +112 -0
  412. package/corpus/templates/design/server/lib/verify-write-grant.ts +16 -2
  413. package/corpus/templates/design/server/lib/visible-clipboard-html.ts +13 -3
  414. package/corpus/templates/design/server/plugins/agent-chat.ts +7 -0
  415. package/corpus/templates/design/server/plugins/db.ts +42 -0
  416. package/corpus/templates/design/server/plugins/local-figma-qa-upload.ts +7 -0
  417. package/corpus/templates/design/server/register-secrets.ts +8 -8
  418. package/corpus/templates/design/server/routes/api/qa-figma-import-assets/[assetId].get.ts +46 -0
  419. package/corpus/templates/design/server/source-workspace.ts +104 -12
  420. package/corpus/templates/design/shared/code-layer.ts +105 -28
  421. package/corpus/templates/design/shared/design-system-templates.ts +301 -0
  422. package/corpus/templates/design/shared/design-template-presets.ts +195 -0
  423. package/corpus/templates/design/shared/figma-url.ts +8 -0
  424. package/corpus/templates/design/shared/html-integrity.ts +306 -0
  425. package/corpus/templates/design/shared/interaction-states.ts +318 -23
  426. package/corpus/templates/design/shared/locked-layers.ts +151 -0
  427. package/corpus/templates/dispatch/.agents/skills/capture-learnings/SKILL.md +22 -9
  428. package/corpus/templates/dispatch/app/i18n/zh-TW.ts +48 -0
  429. package/corpus/templates/dispatch/app/i18n-data.ts +536 -1
  430. package/corpus/templates/dispatch/changelog/2026-07-10-connect-slack-workspaces-with-oauth-and-manage-channel-ident.md +6 -0
  431. package/corpus/templates/dispatch/changelog/2026-07-10-microsoft-teams-bots-and-discord-slash-commands-can-now-conn.md +6 -0
  432. package/corpus/templates/dispatch/changelog/2026-07-10-slack-and-telegram-now-route-uptime-monitor-requests-to-anal.md +6 -0
  433. package/corpus/templates/forms/.agents/skills/capture-learnings/SKILL.md +22 -9
  434. package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +7 -0
  435. package/corpus/templates/forms/AGENTS.md +4 -0
  436. package/corpus/templates/forms/actions/create-form.ts +44 -7
  437. package/corpus/templates/forms/actions/lib/assert-publishable-form.ts +38 -0
  438. package/corpus/templates/forms/actions/patch-form-fields.ts +4 -0
  439. package/corpus/templates/forms/actions/update-form.ts +2 -36
  440. package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
  441. package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
  442. package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
  443. package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
  444. package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
  445. package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
  446. package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
  447. package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
  448. package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
  449. package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
  450. package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
  451. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +17 -0
  452. package/corpus/templates/forms/changelog/2026-07-10-agent-created-published-forms-now-return-the-anonymous-publi.md +6 -0
  453. package/corpus/templates/forms/changelog/2026-07-10-published-forms-can-no-longer-be-edited-into-an-unusable-emp.md +6 -0
  454. package/corpus/templates/forms/server/handlers/submissions.ts +4 -3
  455. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
  456. package/corpus/templates/forms/shared/types.ts +5 -0
  457. package/corpus/templates/mail/.agents/skills/capture-learnings/SKILL.md +22 -9
  458. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  459. package/corpus/templates/mail/changelog/2026-07-10-mail-navigation-now-uses-a-clean-borderless-drawer.md +6 -0
  460. package/corpus/templates/plan/.agents/skills/capture-learnings/SKILL.md +22 -9
  461. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +4 -1
  462. package/corpus/templates/plan/changelog/2026-07-10-local-plans-now-keep-valid-content-visible-when-one-generate.md +6 -0
  463. package/corpus/templates/plan/server/lib/local-plan-files.ts +4 -1
  464. package/corpus/templates/slides/.agents/skills/capture-learnings/SKILL.md +22 -9
  465. package/dist/a2a/artifact-response.d.ts.map +1 -1
  466. package/dist/a2a/artifact-response.js +88 -2
  467. package/dist/a2a/artifact-response.js.map +1 -1
  468. package/dist/a2a/client.d.ts +9 -0
  469. package/dist/a2a/client.d.ts.map +1 -1
  470. package/dist/a2a/client.js +1 -0
  471. package/dist/a2a/client.js.map +1 -1
  472. package/dist/a2a/handlers.d.ts.map +1 -1
  473. package/dist/a2a/handlers.js +24 -69
  474. package/dist/a2a/handlers.js.map +1 -1
  475. package/dist/a2a/types.d.ts +2 -0
  476. package/dist/a2a/types.d.ts.map +1 -1
  477. package/dist/a2a/types.js.map +1 -1
  478. package/dist/agent/durable-background.d.ts +11 -0
  479. package/dist/agent/durable-background.d.ts.map +1 -1
  480. package/dist/agent/durable-background.js +17 -0
  481. package/dist/agent/durable-background.js.map +1 -1
  482. package/dist/agent/production-agent.d.ts +5 -3
  483. package/dist/agent/production-agent.d.ts.map +1 -1
  484. package/dist/agent/production-agent.js +34 -8
  485. package/dist/agent/production-agent.js.map +1 -1
  486. package/dist/agent/run-manager.d.ts +44 -2
  487. package/dist/agent/run-manager.d.ts.map +1 -1
  488. package/dist/agent/run-manager.js +124 -18
  489. package/dist/agent/run-manager.js.map +1 -1
  490. package/dist/agent/run-store.d.ts +109 -0
  491. package/dist/agent/run-store.d.ts.map +1 -1
  492. package/dist/agent/run-store.js +274 -37
  493. package/dist/agent/run-store.js.map +1 -1
  494. package/dist/agent/types.d.ts +22 -0
  495. package/dist/agent/types.d.ts.map +1 -1
  496. package/dist/agent/types.js.map +1 -1
  497. package/dist/audit/record.d.ts.map +1 -1
  498. package/dist/audit/record.js +29 -0
  499. package/dist/audit/record.js.map +1 -1
  500. package/dist/audit/store.d.ts.map +1 -1
  501. package/dist/audit/store.js +62 -3
  502. package/dist/audit/store.js.map +1 -1
  503. package/dist/audit/types.d.ts +13 -0
  504. package/dist/audit/types.d.ts.map +1 -1
  505. package/dist/audit/types.js.map +1 -1
  506. package/dist/automation/index.d.ts +193 -0
  507. package/dist/automation/index.d.ts.map +1 -0
  508. package/dist/automation/index.js +464 -0
  509. package/dist/automation/index.js.map +1 -0
  510. package/dist/cli/create.d.ts.map +1 -1
  511. package/dist/cli/create.js +10 -3
  512. package/dist/cli/create.js.map +1 -1
  513. package/dist/cli/design-connect.d.ts.map +1 -1
  514. package/dist/cli/design-connect.js +492 -35
  515. package/dist/cli/design-connect.js.map +1 -1
  516. package/dist/client/AgentPanel.d.ts +7 -1
  517. package/dist/client/AgentPanel.d.ts.map +1 -1
  518. package/dist/client/AgentPanel.js +32 -8
  519. package/dist/client/AgentPanel.js.map +1 -1
  520. package/dist/client/AssistantChat.d.ts +15 -0
  521. package/dist/client/AssistantChat.d.ts.map +1 -1
  522. package/dist/client/AssistantChat.js +115 -47
  523. package/dist/client/AssistantChat.js.map +1 -1
  524. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  525. package/dist/client/MultiTabAssistantChat.js +1 -1
  526. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  527. package/dist/client/RunStuckBanner.d.ts +14 -1
  528. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  529. package/dist/client/RunStuckBanner.js +35 -7
  530. package/dist/client/RunStuckBanner.js.map +1 -1
  531. package/dist/client/agent-chat-adapter.d.ts +4 -1
  532. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  533. package/dist/client/agent-chat-adapter.js +97 -16
  534. package/dist/client/agent-chat-adapter.js.map +1 -1
  535. package/dist/client/automation.d.ts +14 -0
  536. package/dist/client/automation.d.ts.map +1 -0
  537. package/dist/client/automation.js +9 -0
  538. package/dist/client/automation.js.map +1 -0
  539. package/dist/client/chat/run-recovery.d.ts +4 -0
  540. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  541. package/dist/client/chat/run-recovery.js +12 -10
  542. package/dist/client/chat/run-recovery.js.map +1 -1
  543. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  544. package/dist/client/composer/TiptapComposer.js +8 -0
  545. package/dist/client/composer/TiptapComposer.js.map +1 -1
  546. package/dist/client/index.d.ts +2 -0
  547. package/dist/client/index.d.ts.map +1 -1
  548. package/dist/client/index.js +2 -0
  549. package/dist/client/index.js.map +1 -1
  550. package/dist/client/integrations/api.d.ts +115 -0
  551. package/dist/client/integrations/api.d.ts.map +1 -0
  552. package/dist/client/integrations/api.js +123 -0
  553. package/dist/client/integrations/api.js.map +1 -0
  554. package/dist/client/integrations/index.d.ts +1 -0
  555. package/dist/client/integrations/index.d.ts.map +1 -1
  556. package/dist/client/integrations/index.js +1 -0
  557. package/dist/client/integrations/index.js.map +1 -1
  558. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  559. package/dist/client/transcription/use-live-transcription.js +59 -5
  560. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  561. package/dist/client/use-run-stuck-detection.d.ts +16 -1
  562. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  563. package/dist/client/use-run-stuck-detection.js +23 -4
  564. package/dist/client/use-run-stuck-detection.js.map +1 -1
  565. package/dist/collab/awareness.d.ts +2 -2
  566. package/dist/collab/awareness.d.ts.map +1 -1
  567. package/dist/deploy/build.d.ts.map +1 -1
  568. package/dist/deploy/build.js +49 -4
  569. package/dist/deploy/build.js.map +1 -1
  570. package/dist/deploy/workspace-deploy.d.ts.map +1 -1
  571. package/dist/deploy/workspace-deploy.js +31 -2
  572. package/dist/deploy/workspace-deploy.js.map +1 -1
  573. package/dist/file-upload/types.d.ts +2 -0
  574. package/dist/file-upload/types.d.ts.map +1 -1
  575. package/dist/file-upload/types.js.map +1 -1
  576. package/dist/index.d.ts +2 -0
  577. package/dist/index.d.ts.map +1 -1
  578. package/dist/index.js +2 -0
  579. package/dist/index.js.map +1 -1
  580. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  581. package/dist/integrations/a2a-continuations-store.js +6 -4
  582. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  583. package/dist/integrations/adapters/discord.d.ts +3 -0
  584. package/dist/integrations/adapters/discord.d.ts.map +1 -0
  585. package/dist/integrations/adapters/discord.js +261 -0
  586. package/dist/integrations/adapters/discord.js.map +1 -0
  587. package/dist/integrations/adapters/index.d.ts +4 -0
  588. package/dist/integrations/adapters/index.d.ts.map +1 -0
  589. package/dist/integrations/adapters/index.js +26 -0
  590. package/dist/integrations/adapters/index.js.map +1 -0
  591. package/dist/integrations/adapters/microsoft-teams.d.ts +5 -0
  592. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -0
  593. package/dist/integrations/adapters/microsoft-teams.js +295 -0
  594. package/dist/integrations/adapters/microsoft-teams.js.map +1 -0
  595. package/dist/integrations/adapters/slack.d.ts +8 -2
  596. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  597. package/dist/integrations/adapters/slack.js +736 -27
  598. package/dist/integrations/adapters/slack.js.map +1 -1
  599. package/dist/integrations/adapters/telegram.d.ts +0 -2
  600. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  601. package/dist/integrations/adapters/telegram.js +49 -15
  602. package/dist/integrations/adapters/telegram.js.map +1 -1
  603. package/dist/integrations/adapters/whatsapp.d.ts +0 -2
  604. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  605. package/dist/integrations/adapters/whatsapp.js +40 -17
  606. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  607. package/dist/integrations/catalog.d.ts +75 -0
  608. package/dist/integrations/catalog.d.ts.map +1 -0
  609. package/dist/integrations/catalog.js +451 -0
  610. package/dist/integrations/catalog.js.map +1 -0
  611. package/dist/integrations/controls-store.d.ts +44 -0
  612. package/dist/integrations/controls-store.d.ts.map +1 -0
  613. package/dist/integrations/controls-store.js +129 -0
  614. package/dist/integrations/controls-store.js.map +1 -0
  615. package/dist/integrations/index.d.ts +13 -1
  616. package/dist/integrations/index.d.ts.map +1 -1
  617. package/dist/integrations/index.js +9 -0
  618. package/dist/integrations/index.js.map +1 -1
  619. package/dist/integrations/installations-store.d.ts +112 -0
  620. package/dist/integrations/installations-store.d.ts.map +1 -0
  621. package/dist/integrations/installations-store.js +493 -0
  622. package/dist/integrations/installations-store.js.map +1 -0
  623. package/dist/integrations/integration-memory.d.ts +24 -0
  624. package/dist/integrations/integration-memory.d.ts.map +1 -0
  625. package/dist/integrations/integration-memory.js +136 -0
  626. package/dist/integrations/integration-memory.js.map +1 -0
  627. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  628. package/dist/integrations/pending-tasks-retry-job.js +6 -5
  629. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  630. package/dist/integrations/pending-tasks-store.d.ts +17 -0
  631. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  632. package/dist/integrations/pending-tasks-store.js +71 -5
  633. package/dist/integrations/pending-tasks-store.js.map +1 -1
  634. package/dist/integrations/plugin.d.ts.map +1 -1
  635. package/dist/integrations/plugin.js +520 -35
  636. package/dist/integrations/plugin.js.map +1 -1
  637. package/dist/integrations/scope-store.d.ts +65 -0
  638. package/dist/integrations/scope-store.d.ts.map +1 -0
  639. package/dist/integrations/scope-store.js +361 -0
  640. package/dist/integrations/scope-store.js.map +1 -0
  641. package/dist/integrations/slack-manifest.d.ts +66 -0
  642. package/dist/integrations/slack-manifest.d.ts.map +1 -0
  643. package/dist/integrations/slack-manifest.js +70 -0
  644. package/dist/integrations/slack-manifest.js.map +1 -0
  645. package/dist/integrations/slack-oauth.d.ts +99 -0
  646. package/dist/integrations/slack-oauth.d.ts.map +1 -0
  647. package/dist/integrations/slack-oauth.js +226 -0
  648. package/dist/integrations/slack-oauth.js.map +1 -0
  649. package/dist/integrations/types.d.ts +144 -0
  650. package/dist/integrations/types.d.ts.map +1 -1
  651. package/dist/integrations/types.js +16 -1
  652. package/dist/integrations/types.js.map +1 -1
  653. package/dist/integrations/usage-budget-store.d.ts +84 -0
  654. package/dist/integrations/usage-budget-store.d.ts.map +1 -0
  655. package/dist/integrations/usage-budget-store.js +650 -0
  656. package/dist/integrations/usage-budget-store.js.map +1 -0
  657. package/dist/integrations/webhook-handler.d.ts +4 -0
  658. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  659. package/dist/integrations/webhook-handler.js +415 -47
  660. package/dist/integrations/webhook-handler.js.map +1 -1
  661. package/dist/jobs/scheduler.d.ts +6 -0
  662. package/dist/jobs/scheduler.d.ts.map +1 -1
  663. package/dist/jobs/scheduler.js +83 -4
  664. package/dist/jobs/scheduler.js.map +1 -1
  665. package/dist/jobs/tools.d.ts.map +1 -1
  666. package/dist/jobs/tools.js +43 -11
  667. package/dist/jobs/tools.js.map +1 -1
  668. package/dist/provider-api/index.d.ts +4 -4
  669. package/dist/provider-api/index.d.ts.map +1 -1
  670. package/dist/provider-api/index.js +72 -1
  671. package/dist/provider-api/index.js.map +1 -1
  672. package/dist/resources/handlers.d.ts.map +1 -1
  673. package/dist/resources/handlers.js +57 -15
  674. package/dist/resources/handlers.js.map +1 -1
  675. package/dist/resources/script-helpers.d.ts.map +1 -1
  676. package/dist/resources/script-helpers.js +34 -8
  677. package/dist/resources/script-helpers.js.map +1 -1
  678. package/dist/resources/store.d.ts +10 -0
  679. package/dist/resources/store.d.ts.map +1 -1
  680. package/dist/resources/store.js +54 -47
  681. package/dist/resources/store.js.map +1 -1
  682. package/dist/scripts/call-agent.d.ts.map +1 -1
  683. package/dist/scripts/call-agent.js +106 -1
  684. package/dist/scripts/call-agent.js.map +1 -1
  685. package/dist/server/agent-chat-plugin.d.ts +15 -4
  686. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  687. package/dist/server/agent-chat-plugin.js +248 -113
  688. package/dist/server/agent-chat-plugin.js.map +1 -1
  689. package/dist/server/auth.d.ts.map +1 -1
  690. package/dist/server/auth.js +10 -14
  691. package/dist/server/auth.js.map +1 -1
  692. package/dist/server/builder-design-systems.d.ts +3 -1
  693. package/dist/server/builder-design-systems.d.ts.map +1 -1
  694. package/dist/server/builder-design-systems.js +9 -2
  695. package/dist/server/builder-design-systems.js.map +1 -1
  696. package/dist/server/edge.d.ts +1 -1
  697. package/dist/server/edge.d.ts.map +1 -1
  698. package/dist/server/edge.js +1 -1
  699. package/dist/server/edge.js.map +1 -1
  700. package/dist/server/index.d.ts +2 -1
  701. package/dist/server/index.d.ts.map +1 -1
  702. package/dist/server/index.js +2 -1
  703. package/dist/server/index.js.map +1 -1
  704. package/dist/server/request-context.d.ts +18 -0
  705. package/dist/server/request-context.d.ts.map +1 -1
  706. package/dist/server/request-context.js.map +1 -1
  707. package/dist/server/social-og-image.d.ts.map +1 -1
  708. package/dist/server/social-og-image.js +3 -2
  709. package/dist/server/social-og-image.js.map +1 -1
  710. package/dist/server/transcribe-voice.d.ts +1 -1
  711. package/dist/styles/agent-native.css +62 -44
  712. package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  713. package/dist/templates/default/AGENTS.md +7 -3
  714. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  715. package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  716. package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  717. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  718. package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  719. package/dist/templates/workspace-core/AGENTS.md +3 -2
  720. package/dist/usage/store.d.ts +7 -0
  721. package/dist/usage/store.d.ts.map +1 -1
  722. package/dist/usage/store.js +24 -3
  723. package/dist/usage/store.js.map +1 -1
  724. package/docs/content/automation-connectors.mdx +80 -0
  725. package/docs/content/extensions.mdx +8 -0
  726. package/docs/content/locales/ar-SA/extensions.mdx +8 -0
  727. package/docs/content/locales/ar-SA/messaging.mdx +84 -15
  728. package/docs/content/locales/ar-SA/template-clips.mdx +1 -0
  729. package/docs/content/locales/ar-SA/template-design.mdx +1 -0
  730. package/docs/content/locales/de-DE/extensions.mdx +10 -0
  731. package/docs/content/locales/de-DE/messaging.mdx +92 -15
  732. package/docs/content/locales/de-DE/template-clips.mdx +1 -0
  733. package/docs/content/locales/de-DE/template-design.mdx +1 -0
  734. package/docs/content/locales/es-ES/extensions.mdx +10 -0
  735. package/docs/content/locales/es-ES/messaging.mdx +91 -15
  736. package/docs/content/locales/es-ES/template-clips.mdx +1 -0
  737. package/docs/content/locales/es-ES/template-design.mdx +1 -0
  738. package/docs/content/locales/fr-FR/extensions.mdx +10 -0
  739. package/docs/content/locales/fr-FR/messaging.mdx +91 -15
  740. package/docs/content/locales/fr-FR/template-clips.mdx +1 -0
  741. package/docs/content/locales/fr-FR/template-design.mdx +1 -0
  742. package/docs/content/locales/hi-IN/extensions.mdx +8 -0
  743. package/docs/content/locales/hi-IN/messaging.mdx +81 -15
  744. package/docs/content/locales/hi-IN/template-clips.mdx +1 -0
  745. package/docs/content/locales/hi-IN/template-design.mdx +1 -0
  746. package/docs/content/locales/ja-JP/extensions.mdx +9 -0
  747. package/docs/content/locales/ja-JP/messaging.mdx +81 -15
  748. package/docs/content/locales/ja-JP/template-clips.mdx +1 -0
  749. package/docs/content/locales/ja-JP/template-design.mdx +1 -0
  750. package/docs/content/locales/ko-KR/extensions.mdx +8 -0
  751. package/docs/content/locales/ko-KR/messaging.mdx +80 -15
  752. package/docs/content/locales/ko-KR/template-clips.mdx +1 -0
  753. package/docs/content/locales/ko-KR/template-design.mdx +1 -0
  754. package/docs/content/locales/pt-BR/extensions.mdx +9 -0
  755. package/docs/content/locales/pt-BR/messaging.mdx +88 -15
  756. package/docs/content/locales/pt-BR/template-clips.mdx +1 -0
  757. package/docs/content/locales/pt-BR/template-design.mdx +1 -0
  758. package/docs/content/locales/zh-CN/extensions.mdx +6 -0
  759. package/docs/content/locales/zh-CN/messaging.mdx +78 -15
  760. package/docs/content/locales/zh-CN/template-clips.mdx +1 -0
  761. package/docs/content/locales/zh-CN/template-design.mdx +1 -0
  762. package/docs/content/locales/zh-TW/extensions.mdx +6 -0
  763. package/docs/content/locales/zh-TW/messaging.mdx +78 -15
  764. package/docs/content/locales/zh-TW/template-clips.mdx +1 -0
  765. package/docs/content/locales/zh-TW/template-design.mdx +1 -0
  766. package/docs/content/messaging.mdx +227 -45
  767. package/docs/content/template-clips.mdx +3 -0
  768. package/docs/content/template-design.mdx +4 -0
  769. package/package.json +5 -1
  770. package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  771. package/src/templates/default/AGENTS.md +7 -3
  772. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  773. package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  774. package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  775. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  776. package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  777. package/src/templates/workspace-core/AGENTS.md +3 -2
@@ -3,18 +3,45 @@ import { createError, getHeader, readRawBody } from "h3";
3
3
 
4
4
  import type { EnvKeyConfig } from "../../server/create-server.js";
5
5
  import { resolveSecret } from "../../server/credential-provider.js";
6
+ import { getRequestContext } from "../../server/request-context.js";
7
+ import { getIntegrationRequestContext } from "../../server/request-context.js";
8
+ import { createIntegrationControl } from "../controls-store.js";
9
+ import {
10
+ getActiveIntegrationInstallationByKey,
11
+ getActiveIntegrationInstallationForTenant,
12
+ listIntegrationInstallations,
13
+ resolveIntegrationTokenBundle,
14
+ } from "../installations-store.js";
15
+ import { hasActivePendingTask } from "../pending-tasks-store.js";
16
+ import { slackInstallationKey } from "../slack-oauth.js";
6
17
  import type {
7
18
  PlatformAdapter,
8
19
  IncomingMessage,
9
20
  OutgoingMessage,
10
21
  IntegrationStatus,
11
22
  OutboundTarget,
23
+ PlatformRunProgress,
24
+ IntegrationContextMessage,
25
+ IntegrationFileReference,
12
26
  } from "../types.js";
13
27
 
14
28
  /** Slack's max message length */
15
29
  const SLACK_MAX_LENGTH = 4000;
16
30
  const SLACK_SECTION_TEXT_MAX_LENGTH = 3000;
17
31
  const SLACK_API_TIMEOUT_MS = 10_000;
32
+ // Permalink lookup happens on Slack's acknowledgement path, so keep it well
33
+ // inside Slack's three-second Events API deadline. Failure is non-fatal: the
34
+ // normalized message still carries channel/thread ids for replies.
35
+ const SLACK_PERMALINK_TIMEOUT_MS = 1_000;
36
+ const SLACK_CONTEXT_MESSAGE_LIMIT = 15;
37
+ const SLACK_CONTEXT_TEXT_LIMIT = 2_000;
38
+
39
+ export interface SlackAdapterOptions {
40
+ /** Resolve the bot token for the exact Slack installation. */
41
+ resolveBotToken?: (incoming: IncomingMessage) => Promise<string | undefined>;
42
+ /** Override active-thread detection for hosted adapters/tests. */
43
+ isThreadActive?: (incoming: IncomingMessage) => Promise<boolean>;
44
+ }
18
45
 
19
46
  /**
20
47
  * Create a Slack platform adapter.
@@ -36,20 +63,52 @@ const SLACK_API_TIMEOUT_MS = 10_000;
36
63
  * (`api_app_id`) to additionally pin events to. Useful when the same
37
64
  * signing secret rotation surfaces multiple app installs.
38
65
  */
39
- export function slackAdapter(): PlatformAdapter {
66
+ export function slackAdapter(
67
+ options: SlackAdapterOptions = {},
68
+ ): PlatformAdapter {
69
+ const resolveBotToken = async (incoming: IncomingMessage) =>
70
+ (await options.resolveBotToken?.(incoming)) ??
71
+ (await resolveManagedSlackBotToken(incoming)) ??
72
+ (await resolveSecret("SLACK_BOT_TOKEN")) ??
73
+ undefined;
74
+
40
75
  return {
41
76
  platform: "slack",
42
77
  label: "Slack",
78
+ capabilities: {
79
+ replyText: true,
80
+ proactiveMessages: true,
81
+ nativeThreads: true,
82
+ contextualReplies: true,
83
+ deferredWebhookResponse: false,
84
+ interactionOnly: false,
85
+ nativeContextHydration: true,
86
+ liveRunProgress: true,
87
+ },
43
88
 
44
89
  getRequiredEnvKeys(): EnvKeyConfig[] {
45
90
  return [
46
91
  {
47
92
  key: "SLACK_BOT_TOKEN",
48
93
  label: "Slack Bot Token",
49
- required: true,
94
+ required: false,
50
95
  helpText:
51
96
  "In your Slack app's left nav: OAuth & Permissions → Bot User OAuth Token (starts with `xoxb-`).",
52
97
  },
98
+ {
99
+ key: "SLACK_CLIENT_ID",
100
+ label: "Slack OAuth Client ID",
101
+ required: false,
102
+ helpText:
103
+ "Slack app Basic Information → App Credentials → Client ID.",
104
+ },
105
+ {
106
+ key: "SLACK_CLIENT_SECRET",
107
+ label: "Slack OAuth Client Secret",
108
+ required: false,
109
+ helpText:
110
+ "Slack app Basic Information → App Credentials → Client Secret.",
111
+ },
53
112
  {
54
113
  key: "SLACK_SIGNING_SECRET",
55
114
  label: "Slack Signing Secret",
@@ -132,7 +191,7 @@ export function slackAdapter(): PlatformAdapter {
132
191
  // handler, so this is purely a tenant-isolation gate (not a forgery
133
192
  // defense). When unset in production we surface a one-time warning
134
193
  // recommending it be configured.
135
- enforceWorkspaceAllowlist(payload);
194
+ await enforceWorkspaceAllowlist(payload);
136
195
 
137
196
  // Handle Events API wrapper
138
197
  if (payload.type === "event_callback") {
@@ -145,38 +204,104 @@ export function slackAdapter(): PlatformAdapter {
145
204
  if (e.subtype === "message_changed" || e.subtype === "message_deleted")
146
205
  return null;
147
206
 
148
- // Handle both direct messages and app_mentions
207
+ // Handle DMs and explicit mentions. Ordinary channel replies are only
208
+ // accepted while this exact workspace-qualified thread has queued or
209
+ // executing work; broad message subscriptions must never invoke the
210
+ // agent for general channel chatter.
149
211
  const text = e.text?.trim();
150
212
  if (!text) return null;
151
213
 
214
+ const teamId =
215
+ typeof payload.team_id === "string" ? payload.team_id : "unknown";
216
+ const apiAppId =
217
+ typeof payload.api_app_id === "string"
218
+ ? payload.api_app_id
219
+ : "unknown";
220
+ const agentContext = normalizeSlackAgentContext(e.app_context, teamId);
221
+ const isDm =
222
+ typeof e.channel_type === "string"
223
+ ? e.channel_type === "im"
224
+ : typeof e.channel === "string" && e.channel.startsWith("D");
225
+ const isMention = e.type === "app_mention";
226
+ const isThreadReply = !isDm && !isMention && !!e.thread_ts;
227
+ if (!isDm && !isMention && !isThreadReply) return null;
228
+
152
229
  // Remove bot mention from text (e.g., "<@U123> do something" → "do something")
153
230
  const cleanText = text.replace(/<@[A-Z0-9]+>/g, "").trim();
154
231
  if (!cleanText) return null;
155
232
 
156
233
  // Thread ID: use thread_ts if in a thread, otherwise message ts
157
234
  const threadTs = e.thread_ts || e.ts;
158
- const externalThreadId = `${e.channel}:${threadTs}`;
159
-
160
- return {
235
+ const externalThreadId = `${apiAppId}:${teamId}:${e.channel}:${threadTs}`;
236
+ const partialIncoming: IncomingMessage = {
161
237
  platform: "slack",
162
238
  externalThreadId,
163
239
  text: cleanText,
164
240
  senderName: e.user,
165
241
  senderId: e.user,
242
+ triggerKind: isDm ? "dm" : isMention ? "mention" : "thread_reply",
243
+ conversationType: isDm ? "dm" : "unknown",
244
+ tenantId: teamId,
245
+ actorTrust: { memberType: "unknown", verified: true },
166
246
  platformContext: {
167
247
  channelId: e.channel,
168
- threadTs: threadTs,
248
+ channelType: e.channel_type,
249
+ threadTs,
169
250
  messageTs: e.ts,
170
- teamId: payload.team_id,
251
+ teamId,
252
+ apiAppId,
253
+ enterpriseId:
254
+ typeof payload.enterprise_id === "string"
255
+ ? payload.enterprise_id
256
+ : undefined,
171
257
  eventId: payload.event_id,
258
+ ...(agentContext
259
+ ? {
260
+ agentContext: agentContext.entities,
261
+ activeContextChannelId: agentContext.channelId,
262
+ }
263
+ : {}),
172
264
  },
265
+ threadRef: threadTs,
266
+ replyRef: e.ts,
173
267
  timestamp: Math.floor(parseFloat(e.ts) * 1000),
174
268
  };
269
+
270
+ if (isThreadReply) {
271
+ const active = options.isThreadActive
272
+ ? await options.isThreadActive(partialIncoming)
273
+ : await hasActivePendingTask("slack", externalThreadId);
274
+ if (!active) return null;
275
+ }
276
+
277
+ const token = await resolveBotToken(partialIncoming);
278
+ const threadPermalink = await resolveSlackThreadPermalink(
279
+ e.channel,
280
+ threadTs,
281
+ token,
282
+ );
283
+
284
+ return {
285
+ ...partialIncoming,
286
+ platformContext: {
287
+ ...partialIncoming.platformContext,
288
+ ...(threadPermalink ? { threadPermalink } : {}),
289
+ },
290
+ ...(threadPermalink ? { sourceUrl: threadPermalink } : {}),
291
+ };
175
292
  }
176
293
 
177
294
  return null;
178
295
  },
179
296
 
297
+ getLegacyExternalThreadIds(incoming: IncomingMessage): string[] {
298
+ const channelId = incoming.platformContext.channelId;
299
+ const threadTs = incoming.platformContext.threadTs;
300
+ return typeof channelId === "string" && typeof threadTs === "string"
301
+ ? [`${channelId}:${threadTs}`]
302
+ : [];
303
+ },
304
+
180
305
  async postProcessingPlaceholder(
181
306
  incoming: IncomingMessage,
182
307
  ): Promise<{ placeholderRef: string } | null> {
@@ -186,26 +311,43 @@ export function slackAdapter(): PlatformAdapter {
186
311
  // redundant and added an extra chunk that we then had to overwrite.
187
312
  // We just set the native status and return null so sendResponse posts
188
313
  // the final reply as a fresh message.
189
- const token = await resolveSecret("SLACK_BOT_TOKEN");
314
+ const token = await resolveBotToken(incoming);
190
315
  if (!token) return null;
191
316
 
192
317
  const channelId = incoming.platformContext.channelId as string;
193
318
  const threadTs = incoming.platformContext.threadTs as string;
194
319
  if (!channelId || !threadTs) return null;
195
320
 
196
- // Best-effort: flip the native AI-assistant "is thinking…" status bar
197
- // in the channel input area. Requires `assistant:write` scope on the
198
- // app otherwise silently no-ops.
321
+ // Best-effort: flip the native Agent "is thinking…" status bar in the
322
+ // channel input area. Slack accepts chat:write for this method. The
323
+ // canonical manifest separately requests assistant:write for Agent View
324
+ // and app_context_changed events.
199
325
  setSlackAssistantStatus(token, channelId, threadTs, "is thinking…");
200
326
  return null;
201
327
  },
202
328
 
329
+ async hydrateIncomingMessage(
330
+ incoming: IncomingMessage,
331
+ ): Promise<IncomingMessage> {
332
+ const token = await resolveBotToken(incoming);
333
+ if (!token) return incoming;
334
+ return hydrateSlackContext(token, incoming);
335
+ },
336
+
337
+ async startRunProgress(
338
+ incoming: IncomingMessage,
339
+ ): Promise<PlatformRunProgress | null> {
340
+ const token = await resolveBotToken(incoming);
341
+ if (!token) return null;
342
+ return startSlackRunProgress(token, incoming);
343
+ },
344
+
203
345
  async sendResponse(
204
346
  message: OutgoingMessage,
205
347
  context: IncomingMessage,
206
348
  opts?: { placeholderRef?: string },
207
349
  ): Promise<void> {
208
- const token = await resolveSecret("SLACK_BOT_TOKEN");
350
+ const token = await resolveBotToken(context);
209
351
  if (!token) {
210
352
  console.error("[slack] SLACK_BOT_TOKEN not configured");
211
353
  return;
@@ -298,7 +440,19 @@ export function slackAdapter(): PlatformAdapter {
298
440
  message: OutgoingMessage,
299
441
  target: OutboundTarget,
300
442
  ): Promise<void> {
301
- const token = await resolveSecret("SLACK_BOT_TOKEN");
443
+ const targetContext: IncomingMessage = {
444
+ platform: "slack",
445
+ externalThreadId: `${target.tenantId ?? "unknown"}:${target.destination}:${target.threadRef ?? "root"}`,
446
+ text: "",
447
+ platformContext: {
448
+ channelId: target.destination,
449
+ threadTs: target.threadRef,
450
+ teamId: target.tenantId,
451
+ },
452
+ tenantId: target.tenantId,
453
+ timestamp: Date.now(),
454
+ };
455
+ const token = await resolveBotToken(targetContext);
302
456
  if (!token) {
303
457
  console.error("[slack] SLACK_BOT_TOKEN not configured");
304
458
  return;
@@ -351,7 +505,21 @@ export function slackAdapter(): PlatformAdapter {
351
505
  async getStatus(_baseUrl?: string): Promise<IntegrationStatus> {
352
506
  const hasToken = !!(await resolveSecret("SLACK_BOT_TOKEN"));
353
507
  const hasSecret = !!(await resolveSecret("SLACK_SIGNING_SECRET"));
354
- const configured = hasToken && hasSecret;
508
+ const ctx = getRequestContext();
509
+ const managedInstallations = ctx?.userEmail
510
+ ? await listIntegrationInstallations(
511
+ {
512
+ userEmail: ctx.userEmail,
513
+ orgId: ctx.orgId ?? null,
514
+ },
515
+ "slack",
516
+ ).catch(() => [])
517
+ : [];
518
+ const connectedInstallations = managedInstallations.filter(
519
+ (installation) => installation.status === "connected",
520
+ );
521
+ const configured =
522
+ hasSecret && (hasToken || connectedInstallations.length > 0);
355
523
 
356
524
  return {
357
525
  platform: "slack",
@@ -361,9 +529,18 @@ export function slackAdapter(): PlatformAdapter {
361
529
  details: {
362
530
  hasToken,
363
531
  hasSecret,
532
+ managedInstallationCount: connectedInstallations.length,
533
+ managedInstallations: connectedInstallations.map((installation) => ({
534
+ id: installation.id,
535
+ teamId: installation.teamId,
536
+ teamName: installation.teamName,
537
+ enterpriseId: installation.enterpriseId,
538
+ health: installation.health,
539
+ scopes: installation.scopes,
540
+ })),
364
541
  },
365
542
  error: !configured
366
- ? "Save SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET in settings"
543
+ ? "Connect a Slack workspace with OAuth or save a bot token and signing secret"
367
544
  : undefined,
368
545
  };
369
546
  },
@@ -386,6 +563,50 @@ function parseAllowlistEnv(name: string): Set<string> | null {
386
563
  return new Set(values);
387
564
  }
388
565
 
566
+ async function resolveManagedSlackBotToken(
567
+ incoming: IncomingMessage,
568
+ ): Promise<string | undefined> {
569
+ const teamId =
570
+ typeof incoming.platformContext.teamId === "string"
571
+ ? incoming.platformContext.teamId
572
+ : incoming.tenantId;
573
+ const apiAppId =
574
+ typeof incoming.platformContext.apiAppId === "string"
575
+ ? incoming.platformContext.apiAppId
576
+ : undefined;
577
+ const enterpriseId =
578
+ typeof incoming.platformContext.enterpriseId === "string"
579
+ ? incoming.platformContext.enterpriseId
580
+ : undefined;
581
+ if (!teamId && !enterpriseId) return undefined;
582
+ try {
583
+ let installation = apiAppId
584
+ ? await getActiveIntegrationInstallationByKey(
585
+ "slack",
586
+ slackInstallationKey({ teamId, enterpriseId, apiAppId }),
587
+ )
588
+ : null;
589
+ if (!installation && teamId) {
590
+ installation = await getActiveIntegrationInstallationForTenant(
591
+ "slack",
592
+ teamId,
593
+ );
594
+ }
595
+ if (!installation && enterpriseId) {
596
+ installation = await getActiveIntegrationInstallationForTenant(
597
+ "slack",
598
+ enterpriseId,
599
+ );
600
+ }
601
+ const key =
602
+ installation?.installationKey ??
603
+ slackInstallationKey({ teamId, enterpriseId, apiAppId });
604
+ return (await resolveIntegrationTokenBundle("slack", key))?.accessToken;
605
+ } catch {
606
+ return undefined;
607
+ }
608
+ }
609
+
389
610
  let _missingAllowlistWarned = false;
390
611
 
391
612
  /**
@@ -414,7 +635,7 @@ let _missingAllowlistWarned = false;
414
635
  * received, which the integrations plugin surfaces to the caller as
415
636
  * "Unrecognized Slack workspace" without enqueuing the event.
416
637
  */
417
- function enforceWorkspaceAllowlist(payload: any): void {
638
+ async function enforceWorkspaceAllowlist(payload: any): Promise<void> {
418
639
  const teamId =
419
640
  typeof payload?.team_id === "string" ? payload.team_id : undefined;
420
641
  const apiAppId =
@@ -425,10 +646,17 @@ function enforceWorkspaceAllowlist(payload: any): void {
425
646
 
426
647
  if (!allowedTeamIds) {
427
648
  if (process.env.NODE_ENV === "production") {
428
- throw createError({
429
- statusCode: 401,
430
- statusMessage: "Slack workspace allowlist is not configured",
431
- });
649
+ let managed = false;
650
+ try {
651
+ const key = slackInstallationKey({ teamId, apiAppId });
652
+ managed = !!(await getActiveIntegrationInstallationByKey("slack", key));
653
+ } catch {}
654
+ if (!managed) {
655
+ throw createError({
656
+ statusCode: 401,
657
+ statusMessage: "Slack workspace is not connected",
658
+ });
659
+ }
432
660
  }
433
661
  if (!_missingAllowlistWarned) {
434
662
  _missingAllowlistWarned = true;
@@ -569,10 +797,10 @@ function markdownToSlackMrkdwn(text: string): string {
569
797
  }
570
798
 
571
799
  /**
572
- * Optionally set Slack's native AI-assistant status indicator (the small
573
- * "is thinking…" line under the message composer) for an app configured
574
- * with the `assistant:write` scope. Pure best-effort fails silently for
575
- * apps that aren't set up as AI assistants.
800
+ * Optionally set Slack's native Agent status indicator (the small "is
801
+ * thinking…" line under the message composer). The method uses chat:write;
802
+ * Agent View itself is configured separately by the Slack app manifest.
803
+ * Pure best-effort failures never block the response.
576
804
  */
577
805
  function setSlackAssistantStatus(
578
806
  token: string,
@@ -671,11 +899,12 @@ async function postFresh(
671
899
  async function slackApiFetch(
672
900
  url: string,
673
901
  init: RequestInit,
902
+ timeoutMs = SLACK_API_TIMEOUT_MS,
674
903
  ): Promise<Response> {
675
904
  const controller =
676
905
  typeof AbortController !== "undefined" ? new AbortController() : undefined;
677
906
  const timer = controller
678
- ? setTimeout(() => controller.abort(), SLACK_API_TIMEOUT_MS)
907
+ ? setTimeout(() => controller.abort(), timeoutMs)
679
908
  : undefined;
680
909
  try {
681
910
  return await fetch(url, {
@@ -686,3 +915,608 @@ async function slackApiFetch(
686
915
  if (timer) clearTimeout(timer);
687
916
  }
688
917
  }
918
+
919
+ async function resolveSlackThreadPermalink(
920
+ channelId: unknown,
921
+ threadTs: unknown,
922
+ resolvedToken?: string,
923
+ ): Promise<string | undefined> {
924
+ if (typeof channelId !== "string" || typeof threadTs !== "string") {
925
+ return undefined;
926
+ }
927
+ const token = resolvedToken ?? (await resolveSecret("SLACK_BOT_TOKEN"));
928
+ if (!token) return undefined;
929
+
930
+ try {
931
+ const url = new URL("https://slack.com/api/chat.getPermalink");
932
+ url.searchParams.set("channel", channelId);
933
+ url.searchParams.set("message_ts", threadTs);
934
+ const res = await slackApiFetch(
935
+ url.toString(),
936
+ { headers: { Authorization: `Bearer ${token}` } },
937
+ SLACK_PERMALINK_TIMEOUT_MS,
938
+ );
939
+ const data = (await res.json()) as {
940
+ ok?: boolean;
941
+ permalink?: unknown;
942
+ };
943
+ if (!data.ok || typeof data.permalink !== "string") return undefined;
944
+
945
+ const permalink = new URL(data.permalink);
946
+ const isSlackHost =
947
+ permalink.hostname === "slack.com" ||
948
+ permalink.hostname.endsWith(".slack.com");
949
+ if (permalink.protocol !== "https:" || !isSlackHost) return undefined;
950
+ return permalink.toString();
951
+ } catch {
952
+ return undefined;
953
+ }
954
+ }
955
+
956
+ function boundedSlackText(value: unknown): string {
957
+ return typeof value === "string"
958
+ ? value.slice(0, SLACK_CONTEXT_TEXT_LIMIT)
959
+ : "";
960
+ }
961
+
962
+ function normalizeSlackAgentContext(
963
+ value: unknown,
964
+ eventTeamId: string,
965
+ ): {
966
+ entities: Array<{ type: string; value: string; teamId?: string }>;
967
+ channelId?: string;
968
+ } | null {
969
+ if (!value || typeof value !== "object") return null;
970
+ const rawEntities = (value as { entities?: unknown }).entities;
971
+ if (!Array.isArray(rawEntities)) return null;
972
+ const entities = rawEntities.slice(0, 10).flatMap((entity) => {
973
+ if (!entity || typeof entity !== "object") return [];
974
+ const record = entity as Record<string, unknown>;
975
+ if (typeof record.type !== "string" || typeof record.value !== "string") {
976
+ return [];
977
+ }
978
+ const teamId =
979
+ typeof record.team_id === "string" ? record.team_id : undefined;
980
+ if (teamId && eventTeamId !== "unknown" && teamId !== eventTeamId) {
981
+ return [];
982
+ }
983
+ return [
984
+ {
985
+ type: record.type.slice(0, 100),
986
+ value: record.value.slice(0, 200),
987
+ ...(teamId ? { teamId } : {}),
988
+ },
989
+ ];
990
+ });
991
+ if (!entities.length) return null;
992
+ const channelId = entities.find(
993
+ (entity) =>
994
+ entity.type === "slack#/types/channel_id" &&
995
+ /^[CDG][A-Z0-9]+$/.test(entity.value),
996
+ )?.value;
997
+ return { entities, ...(channelId ? { channelId } : {}) };
998
+ }
999
+
1000
+ function slackFileReference(value: unknown): IntegrationFileReference | null {
1001
+ if (!value || typeof value !== "object") return null;
1002
+ const file = value as Record<string, unknown>;
1003
+ if (typeof file.id !== "string") return null;
1004
+ return {
1005
+ id: file.id,
1006
+ ...(typeof file.name === "string" ? { name: file.name } : {}),
1007
+ ...(typeof file.mimetype === "string" ? { mimetype: file.mimetype } : {}),
1008
+ ...(typeof file.size === "number" ? { size: file.size } : {}),
1009
+ ...(typeof file.permalink === "string"
1010
+ ? { permalink: file.permalink }
1011
+ : {}),
1012
+ ...(typeof file.url_private_download === "string"
1013
+ ? { downloadUrl: file.url_private_download }
1014
+ : {}),
1015
+ };
1016
+ }
1017
+
1018
+ async function slackJson(
1019
+ token: string,
1020
+ method: string,
1021
+ params: Record<string, string>,
1022
+ timeoutMs = 2_000,
1023
+ ): Promise<Record<string, any> | null> {
1024
+ try {
1025
+ const url = new URL(`https://slack.com/api/${method}`);
1026
+ for (const [key, value] of Object.entries(params)) {
1027
+ url.searchParams.set(key, value);
1028
+ }
1029
+ const response = await slackApiFetch(
1030
+ url.toString(),
1031
+ { headers: { Authorization: `Bearer ${token}` } },
1032
+ timeoutMs,
1033
+ );
1034
+ const body = (await response.json()) as Record<string, any>;
1035
+ return body.ok ? body : null;
1036
+ } catch {
1037
+ return null;
1038
+ }
1039
+ }
1040
+
1041
+ async function hydrateSlackContext(
1042
+ token: string,
1043
+ incoming: IncomingMessage,
1044
+ ): Promise<IncomingMessage> {
1045
+ const channelId = incoming.platformContext.channelId;
1046
+ const threadTs = incoming.platformContext.threadTs;
1047
+ const senderId = incoming.senderId;
1048
+ if (typeof channelId !== "string" || typeof threadTs !== "string") {
1049
+ return incoming;
1050
+ }
1051
+
1052
+ const [thread, history, pins, conversation, user] = await Promise.all([
1053
+ slackJson(token, "conversations.replies", {
1054
+ channel: channelId,
1055
+ ts: threadTs,
1056
+ limit: String(SLACK_CONTEXT_MESSAGE_LIMIT),
1057
+ }),
1058
+ slackJson(token, "conversations.history", {
1059
+ channel: channelId,
1060
+ latest: String(incoming.platformContext.messageTs ?? threadTs),
1061
+ inclusive: "true",
1062
+ limit: String(SLACK_CONTEXT_MESSAGE_LIMIT),
1063
+ }),
1064
+ slackJson(token, "pins.list", { channel: channelId }),
1065
+ slackJson(token, "conversations.info", { channel: channelId }),
1066
+ senderId ? slackJson(token, "users.info", { user: senderId }) : null,
1067
+ ]);
1068
+
1069
+ // Agent View can attach the Slack surface the user is currently viewing to
1070
+ // a DM. Treat it only as a context hint: Slack requires us to prove the bot
1071
+ // can access the referenced channel with conversations.info before reading
1072
+ // any history from it.
1073
+ const activeContextChannelId =
1074
+ typeof incoming.platformContext.activeContextChannelId === "string"
1075
+ ? incoming.platformContext.activeContextChannelId
1076
+ : null;
1077
+ const activeContextConversation =
1078
+ activeContextChannelId && activeContextChannelId !== channelId
1079
+ ? await slackJson(token, "conversations.info", {
1080
+ channel: activeContextChannelId,
1081
+ })
1082
+ : null;
1083
+ const activeContextHistory = activeContextConversation
1084
+ ? await slackJson(token, "conversations.history", {
1085
+ channel: activeContextChannelId!,
1086
+ limit: "5",
1087
+ })
1088
+ : null;
1089
+ const activeContextName =
1090
+ typeof activeContextConversation?.channel?.name === "string"
1091
+ ? activeContextConversation.channel.name
1092
+ : null;
1093
+ const activeContextMessages = Array.isArray(activeContextHistory?.messages)
1094
+ ? activeContextHistory.messages.map((message: Record<string, any>) => ({
1095
+ ...message,
1096
+ text: `[Active Slack context${activeContextName ? ` #${activeContextName}` : ""}] ${boundedSlackText(message.text)}`,
1097
+ }))
1098
+ : [];
1099
+
1100
+ const rawMessages = [
1101
+ ...(Array.isArray(history?.messages) ? history.messages : []),
1102
+ ...(Array.isArray(pins?.items)
1103
+ ? pins.items
1104
+ .map((item: Record<string, any>) => item.message)
1105
+ .filter(Boolean)
1106
+ : []),
1107
+ ...(Array.isArray(thread?.messages) ? thread.messages : []),
1108
+ ...activeContextMessages,
1109
+ ];
1110
+ const uniqueMessages = [
1111
+ ...new Map(
1112
+ rawMessages.map((message: Record<string, any>, index) => [
1113
+ typeof message.ts === "string" ? message.ts : `unknown:${index}`,
1114
+ message,
1115
+ ]),
1116
+ ).values(),
1117
+ ].slice(-SLACK_CONTEXT_MESSAGE_LIMIT);
1118
+
1119
+ const messages: IntegrationContextMessage[] = uniqueMessages.length
1120
+ ? uniqueMessages.map((message: Record<string, any>) => {
1121
+ const files = Array.isArray(message.files)
1122
+ ? message.files
1123
+ .map(slackFileReference)
1124
+ .filter(
1125
+ (
1126
+ file: IntegrationFileReference | null,
1127
+ ): file is IntegrationFileReference => file !== null,
1128
+ )
1129
+ : undefined;
1130
+ const reactions = Array.isArray(message.reactions)
1131
+ ? message.reactions
1132
+ .filter(
1133
+ (reaction: any) =>
1134
+ typeof reaction?.name === "string" &&
1135
+ typeof reaction?.count === "number",
1136
+ )
1137
+ .map((reaction: any) => ({
1138
+ name: reaction.name,
1139
+ count: reaction.count,
1140
+ }))
1141
+ : undefined;
1142
+ return {
1143
+ ...(typeof message.user === "string"
1144
+ ? { senderId: message.user }
1145
+ : {}),
1146
+ text: boundedSlackText(message.text),
1147
+ timestamp:
1148
+ typeof message.ts === "string"
1149
+ ? Math.floor(parseFloat(message.ts) * 1000)
1150
+ : incoming.timestamp,
1151
+ ...(reactions?.length ? { reactions } : {}),
1152
+ ...(files?.length ? { files } : {}),
1153
+ };
1154
+ })
1155
+ : [];
1156
+
1157
+ const profile = user?.user?.profile;
1158
+ const isGuest = user?.user?.is_restricted === true;
1159
+ const isExternal = user?.user?.is_ultra_restricted === true;
1160
+ const isPrivate = conversation?.channel?.is_private === true;
1161
+ const isExternalShared = conversation?.channel?.is_ext_shared === true;
1162
+ const isMpim = conversation?.channel?.is_mpim === true;
1163
+ const isIm = conversation?.channel?.is_im === true;
1164
+ const conversationType = isIm
1165
+ ? "dm"
1166
+ : isMpim
1167
+ ? "group_dm"
1168
+ : isPrivate
1169
+ ? "private_channel"
1170
+ : "channel";
1171
+
1172
+ const attachedFiles = messages.flatMap((message) => message.files ?? []);
1173
+ const detailedFiles = await Promise.all(
1174
+ [...new Set(attachedFiles.map((file) => file.id))]
1175
+ .slice(0, 5)
1176
+ .map(async (fileId) => {
1177
+ const result = await slackJson(token, "files.info", { file: fileId });
1178
+ return slackFileReference(result?.file);
1179
+ }),
1180
+ );
1181
+ const fileById = new Map(
1182
+ detailedFiles
1183
+ .filter((file): file is IntegrationFileReference => file !== null)
1184
+ .map((file) => [file.id, file]),
1185
+ );
1186
+ const hydratedMessages = messages.map((message) => ({
1187
+ ...message,
1188
+ ...(message.files?.length
1189
+ ? {
1190
+ files: message.files.map((file) => ({
1191
+ ...file,
1192
+ ...(fileById.get(file.id) ?? {}),
1193
+ })),
1194
+ }
1195
+ : {}),
1196
+ }));
1197
+ const hydratedFiles = hydratedMessages.flatMap(
1198
+ (message) => message.files ?? [],
1199
+ );
1200
+ return {
1201
+ ...incoming,
1202
+ ...(typeof profile?.display_name === "string" && profile.display_name
1203
+ ? { senderName: profile.display_name }
1204
+ : typeof profile?.real_name === "string" && profile.real_name
1205
+ ? { senderName: profile.real_name }
1206
+ : {}),
1207
+ ...(typeof profile?.email === "string"
1208
+ ? { senderEmail: profile.email, senderVerified: true }
1209
+ : {}),
1210
+ conversationType,
1211
+ actorTrust: {
1212
+ memberType: isExternal ? "external" : isGuest ? "guest" : "member",
1213
+ verified: true,
1214
+ },
1215
+ contextMessages: hydratedMessages,
1216
+ ...(hydratedFiles.length ? { files: hydratedFiles.slice(0, 20) } : {}),
1217
+ platformContext: {
1218
+ ...incoming.platformContext,
1219
+ channelName:
1220
+ typeof conversation?.channel?.name === "string"
1221
+ ? conversation.channel.name
1222
+ : undefined,
1223
+ isExternalShared,
1224
+ ...(activeContextChannelId
1225
+ ? {
1226
+ activeContextChannelId,
1227
+ ...(activeContextName
1228
+ ? { activeContextChannelName: activeContextName }
1229
+ : {}),
1230
+ }
1231
+ : {}),
1232
+ },
1233
+ };
1234
+ }
1235
+
1236
+ function shortTaskTitle(value: unknown): string {
1237
+ const text = typeof value === "string" ? value : "Working";
1238
+ return text.replace(/[-_]/g, " ").slice(0, 200);
1239
+ }
1240
+
1241
+ async function postSlackJson(
1242
+ token: string,
1243
+ method: string,
1244
+ body: Record<string, unknown>,
1245
+ ): Promise<Record<string, any>> {
1246
+ const response = await slackApiFetch(`https://slack.com/api/${method}`, {
1247
+ method: "POST",
1248
+ headers: {
1249
+ Authorization: `Bearer ${token}`,
1250
+ "Content-Type": "application/json",
1251
+ },
1252
+ body: JSON.stringify(body),
1253
+ });
1254
+ const data = (await response.json()) as Record<string, any>;
1255
+ if (!data.ok) throw new Error(data.error || `${method} failed`);
1256
+ return data;
1257
+ }
1258
+
1259
+ async function startSlackRunProgress(
1260
+ token: string,
1261
+ incoming: IncomingMessage,
1262
+ ): Promise<PlatformRunProgress | null> {
1263
+ const channel = incoming.platformContext.channelId;
1264
+ const threadTs = incoming.platformContext.threadTs;
1265
+ if (typeof channel !== "string" || typeof threadTs !== "string") return null;
1266
+
1267
+ let started: Record<string, any>;
1268
+ try {
1269
+ started = await postSlackJson(token, "chat.startStream", {
1270
+ channel,
1271
+ thread_ts: threadTs,
1272
+ ...(incoming.tenantId ? { recipient_team_id: incoming.tenantId } : {}),
1273
+ ...(incoming.senderId ? { recipient_user_id: incoming.senderId } : {}),
1274
+ task_display_mode: "plan",
1275
+ markdown_text: "Working on your request…",
1276
+ chunks: [
1277
+ {
1278
+ type: "plan_update",
1279
+ title: "Working on your request",
1280
+ },
1281
+ {
1282
+ type: "task_update",
1283
+ id: "agent-native:context",
1284
+ title: "Understand the request",
1285
+ status: "in_progress",
1286
+ details: "Loading relevant context",
1287
+ },
1288
+ ],
1289
+ });
1290
+ } catch {
1291
+ return null;
1292
+ }
1293
+
1294
+ const streamTs = started.ts;
1295
+ if (typeof streamTs !== "string") return null;
1296
+ const tasks = new Map<string, { title: string; status: string }>();
1297
+ const toolTaskIds = new Map<string, string>();
1298
+ tasks.set("agent-native:context", {
1299
+ title: "Understand the request",
1300
+ status: "in_progress",
1301
+ });
1302
+ let sequence = 0;
1303
+ let lastWriteAt = 0;
1304
+ let pending: Record<string, unknown> | null = null;
1305
+ let pendingTimer: ReturnType<typeof setTimeout> | null = null;
1306
+ let approvalControls: { approve: string; deny: string } | null = null;
1307
+ let cancelControl: string | null = null;
1308
+
1309
+ const append = async (chunk: Record<string, unknown>) => {
1310
+ const now = Date.now();
1311
+ const write = async (value: Record<string, unknown>) => {
1312
+ lastWriteAt = Date.now();
1313
+ await postSlackJson(token, "chat.appendStream", {
1314
+ channel,
1315
+ ts: streamTs,
1316
+ markdown_text: "Progress updated.",
1317
+ chunks: [value],
1318
+ }).catch(() => {});
1319
+ };
1320
+ if (now - lastWriteAt >= 900) {
1321
+ await write(chunk);
1322
+ return;
1323
+ }
1324
+ pending = chunk;
1325
+ if (!pendingTimer) {
1326
+ pendingTimer = setTimeout(
1327
+ () => {
1328
+ const value = pending;
1329
+ pending = null;
1330
+ pendingTimer = null;
1331
+ if (value) void write(value);
1332
+ },
1333
+ 900 - (now - lastWriteAt),
1334
+ );
1335
+ }
1336
+ };
1337
+
1338
+ const taskId = (prefix: string, explicit?: string) =>
1339
+ `${prefix}:${explicit || ++sequence}`.slice(0, 240);
1340
+
1341
+ return {
1342
+ async onEvent(event) {
1343
+ if (!cancelControl) {
1344
+ const context = getIntegrationRequestContext();
1345
+ const ownerEmail = getRequestContext()?.userEmail;
1346
+ if (
1347
+ context?.lineage?.runId &&
1348
+ ownerEmail &&
1349
+ incoming.senderId &&
1350
+ incoming.tenantId
1351
+ ) {
1352
+ cancelControl = await createIntegrationControl({
1353
+ action: "cancel",
1354
+ ownerEmail,
1355
+ orgId: getRequestContext()?.orgId ?? null,
1356
+ requesterId: incoming.senderId,
1357
+ teamId: incoming.tenantId,
1358
+ apiAppId:
1359
+ typeof incoming.platformContext.apiAppId === "string"
1360
+ ? incoming.platformContext.apiAppId
1361
+ : null,
1362
+ channelId: channel,
1363
+ messageTs: streamTs,
1364
+ runId: context.lineage.runId,
1365
+ incoming,
1366
+ });
1367
+ await append({
1368
+ type: "blocks",
1369
+ blocks: [
1370
+ {
1371
+ type: "actions",
1372
+ block_id: `agent-native-running-${Date.now()}`,
1373
+ elements: [
1374
+ {
1375
+ type: "button",
1376
+ text: { type: "plain_text", text: "Cancel" },
1377
+ style: "danger",
1378
+ action_id: "agent_native_cancel",
1379
+ value: cancelControl,
1380
+ },
1381
+ ],
1382
+ },
1383
+ ],
1384
+ });
1385
+ }
1386
+ }
1387
+ if (event.type === "approval_required") {
1388
+ const context = getIntegrationRequestContext();
1389
+ const ownerEmail = getRequestContext()?.userEmail;
1390
+ if (context && ownerEmail && incoming.senderId && incoming.tenantId) {
1391
+ const common = {
1392
+ ownerEmail,
1393
+ orgId: getRequestContext()?.orgId ?? null,
1394
+ requesterId: incoming.senderId,
1395
+ teamId: incoming.tenantId,
1396
+ apiAppId:
1397
+ typeof incoming.platformContext.apiAppId === "string"
1398
+ ? incoming.platformContext.apiAppId
1399
+ : null,
1400
+ channelId: channel,
1401
+ messageTs: streamTs,
1402
+ runId: context.lineage?.runId ?? null,
1403
+ approvalKey: event.approvalKey,
1404
+ incoming,
1405
+ };
1406
+ const [approve, deny] = await Promise.all([
1407
+ createIntegrationControl({ ...common, action: "approve" }),
1408
+ createIntegrationControl({ ...common, action: "deny" }),
1409
+ ]);
1410
+ approvalControls = { approve, deny };
1411
+ }
1412
+ await append({
1413
+ type: "task_update",
1414
+ id: "agent-native:approval",
1415
+ title: `Approve ${shortTaskTitle(event.tool)}`,
1416
+ status: "pending",
1417
+ details: "Waiting for the requester",
1418
+ });
1419
+ } else if (event.type === "tool_start") {
1420
+ const key = event.id || event.tool;
1421
+ const id = taskId("tool", event.id);
1422
+ toolTaskIds.set(key, id);
1423
+ const title = shortTaskTitle(event.tool);
1424
+ tasks.set(id, { title, status: "in_progress" });
1425
+ await append({
1426
+ type: "task_update",
1427
+ id,
1428
+ title,
1429
+ status: "in_progress",
1430
+ });
1431
+ } else if (event.type === "tool_done") {
1432
+ const key = event.id || event.tool;
1433
+ const id = toolTaskIds.get(key) ?? taskId("tool", event.id);
1434
+ const title = tasks.get(id)?.title ?? shortTaskTitle(event.tool);
1435
+ tasks.set(id, {
1436
+ title,
1437
+ status: event.isError ? "error" : "complete",
1438
+ });
1439
+ await append({
1440
+ type: "task_update",
1441
+ id,
1442
+ title,
1443
+ status: event.isError ? "error" : "complete",
1444
+ });
1445
+ } else if (event.type === "agent_call") {
1446
+ const id = taskId("agent", event.agent);
1447
+ const status =
1448
+ event.status === "start"
1449
+ ? "in_progress"
1450
+ : event.status === "done"
1451
+ ? "complete"
1452
+ : "error";
1453
+ const title = `Ask ${shortTaskTitle(event.agent)}`;
1454
+ tasks.set(id, { title, status });
1455
+ await append({ type: "task_update", id, title, status });
1456
+ } else if (event.type === "activity") {
1457
+ await append({
1458
+ type: "task_update",
1459
+ id: "agent-native:context",
1460
+ title: "Understand the request",
1461
+ status: "in_progress",
1462
+ details: shortTaskTitle(event.label),
1463
+ });
1464
+ }
1465
+ },
1466
+ async complete(message) {
1467
+ if (pendingTimer) clearTimeout(pendingTimer);
1468
+ const finalChunks = [...tasks.entries()].map(([id, task]) => ({
1469
+ type: "task_update",
1470
+ id,
1471
+ title: task.title,
1472
+ status: task.status === "in_progress" ? "complete" : task.status,
1473
+ }));
1474
+ const messageBlocks = Array.isArray(message.platformContext.blocks)
1475
+ ? message.platformContext.blocks
1476
+ : [];
1477
+ const controlBlocks = approvalControls
1478
+ ? [
1479
+ {
1480
+ type: "actions",
1481
+ block_id: `agent-native-approval-${Date.now()}`,
1482
+ elements: [
1483
+ {
1484
+ type: "button",
1485
+ text: { type: "plain_text", text: "Approve" },
1486
+ style: "primary",
1487
+ action_id: "agent_native_approve",
1488
+ value: approvalControls.approve,
1489
+ },
1490
+ {
1491
+ type: "button",
1492
+ text: { type: "plain_text", text: "Deny" },
1493
+ style: "danger",
1494
+ action_id: "agent_native_deny",
1495
+ value: approvalControls.deny,
1496
+ },
1497
+ ],
1498
+ },
1499
+ ]
1500
+ : [];
1501
+ await postSlackJson(token, "chat.stopStream", {
1502
+ channel,
1503
+ ts: streamTs,
1504
+ markdown_text: message.text || "Done.",
1505
+ ...(finalChunks.length ? { chunks: finalChunks } : {}),
1506
+ ...(messageBlocks.length || controlBlocks.length
1507
+ ? { blocks: [...messageBlocks, ...controlBlocks].slice(0, 50) }
1508
+ : {}),
1509
+ });
1510
+ setSlackAssistantStatus(token, channel, threadTs, "");
1511
+ },
1512
+ async fail(message) {
1513
+ if (pendingTimer) clearTimeout(pendingTimer);
1514
+ await postSlackJson(token, "chat.stopStream", {
1515
+ channel,
1516
+ ts: streamTs,
1517
+ markdown_text: message.slice(0, SLACK_MAX_LENGTH),
1518
+ }).catch(() => {});
1519
+ setSlackAssistantStatus(token, channel, threadTs, "");
1520
+ },
1521
+ };
1522
+ }