@mastra/factory 0.24.0-alpha.8 → 0.24.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 (301) hide show
  1. package/README.md +4 -4
  2. package/dist/environment/build-runner.js +2 -1
  3. package/dist/environment/build-runner.js.map +1 -1
  4. package/dist/environment/build-workflow.js +1 -1
  5. package/dist/environment/build-workflow.js.map +1 -1
  6. package/dist/environment/build.js +16 -14
  7. package/dist/environment/build.js.map +1 -1
  8. package/dist/factory.d.ts.map +1 -1
  9. package/dist/factory.js +111 -19
  10. package/dist/factory.js.map +1 -1
  11. package/dist/integrations/base.d.ts +1 -1
  12. package/dist/integrations/base.d.ts.map +1 -1
  13. package/dist/integrations/github/app-identity.js.map +1 -1
  14. package/dist/integrations/github/integration.js +1 -1
  15. package/dist/integrations/github/integration.js.map +1 -1
  16. package/dist/integrations/github/reconcile-worker.js +1 -1
  17. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  18. package/dist/integrations/github/routes.d.ts +1 -1
  19. package/dist/integrations/github/routes.d.ts.map +1 -1
  20. package/dist/integrations/github/routes.js +24 -13
  21. package/dist/integrations/github/routes.js.map +1 -1
  22. package/dist/integrations/github/rules.d.ts.map +1 -1
  23. package/dist/integrations/github/rules.js +53 -9
  24. package/dist/integrations/github/rules.js.map +1 -1
  25. package/dist/integrations/github/sandbox.js +2 -2
  26. package/dist/integrations/github/sandbox.js.map +1 -1
  27. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  28. package/dist/integrations/github/session-subscriptions.js +1 -2
  29. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  30. package/dist/integrations/github/subscriptions.d.ts +7 -4
  31. package/dist/integrations/github/subscriptions.d.ts.map +1 -1
  32. package/dist/integrations/github/subscriptions.js +5 -5
  33. package/dist/integrations/github/subscriptions.js.map +1 -1
  34. package/dist/integrations/gitlab/api.js.map +1 -1
  35. package/dist/integrations/gitlab/integration.js.map +1 -1
  36. package/dist/integrations/gitlab/reconciliation-config.js +2 -1
  37. package/dist/integrations/gitlab/reconciliation-config.js.map +1 -1
  38. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  39. package/dist/integrations/gitlab/routes.js +10 -6
  40. package/dist/integrations/gitlab/routes.js.map +1 -1
  41. package/dist/integrations/gitlab/rules.js.map +1 -1
  42. package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
  43. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  44. package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
  45. package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
  46. package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
  47. package/dist/integrations/gitlab/subscriptions.js +5 -5
  48. package/dist/integrations/gitlab/subscriptions.js.map +1 -1
  49. package/dist/integrations/incidentio/api.js.map +1 -1
  50. package/dist/integrations/incidentio/integration.js.map +1 -1
  51. package/dist/integrations/incidentio/rules.js.map +1 -1
  52. package/dist/integrations/issue-reconcile-worker.js +1 -1
  53. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  54. package/dist/integrations/issue-reconciler.js +1 -1
  55. package/dist/integrations/issue-reconciler.js.map +1 -1
  56. package/dist/integrations/jira/api.js.map +1 -1
  57. package/dist/integrations/jira/integration.js.map +1 -1
  58. package/dist/integrations/jira/rules.js.map +1 -1
  59. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  60. package/dist/integrations/linear/agent-tools.js.map +1 -1
  61. package/dist/integrations/linear/integration.js.map +1 -1
  62. package/dist/integrations/linear/rules.js.map +1 -1
  63. package/dist/integrations/platform/api-client.js.map +1 -1
  64. package/dist/integrations/platform/github/event-worker.js +1 -1
  65. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  66. package/dist/integrations/platform/github/integration.js +2 -2
  67. package/dist/integrations/platform/github/integration.js.map +1 -1
  68. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  69. package/dist/integrations/platform/gitlab/integration.js.map +1 -1
  70. package/dist/integrations/platform/incidentio/integration.js +2 -1
  71. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  72. package/dist/integrations/platform/jira/integration.js.map +1 -1
  73. package/dist/integrations/platform/linear/event-worker.js +1 -1
  74. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  75. package/dist/integrations/platform/linear/integration.js.map +1 -1
  76. package/dist/integrations/slack/connect-route.js +1 -1
  77. package/dist/integrations/slack/connect-route.js.map +1 -1
  78. package/dist/integrations/slack/feed-publisher.js +1 -1
  79. package/dist/integrations/slack/feed-publisher.js.map +1 -1
  80. package/dist/integrations/slack/integration.js +1 -1
  81. package/dist/integrations/slack/integration.js.map +1 -1
  82. package/dist/integrations/slack/slack.d.ts +16 -5
  83. package/dist/integrations/slack/slack.d.ts.map +1 -1
  84. package/dist/integrations/slack/slack.js +87 -34
  85. package/dist/integrations/slack/slack.js.map +1 -1
  86. package/dist/integrations/subscription-session.d.ts +0 -1
  87. package/dist/integrations/subscription-session.d.ts.map +1 -1
  88. package/dist/integrations/subscription-session.js +1 -4
  89. package/dist/integrations/subscription-session.js.map +1 -1
  90. package/dist/integrations/workos/integration.js.map +1 -1
  91. package/dist/mcp/routes.d.ts.map +1 -1
  92. package/dist/mcp/routes.js +1 -1
  93. package/dist/mcp/routes.js.map +1 -1
  94. package/dist/packages/_internals/workspace/dist/index.js +18 -1
  95. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  96. package/dist/routes/attention-activity.d.ts.map +1 -1
  97. package/dist/routes/attention-activity.js +5 -2
  98. package/dist/routes/attention-activity.js.map +1 -1
  99. package/dist/routes/attention-parked.js.map +1 -1
  100. package/dist/routes/attention-providers.d.ts +0 -1
  101. package/dist/routes/attention-providers.d.ts.map +1 -1
  102. package/dist/routes/attention-providers.js +5 -6
  103. package/dist/routes/attention-providers.js.map +1 -1
  104. package/dist/routes/automation-runs.js.map +1 -1
  105. package/dist/routes/config.d.ts +6 -7
  106. package/dist/routes/config.d.ts.map +1 -1
  107. package/dist/routes/config.js +20 -19
  108. package/dist/routes/config.js.map +1 -1
  109. package/dist/routes/contracts.js +1 -1
  110. package/dist/routes/contracts.js.map +1 -1
  111. package/dist/routes/custom-provider-source.js.map +1 -1
  112. package/dist/routes/fs.d.ts +31 -23
  113. package/dist/routes/fs.d.ts.map +1 -1
  114. package/dist/routes/fs.js +218 -64
  115. package/dist/routes/fs.js.map +1 -1
  116. package/dist/routes/intake.js +2 -1
  117. package/dist/routes/intake.js.map +1 -1
  118. package/dist/routes/knowledge.js +1 -1
  119. package/dist/routes/knowledge.js.map +1 -1
  120. package/dist/routes/oauth.js +3 -2
  121. package/dist/routes/oauth.js.map +1 -1
  122. package/dist/routes/projects.d.ts +1 -1
  123. package/dist/routes/projects.d.ts.map +1 -1
  124. package/dist/routes/projects.js +33 -31
  125. package/dist/routes/projects.js.map +1 -1
  126. package/dist/routes/provider-credentials.d.ts.map +1 -1
  127. package/dist/routes/provider-credentials.js +6 -1
  128. package/dist/routes/provider-credentials.js.map +1 -1
  129. package/dist/routes/session-access.d.ts +55 -0
  130. package/dist/routes/session-access.d.ts.map +1 -0
  131. package/dist/routes/session-access.js +165 -0
  132. package/dist/routes/session-access.js.map +1 -0
  133. package/dist/routes/skills.d.ts +2 -4
  134. package/dist/routes/skills.d.ts.map +1 -1
  135. package/dist/routes/skills.js +19 -65
  136. package/dist/routes/skills.js.map +1 -1
  137. package/dist/routes/source-control-sessions.d.ts +6 -1
  138. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  139. package/dist/routes/source-control-sessions.js +95 -12
  140. package/dist/routes/source-control-sessions.js.map +1 -1
  141. package/dist/routes/surface.d.ts +5 -0
  142. package/dist/routes/surface.d.ts.map +1 -1
  143. package/dist/routes/surface.js +10 -4
  144. package/dist/routes/surface.js.map +1 -1
  145. package/dist/routes/tenant-credentials.js.map +1 -1
  146. package/dist/routes/work-items.js.map +1 -1
  147. package/dist/rules/binding-context.js +11 -9
  148. package/dist/rules/binding-context.js.map +1 -1
  149. package/dist/rules/dispatcher.d.ts +1 -1
  150. package/dist/rules/dispatcher.d.ts.map +1 -1
  151. package/dist/rules/dispatcher.js +10 -10
  152. package/dist/rules/dispatcher.js.map +1 -1
  153. package/dist/rules/start-coordinator.d.ts +4 -0
  154. package/dist/rules/start-coordinator.d.ts.map +1 -1
  155. package/dist/rules/start-coordinator.js +36 -4
  156. package/dist/rules/start-coordinator.js.map +1 -1
  157. package/dist/rules/tools.js +2 -2
  158. package/dist/rules/tools.js.map +1 -1
  159. package/dist/rules/transition-service.js.map +1 -1
  160. package/dist/sandbox/callback-factory-sandbox.js.map +1 -1
  161. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  162. package/dist/sandbox/session-retirement.js.map +1 -1
  163. package/dist/secret-encryption.d.ts.map +1 -1
  164. package/dist/secret-encryption.js +65 -25
  165. package/dist/secret-encryption.js.map +1 -1
  166. package/dist/session/channel-reply-processor.d.ts +7 -6
  167. package/dist/session/channel-reply-processor.d.ts.map +1 -1
  168. package/dist/session/channel-reply-processor.js +9 -9
  169. package/dist/session/channel-reply-processor.js.map +1 -1
  170. package/dist/session/channel-session-link-processor.js +1 -0
  171. package/dist/session/channel-session-link-processor.js.map +1 -1
  172. package/dist/session/credential-policy.d.ts.map +1 -1
  173. package/dist/session/credential-policy.js +7 -6
  174. package/dist/session/credential-policy.js.map +1 -1
  175. package/dist/session/default-model-hydration.d.ts +1 -0
  176. package/dist/session/default-model-hydration.d.ts.map +1 -1
  177. package/dist/session/default-model-hydration.js +3 -2
  178. package/dist/session/default-model-hydration.js.map +1 -1
  179. package/dist/session/environment-state-processor.js +1 -1
  180. package/dist/session/environment-state-processor.js.map +1 -1
  181. package/dist/session/filesystem-capture.d.ts +3 -3
  182. package/dist/session/filesystem-capture.d.ts.map +1 -1
  183. package/dist/session/filesystem-capture.js +16 -17
  184. package/dist/session/filesystem-capture.js.map +1 -1
  185. package/dist/session/first-exec-capture.d.ts +1 -1
  186. package/dist/session/first-exec-capture.d.ts.map +1 -1
  187. package/dist/session/first-exec-capture.js +1 -1
  188. package/dist/session/first-exec-capture.js.map +1 -1
  189. package/dist/session/first-message-capture.d.ts +1 -1
  190. package/dist/session/first-message-capture.d.ts.map +1 -1
  191. package/dist/session/first-message-capture.js +1 -1
  192. package/dist/session/first-message-capture.js.map +1 -1
  193. package/dist/session/live-sessions.d.ts +1 -1
  194. package/dist/session/live-sessions.js +1 -1
  195. package/dist/session/live-sessions.js.map +1 -1
  196. package/dist/session/memory-settings-hydration.d.ts +1 -1
  197. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  198. package/dist/session/memory-settings-hydration.js +2 -1
  199. package/dist/session/memory-settings-hydration.js.map +1 -1
  200. package/dist/session/resource-migration.d.ts +105 -0
  201. package/dist/session/resource-migration.d.ts.map +1 -0
  202. package/dist/session/resource-migration.js +195 -0
  203. package/dist/session/resource-migration.js.map +1 -0
  204. package/dist/session/session-threads.d.ts +58 -0
  205. package/dist/session/session-threads.d.ts.map +1 -0
  206. package/dist/session/session-threads.js +90 -0
  207. package/dist/session/session-threads.js.map +1 -0
  208. package/dist/session/source-control-tools.js +8 -8
  209. package/dist/session/source-control-tools.js.map +1 -1
  210. package/dist/session/thinking-default.d.ts.map +1 -1
  211. package/dist/session/thinking-default.js +3 -4
  212. package/dist/session/thinking-default.js.map +1 -1
  213. package/dist/session/thread-session-stamp.d.ts +21 -0
  214. package/dist/session/thread-session-stamp.d.ts.map +1 -0
  215. package/dist/session/thread-session-stamp.js +20 -0
  216. package/dist/session/thread-session-stamp.js.map +1 -0
  217. package/dist/session/thread-title-mirror.d.ts +1 -1
  218. package/dist/session/thread-title-mirror.d.ts.map +1 -1
  219. package/dist/session/thread-title-mirror.js +1 -1
  220. package/dist/session/thread-title-mirror.js.map +1 -1
  221. package/dist/skills/service.d.ts +6 -8
  222. package/dist/skills/service.d.ts.map +1 -1
  223. package/dist/skills/service.js +3 -3
  224. package/dist/skills/service.js.map +1 -1
  225. package/dist/spa-static.js.map +1 -1
  226. package/dist/state-signing.js +1 -1
  227. package/dist/state-signing.js.map +1 -1
  228. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  229. package/dist/storage/domains/audit/agent-audit.js +2 -2
  230. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  231. package/dist/storage/domains/audit/base.js.map +1 -1
  232. package/dist/storage/domains/audit/domain.js.map +1 -1
  233. package/dist/storage/domains/channel-identity/base.js.map +1 -1
  234. package/dist/storage/domains/comments/base.js.map +1 -1
  235. package/dist/storage/domains/comments/domain.js.map +1 -1
  236. package/dist/storage/domains/comments/feed-context.js.map +1 -1
  237. package/dist/storage/domains/credentials/base.js.map +1 -1
  238. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  239. package/dist/storage/domains/intake/base.js.map +1 -1
  240. package/dist/storage/domains/integrations/base.d.ts +8 -0
  241. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  242. package/dist/storage/domains/integrations/base.js +7 -0
  243. package/dist/storage/domains/integrations/base.js.map +1 -1
  244. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  245. package/dist/storage/domains/model-defaults/base.js.map +1 -1
  246. package/dist/storage/domains/projects/base.js.map +1 -1
  247. package/dist/storage/domains/queue-health/base.js.map +1 -1
  248. package/dist/storage/domains/source-control/base.d.ts +19 -0
  249. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  250. package/dist/storage/domains/source-control/base.js +42 -0
  251. package/dist/storage/domains/source-control/base.js.map +1 -1
  252. package/dist/storage/domains/source-control/inmemory.d.ts +5 -1
  253. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  254. package/dist/storage/domains/source-control/inmemory.js +7 -0
  255. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  256. package/dist/storage/domains/work-items/base.d.ts +14 -0
  257. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  258. package/dist/storage/domains/work-items/base.js +25 -1
  259. package/dist/storage/domains/work-items/base.js.map +1 -1
  260. package/dist/storage/domains/work-items/board-version.js.map +1 -1
  261. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  262. package/dist/tools/approval-tools.js +1 -1
  263. package/dist/tools/approval-tools.js.map +1 -1
  264. package/dist/tools/deliver.d.ts +8 -5
  265. package/dist/tools/deliver.d.ts.map +1 -1
  266. package/dist/tools/deliver.js +22 -8
  267. package/dist/tools/deliver.js.map +1 -1
  268. package/dist/tools/messaging-tools.d.ts +1 -1
  269. package/dist/tools/messaging-tools.d.ts.map +1 -1
  270. package/dist/tools/messaging-tools.js +203 -72
  271. package/dist/tools/messaging-tools.js.map +1 -1
  272. package/dist/tools/permissions.d.ts +1 -1
  273. package/dist/tools/permissions.d.ts.map +1 -1
  274. package/dist/tools/permissions.js +1 -1
  275. package/dist/tools/permissions.js.map +1 -1
  276. package/dist/tools/reports.d.ts.map +1 -1
  277. package/dist/tools/reports.js +10 -3
  278. package/dist/tools/reports.js.map +1 -1
  279. package/dist/tools/run-watch.d.ts +2 -0
  280. package/dist/tools/run-watch.d.ts.map +1 -1
  281. package/dist/tools/run-watch.js +42 -2
  282. package/dist/tools/run-watch.js.map +1 -1
  283. package/dist/tools/scope.d.ts +2 -1
  284. package/dist/tools/scope.d.ts.map +1 -1
  285. package/dist/tools/scope.js +3 -2
  286. package/dist/tools/scope.js.map +1 -1
  287. package/dist/tools/session-tools.d.ts +1 -1
  288. package/dist/tools/session-tools.d.ts.map +1 -1
  289. package/dist/tools/session-tools.js +33 -16
  290. package/dist/tools/session-tools.js.map +1 -1
  291. package/dist/tools/sessions-state-processor.js +2 -2
  292. package/dist/tools/sessions-state-processor.js.map +1 -1
  293. package/dist/tools/side-sessions.d.ts +1 -1
  294. package/dist/tools/side-sessions.d.ts.map +1 -1
  295. package/dist/tools/side-sessions.js +1 -1
  296. package/dist/tools/side-sessions.js.map +1 -1
  297. package/dist/workspace.d.ts +5 -2
  298. package/dist/workspace.d.ts.map +1 -1
  299. package/dist/workspace.js +28 -15
  300. package/dist/workspace.js.map +1 -1
  301. package/package.json +13 -13
@@ -15,7 +15,7 @@ function describeToolCall(args) {
15
15
  if (typeof description !== "string") return void 0;
16
16
  const line = description.split("\n").map((part) => part.replace(/\s+/g, " ").trim()).find(Boolean);
17
17
  if (!line) return void 0;
18
- const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, MAX_STATUS_LENGTH - 1)}…` : line;
18
+ const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, 79)}…` : line;
19
19
  return `is ${/^[A-Z][a-z]/.test(text) ? text.charAt(0).toLowerCase() + text.slice(1) : text}`;
20
20
  }
21
21
  const factoryTypingStatus = (chunk) => {
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\n/** Slack renders the status inline next to the app name; keep it to one short line. */\nconst MAX_STATUS_LENGTH = 80;\n\n/**\n * Turns a tool's `description` arg (\"Listing the repo root\") into a status\n * line that reads after the app name (\"<App> is listing the repo root…\").\n * Only the first line counts: issue/PR creators carry whole bodies there.\n */\nfunction describeToolCall(args: unknown): string | undefined {\n if (!args || typeof args !== 'object') return undefined;\n const { description } = args as { description?: unknown };\n if (typeof description !== 'string') return undefined;\n const line = description\n .split('\\n')\n .map(part => part.replace(/\\s+/g, ' ').trim())\n .find(Boolean);\n if (!line) return undefined;\n const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, MAX_STATUS_LENGTH - 1)}…` : line;\n // Drop the sentence-case capital (\"Listing\" → \"listing\") but leave acronyms\n // and identifiers (\"PR lookup\", \"CI status\") alone.\n return `is ${/^[A-Z][a-z]/.test(text) ? text.charAt(0).toLowerCase() + text.slice(1) : text}`;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return describeToolCall(chunk.payload.args) ?? 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\n/**\n * Factory defaults under the caller's overrides. Tool calls stay out of the\n * thread: a Factory session runs hundreds of them, and a row per call (or a\n * card per call) buries the reply. Progress shows through the typing status\n * instead, which names the tool's current step; the session link carries the\n * full trace. `grouped` remains available via `adapterOptions` for callers\n * who want Slack's \"Thinking Steps\" widget.\n */\nfunction factoryAdapterOptions(overrides: SlackAdapterChannelConfig): SlackAdapterChannelConfig {\n return { typingStatus: factoryTypingStatus, toolDisplay: 'hidden', ...overrides };\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: factoryAdapterOptions(adapterOverrides(this.#config.adapterOptions)),\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;;AA0EA,MAAM,oBAAoB;;;;;;AAO1B,SAAS,iBAAiB,MAAmC;CAC3D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO,KAAA;CAC9C,MAAM,EAAE,gBAAgB;CACxB,IAAI,OAAO,gBAAgB,UAAU,OAAO,KAAA;CAC5C,MAAM,OAAO,YACV,MAAM,IAAI,CAAC,CACX,KAAI,SAAQ,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAC7C,KAAK,OAAO;CACf,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,MAAM,OAAO,KAAK,SAAS,oBAAoB,GAAG,KAAK,MAAM,GAAG,oBAAoB,CAAC,EAAE,KAAK;CAG5F,OAAO,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,IAAI;AACzF;AAEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO,iBAAiB,MAAM,QAAQ,IAAI,KAAK;EACjD,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;;;;;;;;;AAUA,SAAS,sBAAsB,WAAiE;CAC9F,OAAO;EAAE,cAAc;EAAqB,aAAa;EAAU,GAAG;CAAU;AAClF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAKC,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB,sBAAsB,iBAAiB,KAAKA,QAAQ,cAAc,CAAC;EACrF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
1
+ {"version":3,"file":"integration.js","names":[],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { TypingStatusFn } from '@mastra/core/channels';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n /**\n * Overrides for the Slack channel adapter entry.\n *\n * @example\n * ```ts\n * new SlackIntegration({\n * signingSecret,\n * adapterOptions: {\n * streaming: true,\n * toolDisplay: 'grouped',\n * },\n * });\n * ```\n */\n adapterOptions?: SlackAdapterChannelConfig;\n}\n\n/** Slack renders the status inline next to the app name; keep it to one short line. */\nconst MAX_STATUS_LENGTH = 80;\n\n/**\n * Turns a tool's `description` arg (\"Listing the repo root\") into a status\n * line that reads after the app name (\"<App> is listing the repo root…\").\n * Only the first line counts: issue/PR creators carry whole bodies there.\n */\nfunction describeToolCall(args: unknown): string | undefined {\n if (!args || typeof args !== 'object') return undefined;\n const { description } = args as { description?: unknown };\n if (typeof description !== 'string') return undefined;\n const line = description\n .split('\\n')\n .map(part => part.replace(/\\s+/g, ' ').trim())\n .find(Boolean);\n if (!line) return undefined;\n const text = line.length > MAX_STATUS_LENGTH ? `${line.slice(0, MAX_STATUS_LENGTH - 1)}…` : line;\n // Drop the sentence-case capital (\"Listing\" → \"listing\") but leave acronyms\n // and identifiers (\"PR lookup\", \"CI status\") alone.\n return `is ${/^[A-Z][a-z]/.test(text) ? text.charAt(0).toLowerCase() + text.slice(1) : text}`;\n}\n\nconst factoryTypingStatus: TypingStatusFn = chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return 'is typing...';\n case 'tool-call':\n return describeToolCall(chunk.payload.args) ?? 'is working...';\n default:\n return undefined;\n }\n};\n\n/**\n * Drops keys the caller set to `undefined` so spreading the overrides cannot\n * erase the Factory defaults they sit on top of.\n */\nfunction adapterOverrides(options: SlackAdapterChannelConfig | undefined): SlackAdapterChannelConfig {\n return Object.fromEntries(\n Object.entries(options ?? {}).filter(([, value]) => value !== undefined),\n ) as SlackAdapterChannelConfig;\n}\n\n/**\n * Factory defaults under the caller's overrides. Tool calls stay out of the\n * thread: a Factory session runs hundreds of them, and a row per call (or a\n * card per call) buries the reply. Progress shows through the typing status\n * instead, which names the tool's current step; the session link carries the\n * full trace. `grouped` remains available via `adapterOptions` for callers\n * who want Slack's \"Thinking Steps\" widget.\n */\nfunction factoryAdapterOptions(overrides: SlackAdapterChannelConfig): SlackAdapterChannelConfig {\n return { typingStatus: factoryTypingStatus, toolDisplay: 'hidden', ...overrides };\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions select the source-control provider from the linked\n // repository on each Factory project.\n const sourceControls =\n ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);\n this.#repoBackedSessions = sourceControls.length > 0;\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControls,\n modelDefaults: ctx.storage.modelDefaults,\n workItems: ctx.runtime?.workItems,\n feed: ctx.feed,\n adapterOptions: factoryAdapterOptions(adapterOverrides(this.#config.adapterOptions)),\n });\n }\n\n feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher {\n return new SlackFeedPublisher({ controller: ctx.controller });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;;;AA0EA,MAAM,oBAAoB;;;;;;AAO1B,SAAS,iBAAiB,MAAmC;CAC3D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO,KAAA;CAC9C,MAAM,EAAE,gBAAgB;CACxB,IAAI,OAAO,gBAAgB,UAAU,OAAO,KAAA;CAC5C,MAAM,OAAO,YACV,MAAM,IAAI,CAAC,CACX,KAAI,SAAQ,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAC7C,KAAK,OAAO;CACf,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,MAAM,OAAO,KAAK,SAAS,oBAAoB,GAAG,KAAK,MAAM,GAAG,EAAqB,EAAE,KAAK;CAG5F,OAAO,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,IAAI;AACzF;AAEA,MAAM,uBAAsC,UAAS;CACnD,QAAQ,MAAM,MAAd;EACE,KAAK,mBACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,KAAK,aACH,OAAO,iBAAiB,MAAM,QAAQ,IAAI,KAAK;EACjD,SACE;CACJ;AACF;;;;;AAMA,SAAS,iBAAiB,SAA2E;CACnG,OAAO,OAAO,YACZ,OAAO,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACzE;AACF;;;;;;;;;AAUA,SAAS,sBAAsB,WAAiE;CAC9F,OAAO;EAAE,cAAc;EAAqB,aAAa;EAAU,GAAG;CAAU;AAClF;AAEA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAK,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,iBACJ,IAAI,QAAQ,mBAAmB,IAAI,QAAQ,qBAAqB,CAAC,IAAI,QAAQ,kBAAkB,IAAI,CAAC;EACtG,KAAK,sBAAsB,eAAe,SAAS;EACnD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAK,QAAQ;IACvB,cAAc,KAAK,QAAQ;IAC3B,eAAe,KAAK,QAAQ;IAC5B,UAAU,KAAK,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB;GACA,eAAe,IAAI,QAAQ;GAC3B,WAAW,IAAI,SAAS;GACxB,MAAM,IAAI;GACV,gBAAgB,sBAAsB,iBAAiB,KAAK,QAAQ,cAAc,CAAC;EACrF,CAAC;CACH;CAEA,cAAc,KAAgD;EAC5D,OAAO,IAAI,mBAAmB,EAAE,YAAY,IAAI,WAAW,CAAC;CAC9D;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAK;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAK;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAK;EAC3B;CACF;AACF"}
@@ -121,9 +121,20 @@ export declare function resolveFactoryForLink({ thread, link, key, accountLinks,
121
121
  accountLinks: ChannelIdentityStorage;
122
122
  projects?: FactoryProjectsStorage;
123
123
  }): Promise<FactoryRouteResult>;
124
+ /**
125
+ * Session ids of repo-backed Slack threads, keyed by the message that started
126
+ * them. The resourceId resolver records the session it found or created; the
127
+ * thread-id resolver, which core runs next for the same message, names the new
128
+ * thread after it.
129
+ */
130
+ export type SlackStartedSessions = WeakMap<object, string>;
124
131
  /**
125
132
  * Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
126
- * factory has a repository gets a Factory user-session id — the controller
133
+ * factory has a repository gets a Factory session. A channel thread belongs to
134
+ * the Factory: its resourceId is the Factory id, so the thread shares the
135
+ * Factory's memory. A DM is private to the sender, so it runs under the
136
+ * sender's user id and shares memory with their own sessions. The session id is recorded in `startedSessions` for the
137
+ * thread-id resolver. The controller
127
138
  * session then materializes the repo sandbox via the factory's dynamic
128
139
  * workspace (clone + PAT), the session shows up in the web Sessions list, and
129
140
  * View Session deep-links land on the normal workspace route.
@@ -147,17 +158,17 @@ export declare function resolveFactoryForLink({ thread, link, key, accountLinks,
147
158
  * Pure lookups only — cards for unlinked/unrouted senders are the dispatch
148
159
  * gate's job; this hook must never post.
149
160
  */
150
- export declare function createChannelResourceIdResolver(deps: SlackChannelDeps): ResolveResourceId;
161
+ export declare function createChannelResourceIdResolver(deps: SlackChannelDeps, startedSessions?: SlackStartedSessions): ResolveResourceId;
151
162
  /**
152
163
  * Thread id for a NEW Slack channel thread. Repo-backed threads take the
153
- * user-session id AS their thread id, matching the web convention
164
+ * session id AS their thread id, matching the web convention
154
165
  * (FactoryStartCoordinator seeds threads with threadId = sessionId) so
155
166
  * `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
156
- * sessions exactly like web-created ones — no `?resourceId=` override needed.
167
+ * sessions exactly like web-created ones — no `?sessionId=` override needed.
157
168
  * Chat-only threads keep the default random id: their `channel:...`
158
169
  * resourceId is a memory key, not a unique thread id.
159
170
  */
160
- export declare const resolveChannelThreadId: ResolveThreadId;
171
+ export declare function createChannelThreadIdResolver(startedSessions: SlackStartedSessions): ResolveThreadId;
161
172
  /** Create channel sessions with their Factory ownership present in initial controller state. */
162
173
  export declare function createChannelSessionResolver(deps: SlackChannelDeps): ChannelSessionResolve;
163
174
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { actorFromChannelAuthor } from "../../storage/domains/comments/actor.js";
2
2
  import { FACTORY_INTERACTIVE_KEY } from "../../auth.js";
3
+ import { isRunBound, memoryResourceOf, sessionStampCounts } from "../../session/session-threads.js";
3
4
  import { primeTenantCredentialsForRequestContext, primeTenantCredentialsForTenant } from "../../routes/tenant-credentials.js";
4
5
  import { readRequestContextOrgId, seedSessionOrg } from "../../session/org-seed.js";
5
6
  import { prepareSessionRunContext } from "../subscription-session.js";
@@ -152,7 +153,11 @@ function threadBranch(threadId) {
152
153
  }
153
154
  /**
154
155
  * Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
155
- * factory has a repository gets a Factory user-session id — the controller
156
+ * factory has a repository gets a Factory session. A channel thread belongs to
157
+ * the Factory: its resourceId is the Factory id, so the thread shares the
158
+ * Factory's memory. A DM is private to the sender, so it runs under the
159
+ * sender's user id and shares memory with their own sessions. The session id is recorded in `startedSessions` for the
160
+ * thread-id resolver. The controller
156
161
  * session then materializes the repo sandbox via the factory's dynamic
157
162
  * workspace (clone + PAT), the session shows up in the web Sessions list, and
158
163
  * View Session deep-links land on the normal workspace route.
@@ -176,7 +181,7 @@ function threadBranch(threadId) {
176
181
  * Pure lookups only — cards for unlinked/unrouted senders are the dispatch
177
182
  * gate's job; this hook must never post.
178
183
  */
179
- function createChannelResourceIdResolver(deps) {
184
+ function createChannelResourceIdResolver(deps, startedSessions = /* @__PURE__ */ new WeakMap()) {
180
185
  const { accountLinks, projects } = deps;
181
186
  const sourceControls = configuredSourceControls(deps);
182
187
  return async ({ platform, thread, message }) => {
@@ -219,13 +224,17 @@ function createChannelResourceIdResolver(deps) {
219
224
  });
220
225
  if (!repo.found) throw new SlackSessionStartError(repo.reason === "connection" ? "Could not start a session: connect source control to this Factory project." : "Could not start a session: link a repository to this Factory project.");
221
226
  const branch = threadBranch(thread.id);
227
+ const memoryResourceId = thread.isDM ? link.userId : factoryProjectId;
222
228
  const existing = await sourceControl.sessions.getForBranch({
223
229
  factoryProjectId,
224
230
  userId: link.userId,
225
231
  branch
226
232
  });
227
- if (existing) return existing.sessionId;
228
- return (await sourceControl.sessions.create({
233
+ if (existing) {
234
+ startedSessions.set(message, existing.sessionId);
235
+ return memoryResourceId;
236
+ }
237
+ const session = await sourceControl.sessions.create({
229
238
  sessionId: globalThis.crypto.randomUUID(),
230
239
  projectRepositoryId: repo.projectRepositoryId,
231
240
  factoryProjectId,
@@ -234,7 +243,9 @@ function createChannelResourceIdResolver(deps) {
234
243
  branch,
235
244
  baseBranch: repo.baseBranch,
236
245
  visibility: thread.isDM ? "private" : "org"
237
- })).sessionId;
246
+ });
247
+ startedSessions.set(message, session.sessionId);
248
+ return memoryResourceId;
238
249
  } catch (error) {
239
250
  if (error instanceof ChannelSessionRejectedError || error instanceof SlackSessionStartError) throw error;
240
251
  if (error instanceof FactorySourceControlConflictError) throw new SlackSessionStartError("Could not start a session: this Factory project has repositories from more than one source-control provider.", { cause: error });
@@ -245,22 +256,55 @@ function createChannelResourceIdResolver(deps) {
245
256
  }
246
257
  /**
247
258
  * Thread id for a NEW Slack channel thread. Repo-backed threads take the
248
- * user-session id AS their thread id, matching the web convention
259
+ * session id AS their thread id, matching the web convention
249
260
  * (FactoryStartCoordinator seeds threads with threadId = sessionId) so
250
261
  * `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
251
- * sessions exactly like web-created ones — no `?resourceId=` override needed.
262
+ * sessions exactly like web-created ones — no `?sessionId=` override needed.
252
263
  * Chat-only threads keep the default random id: their `channel:...`
253
264
  * resourceId is a memory key, not a unique thread id.
254
265
  */
255
- const resolveChannelThreadId = ({ resourceId, defaultThreadId }) => resourceId.startsWith("channel:") ? defaultThreadId : resourceId;
266
+ function createChannelThreadIdResolver(startedSessions) {
267
+ return ({ message, defaultThreadId }) => startedSessions.get(message) ?? defaultThreadId;
268
+ }
269
+ /**
270
+ * The Factory session a repo-backed Slack thread belongs to. A thread under
271
+ * the Factory resource is stamped with its session id (the startup migration
272
+ * stamps threads it moved there). An unmigrated thread runs under its session
273
+ * id as its resource, and a newer thread is named after its session. A stamp
274
+ * or thread id only counts under that session's memory resource; a thread
275
+ * named after a session it can't claim is refused. With no session row for
276
+ * either, the thread is addressed by its own id and fails authorization like
277
+ * any session without a row.
278
+ */
279
+ const UNCLAIMED_SESSION = Symbol("unclaimed-session");
280
+ async function slackThreadSessionId(sessions, thread, loadThread, runs) {
281
+ if (thread.resourceId.startsWith("channel:")) return null;
282
+ const claims = async (sessionId) => {
283
+ const row = await sessions.getBySessionId(sessionId);
284
+ if (!row) return false;
285
+ if (sessionId !== thread.id && !sessionStampCounts(row, thread.resourceId)) return false;
286
+ const own = sessionId === thread.id ? thread : await loadThread(sessionId);
287
+ return thread.resourceId === memoryResourceOf(row, own, await isRunBound(row, runs));
288
+ };
289
+ const stamped = thread.metadata?.factorySessionId;
290
+ if (typeof stamped === "string" && stamped && await claims(stamped)) return stamped;
291
+ if (await sessions.getSourceControlBySessionId(thread.resourceId)) return thread.resourceId;
292
+ if (await claims(thread.id)) return thread.id;
293
+ if (await sessions.getBySessionId(thread.id)) return UNCLAIMED_SESSION;
294
+ return thread.id;
295
+ }
256
296
  /** Create channel sessions with their Factory ownership present in initial controller state. */
257
297
  function createChannelSessionResolver(deps) {
258
298
  const sourceControlSessions = createSourceControlSessionLookup(configuredSourceControls(deps));
259
299
  return async ({ controller, thread, requestContext }) => {
260
- const sourceControl = thread.resourceId.startsWith("channel:") ? null : await sourceControlSessions.getSourceControlBySessionId(thread.resourceId);
261
- const session = sourceControl ? await sourceControl.sessions.getBySessionId(thread.resourceId) : null;
300
+ const stored = thread.metadata?.factorySessionId || thread.resourceId.startsWith("channel:") ? thread : await controller.queryThreadById({ threadId: thread.id }) ?? thread;
301
+ const resolved = await slackThreadSessionId(sourceControlSessions, stored, (threadId) => controller.queryThreadById({ threadId }), deps.workItems);
302
+ if (resolved === UNCLAIMED_SESSION) throw new Error(`Slack thread ${thread.id} is named after a Factory session it doesn't belong to.`);
303
+ const sessionId = resolved ?? thread.resourceId;
304
+ const sourceControl = sessionId.startsWith("channel:") ? null : await sourceControlSessions.getSourceControlBySessionId(sessionId);
305
+ const session = sourceControl ? await sourceControl.sessions.getBySessionId(sessionId) : null;
262
306
  return controller.createSession({
263
- id: thread.resourceId,
307
+ id: sessionId,
264
308
  ownerId: controller.id,
265
309
  resourceId: thread.resourceId,
266
310
  threadId: thread.id,
@@ -299,15 +343,16 @@ function createChannelSessionStartHook(deps) {
299
343
  await seedSessionOrg(session, readRequestContextOrgId(requestContext));
300
344
  if (!projects) return;
301
345
  if (thread.resourceId.startsWith("channel:")) return;
302
- const sourceControl = await sourceControlSessions.getSourceControlBySessionId(thread.resourceId);
346
+ const sessionId = session.identity.getId();
347
+ const sourceControl = await sourceControlSessions.getSourceControlBySessionId(sessionId);
303
348
  if (!sourceControl) return;
304
- const sourceSession = await sourceControl.sessions.getBySessionId(thread.resourceId);
349
+ const sourceSession = await sourceControl.sessions.getBySessionId(sessionId);
305
350
  if (!sourceSession?.factoryProjectId) return;
306
351
  const threadId = session.thread.getId();
307
352
  const binding = threadId && deps.workItems ? await deps.workItems.findRunBindingByThread({
308
353
  orgId: sourceSession.orgId,
309
- resourceId: thread.resourceId,
310
- sessionId: thread.resourceId,
354
+ resourceId: session.identity.getResourceId(),
355
+ sessionId,
311
356
  threadId,
312
357
  rejectAmbiguous: true
313
358
  }) : null;
@@ -335,8 +380,13 @@ function createChannelSessionStartHook(deps) {
335
380
  userId: owner.userId,
336
381
  credentialScope: sessionModel.modelScope
337
382
  });
338
- const modeId = session.mode.get();
339
383
  const activeThreadId = session.thread.getId();
384
+ if (activeThreadId) await session.thread.setSettingOn({
385
+ threadId: activeThreadId,
386
+ key: "factorySessionId",
387
+ value: sessionId
388
+ });
389
+ const modeId = session.mode.get();
340
390
  if (await migratePersistedModelSelection({
341
391
  getMetadata: async () => activeThreadId ? (await session.thread.getById({ threadId: activeThreadId }))?.metadata ?? {} : {},
342
392
  modeId,
@@ -417,15 +467,16 @@ async function prepareExistingSessionOwnerContext(thread, deps, ctx, options) {
417
467
  }
418
468
  if (sourceControls.length === 0 || internalThread.resourceId.startsWith("channel:")) return "ready";
419
469
  const sessions = createSourceControlSessionLookup(sourceControls);
420
- const sessionRow = await sessions.getBySessionId(internalThread.resourceId);
470
+ const memory = await ctx.mastra?.getStorage()?.getStore("memory");
471
+ const resolved = await slackThreadSessionId(sessions, internalThread, async (threadId) => memory?.getThreadById({ threadId }), deps.workItems);
472
+ if (resolved === UNCLAIMED_SESSION) return "access-denied";
473
+ const sessionId = resolved ?? internalThread.resourceId;
474
+ const sessionRow = await sessions.getBySessionId(sessionId);
421
475
  if (sessionRow && sessionRow.orgId === options.expectedOrgId && sessionRow.visibility === "private" && sessionRow.userId !== options.senderUserId) return "access-denied";
422
- if (!options.expectedOrgId) throw new Error(`Could not authorize the owner of Slack Factory session ${internalThread.resourceId}: the sender's account link has no organization.`);
423
- const preparation = await prepareSessionRunContext(ctx.requestContext, internalThread.resourceId, { sessions }, { expectedOrgId: options.expectedOrgId });
476
+ if (!options.expectedOrgId) throw new Error(`Could not authorize the owner of Slack Factory session ${sessionId}: the sender's account link has no organization.`);
477
+ const preparation = await prepareSessionRunContext(ctx.requestContext, sessionId, { sessions }, { expectedOrgId: options.expectedOrgId });
424
478
  if (preparation === "organization-mismatch") return preparation;
425
- if (preparation !== "prepared") {
426
- const reason = preparation === "session-not-found" ? "no source-control session row exists for it, or its project repository row is missing" : "its source-control session row has no organization";
427
- throw new Error(`Could not authorize the owner of Slack Factory session ${internalThread.resourceId}: ${reason}.`);
428
- }
479
+ if (preparation !== "prepared") throw new Error(`Could not authorize the owner of Slack Factory session ${sessionId}: ${preparation === "session-not-found" ? "no source-control session row exists for it, or its project repository row is missing" : "its source-control session row has no organization"}.`);
429
480
  return "ready";
430
481
  }
431
482
  /**
@@ -654,23 +705,25 @@ function createSlackChannelsConfig(deps) {
654
705
  ...deps.slack,
655
706
  webClientOptions: { timeout: SLACK_REQUEST_TIMEOUT_MS }
656
707
  });
708
+ const slack = deps.adapterOptions?.streaming === false ? {
709
+ adapter,
710
+ ...deps.adapterOptions
711
+ } : {
712
+ adapter,
713
+ ...deps.adapterOptions,
714
+ streaming: deps.adapterOptions?.streaming ?? true
715
+ };
716
+ const startedSessions = /* @__PURE__ */ new WeakMap();
657
717
  return {
658
- adapters: { slack: deps.adapterOptions?.streaming === false ? {
659
- adapter,
660
- ...deps.adapterOptions
661
- } : {
662
- adapter,
663
- ...deps.adapterOptions,
664
- streaming: deps.adapterOptions?.streaming ?? true
665
- } },
718
+ adapters: { slack },
666
719
  handlers: createHandlers(deps),
667
- resolveResourceId: createChannelResourceIdResolver(deps),
668
- resolveThreadId: resolveChannelThreadId,
720
+ resolveResourceId: createChannelResourceIdResolver(deps, startedSessions),
721
+ resolveThreadId: createChannelThreadIdResolver(startedSessions),
669
722
  resolveSession: createChannelSessionResolver(deps),
670
723
  onSessionStart: createChannelSessionStartHook(deps)
671
724
  };
672
725
  }
673
726
  //#endregion
674
- export { createChannelResourceIdResolver, createChannelSessionResolver, createChannelSessionStartHook, createHandlers, createSlackChannelsConfig, resolveChannelThreadId, resolveFactoryForLink, resolveLinkedSender, slackThreadSource };
727
+ export { createChannelResourceIdResolver, createChannelSessionResolver, createChannelSessionStartHook, createChannelThreadIdResolver, createHandlers, createSlackChannelsConfig, resolveFactoryForLink, resolveLinkedSender, slackThreadSource };
675
728
 
676
729
  //# sourceMappingURL=slack.js.map