@mastra/factory 0.25.0-alpha.4 → 0.25.0-alpha.6

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 (358) hide show
  1. package/README.md +3 -1
  2. package/dist/auth.js +1 -2
  3. package/dist/boards/define-board.js +1 -2
  4. package/dist/boards/index.js +1 -2
  5. package/dist/boards/registry.js +1 -2
  6. package/dist/boards/relocate.js +1 -2
  7. package/dist/boards/review.js +1 -2
  8. package/dist/boards/semantics.js +1 -2
  9. package/dist/boards/transition-policy.js +1 -2
  10. package/dist/boards/work-tool-rules.js +1 -2
  11. package/dist/boards/work-transition-policy.js +1 -2
  12. package/dist/boards/work.js +1 -2
  13. package/dist/default-thinking-level.d.ts +3 -0
  14. package/dist/default-thinking-level.d.ts.map +1 -0
  15. package/dist/default-thinking-level.js +5 -0
  16. package/dist/default-thinking-level.js.map +1 -0
  17. package/dist/environment/build-runner.js +1 -2
  18. package/dist/environment/build-schedule.js +1 -2
  19. package/dist/environment/build-workflow.js +1 -2
  20. package/dist/environment/build.js +1 -2
  21. package/dist/environment/heads.js +1 -2
  22. package/dist/factory.d.ts +4 -2
  23. package/dist/factory.d.ts.map +1 -1
  24. package/dist/factory.js +62 -21
  25. package/dist/factory.js.map +1 -1
  26. package/dist/feed-events.js +1 -2
  27. package/dist/index.js +2 -2
  28. package/dist/integrations/channel-thread-announcements.d.ts +8 -0
  29. package/dist/integrations/channel-thread-announcements.d.ts.map +1 -0
  30. package/dist/integrations/channel-thread-announcements.js +16 -0
  31. package/dist/integrations/channel-thread-announcements.js.map +1 -0
  32. package/dist/integrations/github/acceptance-labels.js +1 -2
  33. package/dist/integrations/github/app-identity.js +1 -2
  34. package/dist/integrations/github/commits.js +1 -2
  35. package/dist/integrations/github/config.js +1 -2
  36. package/dist/integrations/github/default-rules.js +2 -3
  37. package/dist/integrations/github/integration.js +3 -4
  38. package/dist/integrations/github/issue-reconciler.js +1 -2
  39. package/dist/integrations/github/pat.js +1 -2
  40. package/dist/integrations/github/provenance.d.ts.map +1 -1
  41. package/dist/integrations/github/provenance.js +2 -5
  42. package/dist/integrations/github/provenance.js.map +1 -1
  43. package/dist/integrations/github/reconcile-worker.js +1 -2
  44. package/dist/integrations/github/reconciliation-config.js +1 -2
  45. package/dist/integrations/github/routes.js +4 -5
  46. package/dist/integrations/github/rules.d.ts.map +1 -1
  47. package/dist/integrations/github/rules.js +14 -3
  48. package/dist/integrations/github/rules.js.map +1 -1
  49. package/dist/integrations/github/sandbox-release.js +1 -2
  50. package/dist/integrations/github/sandbox.js +1 -2
  51. package/dist/integrations/github/session-subscriptions.js +1 -2
  52. package/dist/integrations/github/settle-or-abort.js +1 -2
  53. package/dist/integrations/github/stale-reviews.js +1 -2
  54. package/dist/integrations/github/subscriptions.js +1 -2
  55. package/dist/integrations/github/token-refresh.js +1 -2
  56. package/dist/integrations/github/webhook.d.ts.map +1 -1
  57. package/dist/integrations/github/webhook.js +17 -3
  58. package/dist/integrations/github/webhook.js.map +1 -1
  59. package/dist/integrations/gitlab/agent-tools.js +1 -2
  60. package/dist/integrations/gitlab/api.js +1 -2
  61. package/dist/integrations/gitlab/default-rules.js +1 -2
  62. package/dist/integrations/gitlab/integration.d.ts +4 -0
  63. package/dist/integrations/gitlab/integration.d.ts.map +1 -1
  64. package/dist/integrations/gitlab/integration.js +12 -4
  65. package/dist/integrations/gitlab/integration.js.map +1 -1
  66. package/dist/integrations/gitlab/issue-reconciler.js +1 -2
  67. package/dist/integrations/gitlab/merge-request-reconciler.js +1 -2
  68. package/dist/integrations/gitlab/reconciler.js +1 -2
  69. package/dist/integrations/gitlab/reconciliation-config.js +1 -2
  70. package/dist/integrations/gitlab/routes.d.ts +6 -0
  71. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  72. package/dist/integrations/gitlab/routes.js +154 -3
  73. package/dist/integrations/gitlab/routes.js.map +1 -1
  74. package/dist/integrations/gitlab/rules.d.ts.map +1 -1
  75. package/dist/integrations/gitlab/rules.js +5 -3
  76. package/dist/integrations/gitlab/rules.js.map +1 -1
  77. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  78. package/dist/integrations/gitlab/subscriptions.js +1 -2
  79. package/dist/integrations/gitlab/version-control.js +1 -2
  80. package/dist/integrations/gitlab/webhook-dispatch.js +1 -2
  81. package/dist/integrations/gitlab/webhook.js +1 -2
  82. package/dist/integrations/incidentio/agent-tools.js +1 -2
  83. package/dist/integrations/incidentio/api.js +1 -2
  84. package/dist/integrations/incidentio/claim.js +1 -2
  85. package/dist/integrations/incidentio/default-rules.js +1 -2
  86. package/dist/integrations/incidentio/intake.js +1 -2
  87. package/dist/integrations/incidentio/integration.js +1 -2
  88. package/dist/integrations/incidentio/issue-reconciler.js +1 -2
  89. package/dist/integrations/incidentio/reconciliation-config.js +1 -2
  90. package/dist/integrations/incidentio/routes.js +1 -2
  91. package/dist/integrations/incidentio/rules.js +1 -2
  92. package/dist/integrations/issue-reconcile-worker.js +1 -2
  93. package/dist/integrations/issue-reconciler.js +1 -2
  94. package/dist/integrations/jira/adf.js +1 -2
  95. package/dist/integrations/jira/agent-tools.js +1 -2
  96. package/dist/integrations/jira/api.d.ts +1 -1
  97. package/dist/integrations/jira/api.d.ts.map +1 -1
  98. package/dist/integrations/jira/api.js +2 -3
  99. package/dist/integrations/jira/api.js.map +1 -1
  100. package/dist/integrations/jira/claim.js +1 -2
  101. package/dist/integrations/jira/default-rules.js +1 -2
  102. package/dist/integrations/jira/integration.d.ts +3 -1
  103. package/dist/integrations/jira/integration.d.ts.map +1 -1
  104. package/dist/integrations/jira/integration.js +18 -4
  105. package/dist/integrations/jira/integration.js.map +1 -1
  106. package/dist/integrations/jira/issue-reconciler.js +1 -2
  107. package/dist/integrations/jira/reconciliation-config.js +1 -2
  108. package/dist/integrations/jira/routes.js +1 -2
  109. package/dist/integrations/jira/rules.js +1 -2
  110. package/dist/integrations/linear/agent-tools.d.ts +1 -1
  111. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  112. package/dist/integrations/linear/agent-tools.js +5 -5
  113. package/dist/integrations/linear/agent-tools.js.map +1 -1
  114. package/dist/integrations/linear/claim.js +1 -2
  115. package/dist/integrations/linear/default-rules.js +1 -2
  116. package/dist/integrations/linear/integration.d.ts +2 -0
  117. package/dist/integrations/linear/integration.d.ts.map +1 -1
  118. package/dist/integrations/linear/integration.js +2 -2
  119. package/dist/integrations/linear/integration.js.map +1 -1
  120. package/dist/integrations/linear/issue-reconciler.js +1 -2
  121. package/dist/integrations/linear/reconciliation-config.js +1 -2
  122. package/dist/integrations/linear/routes.js +1 -2
  123. package/dist/integrations/linear/rules.js +1 -2
  124. package/dist/integrations/platform/api-client.js +1 -2
  125. package/dist/integrations/platform/connect/routes.js +1 -2
  126. package/dist/integrations/platform/github/event-worker.js +6 -2
  127. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  128. package/dist/integrations/platform/github/integration.js +1 -2
  129. package/dist/integrations/platform/gitlab/event-worker.js +1 -2
  130. package/dist/integrations/platform/gitlab/integration.js +1 -2
  131. package/dist/integrations/platform/incidentio/integration.js +1 -2
  132. package/dist/integrations/platform/jira/agent-tools.js +1 -2
  133. package/dist/integrations/platform/jira/integration.js +1 -2
  134. package/dist/integrations/platform/jira/routes.js +1 -2
  135. package/dist/integrations/platform/linear/event-worker.js +1 -2
  136. package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
  137. package/dist/integrations/platform/linear/integration.js +2 -2
  138. package/dist/integrations/platform/linear/integration.js.map +1 -1
  139. package/dist/integrations/pull-request-head.d.ts +1 -0
  140. package/dist/integrations/pull-request-head.d.ts.map +1 -1
  141. package/dist/integrations/pull-request-head.js +3 -3
  142. package/dist/integrations/pull-request-head.js.map +1 -1
  143. package/dist/integrations/reconciliation-config.js +1 -2
  144. package/dist/integrations/slack/channel-reply-processor.d.ts +1 -0
  145. package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -1
  146. package/dist/integrations/slack/channel-reply-processor.js +16 -6
  147. package/dist/integrations/slack/channel-reply-processor.js.map +1 -1
  148. package/dist/integrations/slack/channel-session-link-processor.d.ts +1 -0
  149. package/dist/integrations/slack/channel-session-link-processor.d.ts.map +1 -1
  150. package/dist/integrations/slack/channel-session-link-processor.js +2 -3
  151. package/dist/integrations/slack/channel-session-link-processor.js.map +1 -1
  152. package/dist/integrations/slack/connect-route.js +1 -2
  153. package/dist/integrations/slack/emoji-shortcodes.generated.js +1 -2
  154. package/dist/integrations/slack/emoji.js +1 -2
  155. package/dist/integrations/slack/feed-publisher.js +1 -2
  156. package/dist/integrations/slack/integration.d.ts +5 -0
  157. package/dist/integrations/slack/integration.d.ts.map +1 -1
  158. package/dist/integrations/slack/integration.js +13 -2
  159. package/dist/integrations/slack/integration.js.map +1 -1
  160. package/dist/integrations/slack/reply-reminder-processor.js +1 -2
  161. package/dist/integrations/slack/reply-tool.d.ts.map +1 -1
  162. package/dist/integrations/slack/reply-tool.js +4 -13
  163. package/dist/integrations/slack/reply-tool.js.map +1 -1
  164. package/dist/integrations/slack/run-error-card.d.ts +3 -0
  165. package/dist/integrations/slack/run-error-card.d.ts.map +1 -0
  166. package/dist/integrations/slack/run-error-card.js +44 -0
  167. package/dist/integrations/slack/run-error-card.js.map +1 -0
  168. package/dist/integrations/slack/share-file-tool.js +1 -2
  169. package/dist/integrations/slack/slack.d.ts.map +1 -1
  170. package/dist/integrations/slack/slack.js +7 -4
  171. package/dist/integrations/slack/slack.js.map +1 -1
  172. package/dist/integrations/subscription-session.js +1 -2
  173. package/dist/integrations/workos/integration.js +1 -2
  174. package/dist/mcp/disconnect.js +1 -2
  175. package/dist/mcp/events.js +2 -3
  176. package/dist/mcp/routes.js +1 -2
  177. package/dist/packages/_internals/workspace/dist/index.js +1 -2
  178. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  179. package/dist/platform-auth-host.js +1 -2
  180. package/dist/provider-display-name.js +1 -2
  181. package/dist/review-verdict.d.ts +1 -0
  182. package/dist/review-verdict.d.ts.map +1 -1
  183. package/dist/review-verdict.js +9 -3
  184. package/dist/review-verdict.js.map +1 -1
  185. package/dist/routes/attention-activity.js +1 -2
  186. package/dist/routes/attention-parked.js +1 -2
  187. package/dist/routes/attention-proposed.js +1 -2
  188. package/dist/routes/attention-providers.js +1 -2
  189. package/dist/routes/attention.js +1 -2
  190. package/dist/routes/automation-runs.js +1 -2
  191. package/dist/routes/config.d.ts +7 -0
  192. package/dist/routes/config.d.ts.map +1 -1
  193. package/dist/routes/config.js +29 -3
  194. package/dist/routes/config.js.map +1 -1
  195. package/dist/routes/contracts.js +1 -2
  196. package/dist/routes/custom-provider-source.js +1 -2
  197. package/dist/routes/fs.js +1 -2
  198. package/dist/routes/intake.js +6 -4
  199. package/dist/routes/intake.js.map +1 -1
  200. package/dist/routes/knowledge.js +1 -2
  201. package/dist/routes/oauth.js +1 -2
  202. package/dist/routes/projects.js +1 -2
  203. package/dist/routes/provider-credentials.js +1 -2
  204. package/dist/routes/route.js +1 -2
  205. package/dist/routes/session-access.js +1 -2
  206. package/dist/routes/skills.js +1 -2
  207. package/dist/routes/source-control-sessions.js +5 -6
  208. package/dist/routes/source-control-settings.js +1 -2
  209. package/dist/routes/surface.js +3 -4
  210. package/dist/routes/surface.js.map +1 -1
  211. package/dist/routes/telemetry.js +1 -2
  212. package/dist/routes/tenant-credentials.js +1 -2
  213. package/dist/routes/work-items.js +2 -3
  214. package/dist/rules/binding-context.js +1 -2
  215. package/dist/rules/decision-applicability.js +1 -2
  216. package/dist/rules/dispatch-errors.js +1 -2
  217. package/dist/rules/dispatcher.d.ts.map +1 -1
  218. package/dist/rules/dispatcher.js +9 -3
  219. package/dist/rules/dispatcher.js.map +1 -1
  220. package/dist/rules/event-rules.js +1 -2
  221. package/dist/rules/processor.js +1 -2
  222. package/dist/rules/resolve.js +1 -2
  223. package/dist/rules/review-source-tool.js +1 -2
  224. package/dist/rules/start-coordinator.js +1 -2
  225. package/dist/rules/terminal-cleanup.js +1 -2
  226. package/dist/rules/tools.js +1 -2
  227. package/dist/rules/transition-service.js +1 -2
  228. package/dist/rules/types.d.ts +4 -1
  229. package/dist/rules/types.d.ts.map +1 -1
  230. package/dist/rules/types.js +1 -2
  231. package/dist/rules/types.js.map +1 -1
  232. package/dist/rules/validation.js +1 -2
  233. package/dist/run-error.d.ts +13 -0
  234. package/dist/run-error.d.ts.map +1 -0
  235. package/dist/run-error.js +81 -0
  236. package/dist/run-error.js.map +1 -0
  237. package/dist/sandbox/callback-factory-sandbox.js +1 -2
  238. package/dist/sandbox/git-ref.js +1 -2
  239. package/dist/sandbox/local-factory-sandbox.d.ts +12 -4
  240. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  241. package/dist/sandbox/local-factory-sandbox.js +32 -7
  242. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  243. package/dist/sandbox/materialization.js +1 -2
  244. package/dist/sandbox/session-retirement.js +1 -2
  245. package/dist/sandbox/session-sandbox.js +1 -2
  246. package/dist/sandbox/workdir.js +1 -2
  247. package/dist/secret-encryption.js +1 -2
  248. package/dist/server-error.js +1 -2
  249. package/dist/session/credential-policy.js +1 -2
  250. package/dist/session/default-model-hydration.js +1 -2
  251. package/dist/session/environment-repositories.js +1 -2
  252. package/dist/session/environment-state-processor.d.ts +0 -1
  253. package/dist/session/environment-state-processor.d.ts.map +1 -1
  254. package/dist/session/environment-state-processor.js +2 -3
  255. package/dist/session/environment-state-processor.js.map +1 -1
  256. package/dist/session/factory-session.js +1 -2
  257. package/dist/session/filesystem-capture.js +2 -3
  258. package/dist/session/filesystem-capture.js.map +1 -1
  259. package/dist/session/first-exec-capture.js +2 -3
  260. package/dist/session/first-exec-capture.js.map +1 -1
  261. package/dist/session/first-message-capture.js +1 -2
  262. package/dist/session/live-sessions.js +1 -2
  263. package/dist/session/memory-settings-hydration.js +1 -2
  264. package/dist/session/org-seed.js +1 -2
  265. package/dist/session/reply-credential-processor.js +1 -2
  266. package/dist/session/resource-migration.js +1 -2
  267. package/dist/session/run-audit.js +1 -2
  268. package/dist/session/session-threads.js +1 -2
  269. package/dist/session/session-title.js +1 -2
  270. package/dist/session/shell-commands.js +1 -2
  271. package/dist/session/source-control-tools.js +2 -3
  272. package/dist/session/thinking-default.d.ts +1 -1
  273. package/dist/session/thinking-default.d.ts.map +1 -1
  274. package/dist/session/thinking-default.js +11 -13
  275. package/dist/session/thinking-default.js.map +1 -1
  276. package/dist/session/thread-session-stamp.js +1 -2
  277. package/dist/session/thread-title-mirror.js +1 -2
  278. package/dist/session/thread-title-mirror.js.map +1 -1
  279. package/dist/session/work-item-repository.js +1 -2
  280. package/dist/session-owner-signal.js +1 -2
  281. package/dist/skills/catalog.js +1 -2
  282. package/dist/skills/service.js +1 -2
  283. package/dist/spa-static.js +1 -2
  284. package/dist/state-signing.js +1 -2
  285. package/dist/storage/domains/audit/actions.js +1 -2
  286. package/dist/storage/domains/audit/actors.js +1 -2
  287. package/dist/storage/domains/audit/agent-audit.js +1 -2
  288. package/dist/storage/domains/audit/base.js +1 -2
  289. package/dist/storage/domains/audit/domain.js +1 -2
  290. package/dist/storage/domains/audit/wire.js +1 -2
  291. package/dist/storage/domains/channel-identity/base.js +1 -2
  292. package/dist/storage/domains/comments/activity.js +1 -2
  293. package/dist/storage/domains/comments/actor.js +1 -2
  294. package/dist/storage/domains/comments/base.js +1 -2
  295. package/dist/storage/domains/comments/domain.js +1 -2
  296. package/dist/storage/domains/comments/feed-context.js +1 -2
  297. package/dist/storage/domains/comments/parse.js +1 -2
  298. package/dist/storage/domains/comments/routes.js +1 -2
  299. package/dist/storage/domains/comments/schema.js +1 -2
  300. package/dist/storage/domains/comments/wire.js +1 -2
  301. package/dist/storage/domains/credentials/base.d.ts +4 -1
  302. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  303. package/dist/storage/domains/credentials/base.js +40 -44
  304. package/dist/storage/domains/credentials/base.js.map +1 -1
  305. package/dist/storage/domains/custom-providers/base.js +1 -2
  306. package/dist/storage/domains/filesystem/base.js +1 -2
  307. package/dist/storage/domains/intake/base.d.ts +5 -0
  308. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  309. package/dist/storage/domains/intake/base.js +10 -2
  310. package/dist/storage/domains/intake/base.js.map +1 -1
  311. package/dist/storage/domains/integrations/base.js +1 -2
  312. package/dist/storage/domains/memory-settings/base.js +1 -2
  313. package/dist/storage/domains/model-defaults/base.js +1 -2
  314. package/dist/storage/domains/projects/base.d.ts +0 -1
  315. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  316. package/dist/storage/domains/projects/base.js +5 -6
  317. package/dist/storage/domains/projects/base.js.map +1 -1
  318. package/dist/storage/domains/queue-health/base.js +1 -2
  319. package/dist/storage/domains/source-control/base.js +1 -2
  320. package/dist/storage/domains/source-control/inmemory.js +1 -2
  321. package/dist/storage/domains/work-items/base.js +1 -2
  322. package/dist/storage/domains/work-items/board-version.js +1 -2
  323. package/dist/storage/domains/work-items/metrics.js +1 -2
  324. package/dist/telemetry-types.js +1 -2
  325. package/dist/telemetry.js +1 -2
  326. package/dist/timing.js +1 -2
  327. package/dist/tools/annotations.js +1 -2
  328. package/dist/tools/approval-tools.js +1 -2
  329. package/dist/tools/deliver.js +2 -3
  330. package/dist/tools/index.js +1 -2
  331. package/dist/tools/messaging-tools.js +1 -2
  332. package/dist/tools/permissions.js +1 -2
  333. package/dist/tools/read-tools.js +1 -2
  334. package/dist/tools/reports.js +1 -2
  335. package/dist/tools/run-watch.d.ts +0 -2
  336. package/dist/tools/run-watch.d.ts.map +1 -1
  337. package/dist/tools/run-watch.js +4 -44
  338. package/dist/tools/run-watch.js.map +1 -1
  339. package/dist/tools/sandbox-tools.js +1 -2
  340. package/dist/tools/scope.js +2 -3
  341. package/dist/tools/session-tools.js +1 -2
  342. package/dist/tools/sessions-state-processor.js +1 -2
  343. package/dist/tools/side-sessions.js +1 -2
  344. package/dist/tools/work-item-tools.js +1 -2
  345. package/dist/work-item-branch.js +1 -2
  346. package/dist/work-item-branch.js.map +1 -1
  347. package/dist/work-items/create-work-item.js +1 -2
  348. package/dist/work-items/decision-actions.js +1 -2
  349. package/dist/workspace.d.ts.map +1 -1
  350. package/dist/workspace.js +1 -3
  351. package/dist/workspace.js.map +1 -1
  352. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  353. package/factory-skills/factory-plan/SKILL.md +1 -1
  354. package/factory-skills/factory-rereview/SKILL.md +14 -3
  355. package/factory-skills/factory-review/SKILL.md +14 -3
  356. package/factory-skills/factory-self-review/SKILL.md +1 -1
  357. package/factory-skills/factory-triage/SKILL.md +52 -29
  358. package/package.json +19 -17
@@ -1,4 +1,4 @@
1
- //#region src/integrations/slack/feed-publisher.ts
1
+ // src/integrations/slack/feed-publisher.ts
2
2
  /** Slack rejects a `markdown_text` block over this; comments allow 16k. */
3
3
  const MAX_SLACK_MARKDOWN_CHARS = 12e3;
4
4
  var SlackFeedPublisher = class {
@@ -38,7 +38,6 @@ function slackCommentSource(threadId, messageTs, teamId) {
38
38
  externalId: `${threadId.split(":")[1] ?? ""}:${messageTs}`
39
39
  };
40
40
  }
41
- //#endregion
42
41
  export { SlackFeedPublisher, slackCommentSource };
43
42
 
44
43
  //# sourceMappingURL=feed-publisher.js.map
@@ -96,6 +96,11 @@ export declare class SlackIntegration implements FactoryIntegration {
96
96
  agentTools({ requestContext }: {
97
97
  requestContext: RequestContext;
98
98
  }): Promise<IntegrationTools>;
99
+ /**
100
+ * When a run opens a pull request, post its link into the session's Slack
101
+ * thread.
102
+ */
103
+ postToolObserver({ toolContext, requestContext, }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void>;
99
104
  channels(ctx: IntegrationContext): FactoryChannelsConfig;
100
105
  feedPublisher(ctx: IntegrationContext): WorkItemFeedPublisher;
101
106
  routes(ctx: IntegrationContext): ApiRoute[];
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAWlH;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAyED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;IAO1C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE;QAAE,KAAK,EAAE,cAAc,EAAE,CAAC;QAAC,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC;gBAQhE,MAAM,EAAE,sBAAsB;IAyB1C,UAAU,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,GAAG,IAAI;IAI7E;;;;OAIG;IACG,UAAU,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQnG,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAuBxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAalH;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAyED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;IAO1C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE;QAAE,KAAK,EAAE,cAAc,EAAE,CAAC;QAAC,MAAM,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC;gBAQhE,MAAM,EAAE,sBAAsB;IAyB1C,UAAU,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,GAAG,IAAI;IAI7E;;;;OAIG;IACG,UAAU,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQnG;;;OAGG;IACG,gBAAgB,CAAC,EACrB,WAAW,EACX,cAAc,GACf,EAAE,UAAU,CAAC,WAAW,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrF,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAuBxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
@@ -1,3 +1,5 @@
1
+ import { parseCreatedPullRequest } from "../github/session-subscriptions.js";
2
+ import { announcePullRequest } from "../channel-thread-announcements.js";
1
3
  import { FactoryChannelReplyProcessor } from "./channel-reply-processor.js";
2
4
  import { FactoryChannelSessionLinkProcessor } from "./channel-session-link-processor.js";
3
5
  import { createSlackConnectRoutes } from "./connect-route.js";
@@ -6,7 +8,7 @@ import { createShareFileTool, isSlackThread } from "./share-file-tool.js";
6
8
  import { FactoryReplyStreamProcessor, createReplyTool } from "./reply-tool.js";
7
9
  import { FactoryReplyReminderProcessor } from "./reply-reminder-processor.js";
8
10
  import { createSlackChannelsConfig } from "./slack.js";
9
- //#region src/integrations/slack/integration.ts
11
+ // src/integrations/slack/integration.ts
10
12
  /** Slack renders the status inline next to the app name; keep it to one short line. */
11
13
  const MAX_STATUS_LENGTH = 80;
12
14
  /**
@@ -117,6 +119,16 @@ var SlackIntegration = class {
117
119
  ...this.#textHidden ? createReplyTool(requestContext, this.#deps) : {}
118
120
  };
119
121
  }
122
+ /**
123
+ * When a run opens a pull request, post its link into the session's Slack
124
+ * thread.
125
+ */
126
+ async postToolObserver({ toolContext, requestContext }) {
127
+ const url = parseCreatedPullRequest(toolContext);
128
+ const threadId = (requestContext?.get("controller"))?.threadId;
129
+ if (!url || !requestContext || !threadId || !await isSlackThread(requestContext, this.#deps)) return;
130
+ announcePullRequest(this.#channels?.(), threadId, "opened", url).catch((error) => console.warn("[slack] pull request opened announcement failed", error));
131
+ }
120
132
  channels(ctx) {
121
133
  const sourceControls = ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);
122
134
  this.#repoBackedSessions = sourceControls.length > 0;
@@ -164,7 +176,6 @@ var SlackIntegration = class {
164
176
  };
165
177
  }
166
178
  };
167
- //#endregion
168
179
  export { SlackIntegration };
169
180
 
170
181
  //# sourceMappingURL=integration.js.map
@@ -1 +1 @@
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 { AgentChannels, TypingStatusFn } from '@mastra/core/channels';\nimport type { InputProcessor, OutputProcessor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { FactoryChannelReplyProcessor } from './channel-reply-processor.js';\nimport { FactoryChannelSessionLinkProcessor } from './channel-session-link-processor.js';\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { FactoryReplyReminderProcessor } from './reply-reminder-processor.js';\nimport { createReplyTool, FactoryReplyStreamProcessor, REPLY_TOOL_NAME } from './reply-tool.js';\nimport { createShareFileTool, isSlackThread } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.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\n/**\n * With reply text hidden, streamed text never reaches the thread, so it gets\n * no status: an \"is typing...\" set after the reply tool's post would sit on\n * the thread with nothing coming, and race the clear at the end of the run.\n */\nconst factoryTypingStatus =\n (textDisplay: SlackAdapterChannelConfig['textDisplay']): TypingStatusFn =>\n chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return textDisplay === 'hidden' ? undefined : 'is typing...';\n // The reply tool's text streams into the thread as the model writes it.\n case 'tool-call-input-streaming-start':\n return chunk.payload.toolName === REPLY_TOOL_NAME ? 'is typing...' : undefined;\n case 'tool-call':\n // Still finishing the streamed reply; don't flip to \"is working...\".\n if (chunk.payload.toolName === REPLY_TOOL_NAME) return 'is typing...';\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 const textDisplay = overrides.textDisplay ?? 'hidden';\n return { typingStatus: factoryTypingStatus(textDisplay), toolDisplay: 'hidden', textDisplay, ...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 readonly #adapterOptions: SlackAdapterChannelConfig;\n /** Bound in `initialize()`; the controller mounts after integrations are built. */\n #channels: ShareFileToolDeps['channels'];\n readonly #deps: ShareFileToolDeps = { channels: () => this.#channels?.() };\n /**\n * With reply text hidden (the Factory default) the agent speaks through\n * `reply_in_thread`, so the prompt, reminder and live streaming for that\n * tool come with it. The setting is fixed for the deployment, so it is\n * decided once here rather than on every run.\n */\n readonly processors: { input: InputProcessor[]; output: OutputProcessor[] };\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 this.#adapterOptions = factoryAdapterOptions(adapterOverrides(config.adapterOptions));\n const textHidden = this.#textHidden;\n this.processors = {\n input: [\n new FactoryChannelReplyProcessor(),\n ...(textHidden ? [new FactoryReplyReminderProcessor()] : []),\n ],\n output: [\n new FactoryChannelSessionLinkProcessor(),\n ...(textHidden ? [new FactoryReplyStreamProcessor(this.#deps)] : []),\n ],\n };\n }\n\n get #textHidden(): boolean {\n return this.#adapterOptions.textDisplay === 'hidden';\n }\n\n initialize(args: { channels?: () => AgentChannels | null | undefined }): void {\n this.#channels = args.channels;\n }\n\n /**\n * `attach_file`, and `reply_in_thread` when reply text is hidden, on runs\n * whose session is linked to a Slack thread. The tools find the thread to\n * post into when they are called.\n */\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await isSlackThread(requestContext, this.#deps))) return {};\n return {\n ...createShareFileTool(requestContext, this.#deps),\n ...(this.#textHidden ? createReplyTool(requestContext, this.#deps) : {}),\n };\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: this.#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":"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"}
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 { AgentChannels, TypingStatusFn } from '@mastra/core/channels';\nimport type { InputProcessor, OutputProcessor } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { SlackAdapterChannelConfig } from '@mastra/slack';\n\nimport type { WorkItemFeedPublisher } from '../../storage/domains/comments/feed-sync.js';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { announcePullRequest } from '../channel-thread-announcements.js';\nimport { parseCreatedPullRequest } from '../github/session-subscriptions.js';\nimport { FactoryChannelReplyProcessor } from './channel-reply-processor.js';\nimport { FactoryChannelSessionLinkProcessor } from './channel-session-link-processor.js';\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { SlackFeedPublisher } from './feed-publisher.js';\nimport { FactoryReplyReminderProcessor } from './reply-reminder-processor.js';\nimport { createReplyTool, FactoryReplyStreamProcessor, REPLY_TOOL_NAME } from './reply-tool.js';\nimport { createShareFileTool, isSlackThread } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.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\n/**\n * With reply text hidden, streamed text never reaches the thread, so it gets\n * no status: an \"is typing...\" set after the reply tool's post would sit on\n * the thread with nothing coming, and race the clear at the end of the run.\n */\nconst factoryTypingStatus =\n (textDisplay: SlackAdapterChannelConfig['textDisplay']): TypingStatusFn =>\n chunk => {\n switch (chunk.type) {\n case 'reasoning-delta':\n return 'is thinking...';\n case 'text-delta':\n return textDisplay === 'hidden' ? undefined : 'is typing...';\n // The reply tool's text streams into the thread as the model writes it.\n case 'tool-call-input-streaming-start':\n return chunk.payload.toolName === REPLY_TOOL_NAME ? 'is typing...' : undefined;\n case 'tool-call':\n // Still finishing the streamed reply; don't flip to \"is working...\".\n if (chunk.payload.toolName === REPLY_TOOL_NAME) return 'is typing...';\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 const textDisplay = overrides.textDisplay ?? 'hidden';\n return { typingStatus: factoryTypingStatus(textDisplay), toolDisplay: 'hidden', textDisplay, ...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 readonly #adapterOptions: SlackAdapterChannelConfig;\n /** Bound in `initialize()`; the controller mounts after integrations are built. */\n #channels: ShareFileToolDeps['channels'];\n readonly #deps: ShareFileToolDeps = { channels: () => this.#channels?.() };\n /**\n * With reply text hidden (the Factory default) the agent speaks through\n * `reply_in_thread`, so the prompt, reminder and live streaming for that\n * tool come with it. The setting is fixed for the deployment, so it is\n * decided once here rather than on every run.\n */\n readonly processors: { input: InputProcessor[]; output: OutputProcessor[] };\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 this.#adapterOptions = factoryAdapterOptions(adapterOverrides(config.adapterOptions));\n const textHidden = this.#textHidden;\n this.processors = {\n input: [\n new FactoryChannelReplyProcessor(),\n ...(textHidden ? [new FactoryReplyReminderProcessor()] : []),\n ],\n output: [\n new FactoryChannelSessionLinkProcessor(),\n ...(textHidden ? [new FactoryReplyStreamProcessor(this.#deps)] : []),\n ],\n };\n }\n\n get #textHidden(): boolean {\n return this.#adapterOptions.textDisplay === 'hidden';\n }\n\n initialize(args: { channels?: () => AgentChannels | null | undefined }): void {\n this.#channels = args.channels;\n }\n\n /**\n * `attach_file`, and `reply_in_thread` when reply text is hidden, on runs\n * whose session is linked to a Slack thread. The tools find the thread to\n * post into when they are called.\n */\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n if (!(await isSlackThread(requestContext, this.#deps))) return {};\n return {\n ...createShareFileTool(requestContext, this.#deps),\n ...(this.#textHidden ? createReplyTool(requestContext, this.#deps) : {}),\n };\n }\n\n /**\n * When a run opens a pull request, post its link into the session's Slack\n * thread.\n */\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const url = parseCreatedPullRequest(toolContext);\n const threadId = (requestContext?.get('controller') as { threadId?: string } | undefined)?.threadId;\n if (!url || !requestContext || !threadId || !(await isSlackThread(requestContext, this.#deps))) return;\n // Not awaited: the tool call must not wait on Slack.\n void announcePullRequest(this.#channels?.(), threadId, 'opened', url).catch(error =>\n console.warn('[slack] pull request opened announcement failed', error),\n );\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: this.#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":"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"}
@@ -1,5 +1,5 @@
1
1
  import { REPLY_TOOL_NAME } from "./reply-tool.js";
2
- //#region src/integrations/slack/reply-reminder-processor.ts
2
+ // src/integrations/slack/reply-reminder-processor.ts
3
3
  const ID = "factory-reply-reminder";
4
4
  /** How long a run may work without posting before it is nudged. */
5
5
  const REPLY_REMINDER_AFTER_MS = 3e5;
@@ -52,7 +52,6 @@ function replyCalls(messages) {
52
52
  }
53
53
  return ids;
54
54
  }
55
- //#endregion
56
55
  export { FactoryReplyReminderProcessor, REPLY_REMINDER_AFTER_MS };
57
56
 
58
57
  //# sourceMappingURL=reply-reminder-processor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reply-tool.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/reply-tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAWjD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,cAAc,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CAyB9G;AA8FD;;;;;;GAMG;AACH,qBAAa,2BAA4B,YAAW,SAAS;IAG/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,QAAQ,CAAC,EAAE,0BAA0B;gBAER,IAAI,GAAE,iBAAsB;IAEnD,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAyBxG;AAmBD,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAwBlF"}
1
+ {"version":3,"file":"reply-tool.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/reply-tool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAWjD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,cAAc,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CAyB9G;AAuFD;;;;;;GAMG;AACH,qBAAa,2BAA4B,YAAW,SAAS;IAG/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,QAAQ,CAAC,EAAE,0BAA0B;gBAER,IAAI,GAAE,iBAAsB;IAEnD,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAyBxG;AAmBD,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAwBlF"}
@@ -1,11 +1,10 @@
1
- import { readFactorySessionScope } from "../../rules/binding-context.js";
2
- import { SESSION_LINK_LABEL, sessionLink } from "./channel-reply-processor.js";
3
- import "./channel-session-link-processor.js";
1
+ import { SESSION_LINK_LABEL } from "./channel-reply-processor.js";
2
+ import { sessionLinkFromRequest } from "./channel-session-link-processor.js";
4
3
  import { postTarget, resolveRenderContext } from "./share-file-tool.js";
5
4
  import "@mastra/core/channels";
6
5
  import { z } from "zod";
7
6
  import { createTool } from "@mastra/core/tools";
8
- //#region src/integrations/slack/reply-tool.ts
7
+ // src/integrations/slack/reply-tool.ts
9
8
  const REPLY_TOOL_NAME = "reply_in_thread";
10
9
  /** Set once the first reply of a new session carried the session link. */
11
10
  const LINKED_KEY = "factory__replyLinked";
@@ -42,14 +41,7 @@ function createReplyTool(requestContext, deps = {}) {
42
41
  */
43
42
  function firstReplyLink(requestContext) {
44
43
  if (!requestContext.get("factory__newChannelSession") || requestContext.get(LINKED_KEY)) return void 0;
45
- const controller = requestContext.get("controller");
46
- if (!controller?.threadId) return void 0;
47
- return sessionLink({
48
- threadId: controller.threadId,
49
- sessionId: controller.sessionId,
50
- resourceId: controller.resourceId,
51
- factoryProjectId: readFactorySessionScope(requestContext)?.factoryProjectId
52
- });
44
+ return sessionLinkFromRequest(requestContext);
53
45
  }
54
46
  /** Live posts of `reply_in_thread` calls whose text is still streaming in, by tool call id. */
55
47
  const STREAMS_KEY = "factory__replyStreams";
@@ -197,7 +189,6 @@ function partialStringField(json, field) {
197
189
  }
198
190
  return out;
199
191
  }
200
- //#endregion
201
192
  export { FactoryReplyStreamProcessor, REPLY_TOOL_NAME, createReplyTool, partialStringField };
202
193
 
203
194
  //# sourceMappingURL=reply-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reply-tool.js","names":[],"sources":["../../../src/integrations/slack/reply-tool.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { CHAT_CHANNEL_RENDER_CONTEXT_KEY } from '@mastra/core/channels';\nimport type { ChatChannelRenderContext } from '@mastra/core/channels';\nimport type { Processor, ProcessOutputStreamArgs } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ChunkType } from '@mastra/core/stream';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport { readFactorySessionScope } from '../../rules/binding-context.js';\nimport type { IntegrationTools } from '../base.js';\nimport { SESSION_LINK_LABEL, sessionLink } from './channel-reply-processor.js';\nimport { NEW_CHANNEL_SESSION_KEY } from './channel-session-link-processor.js';\nimport { postTarget, resolveRenderContext } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.js';\n\nexport const REPLY_TOOL_NAME = 'reply_in_thread';\n\n/** Set once the first reply of a new session carried the session link. */\nconst LINKED_KEY = 'factory__replyLinked';\n\nconst replyInput = z\n .object({\n text: z.string().trim().min(1).max(12_000).describe('The message to post, in markdown.'),\n })\n .strict();\n\n/**\n * The only way a channel run speaks when the adapter hides reply text\n * (`textDisplay: 'hidden'`). Posting becomes a choice the agent makes, so it\n * stops narrating each step into the thread and can say nothing on purpose.\n * Offered only when the Slack adapter hides reply text and the run's thread is\n * linked to Slack; with text shown the reply stream already posts and this\n * tool would double every message.\n */\nexport function createReplyTool(requestContext: RequestContext, deps: ShareFileToolDeps = {}): IntegrationTools {\n return {\n [REPLY_TOOL_NAME]: createTool({\n id: REPLY_TOOL_NAME,\n description:\n 'Post a message in this chat thread. Nothing you write outside this tool reaches the thread, so use it for anything the people there should see: a short note that you are starting a longer job, a question you need answered, and the outcome when you finish. Post when you have something new; do not repeat yourself or post that nothing changed. To stay quiet, do not call it.',\n inputSchema: replyInput,\n execute: async (input, context) => {\n const link = firstReplyLink(requestContext);\n const suffix = link ? `\\n\\n[${SESSION_LINK_LABEL}](${link})` : '';\n // FactoryReplyStreamProcessor may already be streaming this call's text\n // into the thread; finish that post rather than posting it again.\n const toolCallId = context?.agent?.toolCallId;\n const streamed = toolCallId ? replyStreams(requestContext).get(toolCallId) : undefined;\n if (toolCallId) replyStreams(requestContext).delete(toolCallId);\n if (!streamed || !(await streamed.finish(input.text, suffix))) {\n const render = await postTarget(requestContext, deps);\n await render.chatThread.post({ markdown: `${input.text}${suffix}` });\n }\n // Marked only once posted, so a failed first post still gets the link on the retry.\n if (link) requestContext.set(LINKED_KEY, true);\n return { posted: true };\n },\n }),\n };\n}\n\n/**\n * The session link the first reply of a new session ends with. With reply\n * text hidden, `FactoryChannelSessionLinkProcessor`'s text delta never\n * reaches the thread, so the tool carries the link instead.\n */\nfunction firstReplyLink(requestContext: RequestContext): string | undefined {\n if (!requestContext.get(NEW_CHANNEL_SESSION_KEY) || requestContext.get(LINKED_KEY)) return undefined;\n const controller = requestContext.get('controller') as AgentControllerRequestContext | undefined;\n if (!controller?.threadId) return undefined;\n return sessionLink({\n threadId: controller.threadId,\n sessionId: controller.sessionId,\n resourceId: controller.resourceId,\n factoryProjectId: readFactorySessionScope(requestContext)?.factoryProjectId,\n });\n}\n\n/** Live posts of `reply_in_thread` calls whose text is still streaming in, by tool call id. */\nconst STREAMS_KEY = 'factory__replyStreams';\n\ninterface ReplyStream {\n /** Show the text decoded from the args so far. */\n update(text: string): void;\n /** End the post with the validated text. Resolves false if the post failed. */\n finish(text: string, suffix: string): Promise<boolean>;\n /** End the post with whatever has been shown, for a call that never ran. */\n close(): Promise<boolean>;\n}\n\nfunction replyStreams(requestContext: RequestContext): Map<string, ReplyStream> {\n let streams = requestContext.get(STREAMS_KEY) as Map<string, ReplyStream> | undefined;\n if (!streams) {\n streams = new Map();\n requestContext.set(STREAMS_KEY, streams);\n }\n return streams;\n}\n\n/**\n * One streamed Slack post, opened on the first visible text so an abandoned\n * call never leaves an empty message behind.\n */\nfunction openReplyStream(chatThread: ChatChannelRenderContext['chatThread']): ReplyStream {\n const queue: string[] = [];\n let sent = '';\n let closed = false;\n let wake: (() => void) | undefined;\n let done: Promise<boolean> | undefined;\n\n async function* chunks(): AsyncGenerator<string> {\n while (true) {\n while (queue.length > 0) yield queue.shift()!;\n if (closed) return;\n await new Promise<void>(resolve => (wake = resolve));\n }\n }\n const push = (text: string) => {\n if (!text) return;\n queue.push(text);\n done ??= chatThread.post(chunks()).then(\n () => true,\n error => {\n console.warn('[factory] streamed reply failed; posting it whole', { error });\n return false;\n },\n );\n wake?.();\n };\n\n return {\n update(text) {\n const visible = text.trimStart();\n if (!visible.startsWith(sent)) return;\n push(visible.slice(sent.length));\n sent = visible;\n },\n async finish(text, suffix) {\n if (text.startsWith(sent)) push(text.slice(sent.length));\n if (!done) return false;\n push(suffix);\n closed = true;\n wake?.();\n return done;\n },\n async close() {\n closed = true;\n wake?.();\n return done ?? false;\n },\n };\n}\n\n/**\n * Streams a `reply_in_thread` call's `text` into the thread while the model is\n * still writing it, so a long reply appears as it is written instead of all at\n * once when the call completes. The tool finishes the post with the validated\n * text. Runs whose adapter doesn't stream, or that show reply text, are left\n * alone.\n */\nexport class FactoryReplyStreamProcessor implements Processor {\n readonly id = 'factory-reply-stream';\n\n constructor(private readonly deps: ShareFileToolDeps = {}) {}\n\n async processOutputStream({ part, state, requestContext }: ProcessOutputStreamArgs): Promise<ChunkType> {\n if (!requestContext) return part;\n const args = (state.args ??= {}) as Record<string, string>;\n if (part.type === 'tool-call-input-streaming-start' && part.payload.toolName === REPLY_TOOL_NAME) {\n // Resolve the thread the same way the tool does, so a run started from\n // the web UI or woken by a signal streams too, not only a Slack-started one.\n const render = await resolveRenderContext(requestContext, this.deps);\n // A static adapter (`streaming: false`) posts each reply whole, so the tool does too.\n if (render?.textDisplay !== 'hidden' || !render.streaming?.enabled) return part;\n args[part.payload.toolCallId] = '';\n replyStreams(requestContext).set(part.payload.toolCallId, openReplyStream(render.chatThread));\n } else if (part.type === 'tool-call-delta' && part.payload.toolCallId in args) {\n const json = (args[part.payload.toolCallId] += part.payload.argsTextDelta);\n const text = partialStringField(json, 'text');\n if (text !== undefined) replyStreams(requestContext).get(part.payload.toolCallId)?.update(text);\n } else if ((part.type === 'tool-error' || part.type === 'tool-result') && part.payload.toolCallId in args) {\n // A call that failed validation never reaches `execute`; a successful one already finished its post.\n delete args[part.payload.toolCallId];\n await closeStream(requestContext, part.payload.toolCallId);\n } else if (RUN_END.has(part.type)) {\n for (const toolCallId of Object.keys(args)) await closeStream(requestContext, toolCallId);\n state.args = {};\n }\n return part;\n }\n}\n\n/** Chunks after which no `reply_in_thread` call of this run will still execute. */\nconst RUN_END = new Set<string>(['finish', 'abort', 'error', 'tripwire']);\n\n/**\n * Ends a streamed post whose call will never reach `execute`, keeping the text\n * shown so far, so Slack isn't left with a message that never finishes.\n */\nasync function closeStream(requestContext: RequestContext, toolCallId: string): Promise<void> {\n const streams = replyStreams(requestContext);\n const stream = streams.get(toolCallId);\n if (!stream) return;\n streams.delete(toolCallId);\n await stream.close();\n}\n\nconst ESCAPES: Record<string, string> = { '\"': '\"', '\\\\': '\\\\', '/': '/', b: '\\b', f: '\\f', n: '\\n', r: '\\r', t: '\\t' };\n\n/** The decoded value of a top-level string field in JSON that may still be cut off mid-string. */\nexport function partialStringField(json: string, field: string): string | undefined {\n const start = new RegExp(`\"${field}\"\\\\s*:\\\\s*\"`).exec(json);\n if (!start) return undefined;\n let out = '';\n for (let i = start.index + start[0].length; i < json.length; i++) {\n const ch = json[i]!;\n if (ch === '\"') return out;\n if (ch !== '\\\\') {\n out += ch;\n continue;\n }\n const next = json[i + 1];\n if (next === undefined) return out;\n if (next === 'u') {\n const hex = json.slice(i + 2, i + 6);\n if (hex.length < 4) return out;\n out += String.fromCharCode(parseInt(hex, 16));\n i += 5;\n } else {\n out += ESCAPES[next] ?? next;\n i += 1;\n }\n }\n return out;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reply-tool.js","names":[],"sources":["../../../src/integrations/slack/reply-tool.ts"],"sourcesContent":["import { CHAT_CHANNEL_RENDER_CONTEXT_KEY } from '@mastra/core/channels';\nimport type { ChatChannelRenderContext } from '@mastra/core/channels';\nimport type { Processor, ProcessOutputStreamArgs } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ChunkType } from '@mastra/core/stream';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../base.js';\nimport { SESSION_LINK_LABEL } from './channel-reply-processor.js';\nimport { NEW_CHANNEL_SESSION_KEY, sessionLinkFromRequest } from './channel-session-link-processor.js';\nimport { postTarget, resolveRenderContext } from './share-file-tool.js';\nimport type { ShareFileToolDeps } from './share-file-tool.js';\n\nexport const REPLY_TOOL_NAME = 'reply_in_thread';\n\n/** Set once the first reply of a new session carried the session link. */\nconst LINKED_KEY = 'factory__replyLinked';\n\nconst replyInput = z\n .object({\n text: z.string().trim().min(1).max(12_000).describe('The message to post, in markdown.'),\n })\n .strict();\n\n/**\n * The only way a channel run speaks when the adapter hides reply text\n * (`textDisplay: 'hidden'`). Posting becomes a choice the agent makes, so it\n * stops narrating each step into the thread and can say nothing on purpose.\n * Offered only when the Slack adapter hides reply text and the run's thread is\n * linked to Slack; with text shown the reply stream already posts and this\n * tool would double every message.\n */\nexport function createReplyTool(requestContext: RequestContext, deps: ShareFileToolDeps = {}): IntegrationTools {\n return {\n [REPLY_TOOL_NAME]: createTool({\n id: REPLY_TOOL_NAME,\n description:\n 'Post a message in this chat thread. Nothing you write outside this tool reaches the thread, so use it for anything the people there should see: a short note that you are starting a longer job, a question you need answered, and the outcome when you finish. Post when you have something new; do not repeat yourself or post that nothing changed. To stay quiet, do not call it.',\n inputSchema: replyInput,\n execute: async (input, context) => {\n const link = firstReplyLink(requestContext);\n const suffix = link ? `\\n\\n[${SESSION_LINK_LABEL}](${link})` : '';\n // FactoryReplyStreamProcessor may already be streaming this call's text\n // into the thread; finish that post rather than posting it again.\n const toolCallId = context?.agent?.toolCallId;\n const streamed = toolCallId ? replyStreams(requestContext).get(toolCallId) : undefined;\n if (toolCallId) replyStreams(requestContext).delete(toolCallId);\n if (!streamed || !(await streamed.finish(input.text, suffix))) {\n const render = await postTarget(requestContext, deps);\n await render.chatThread.post({ markdown: `${input.text}${suffix}` });\n }\n // Marked only once posted, so a failed first post still gets the link on the retry.\n if (link) requestContext.set(LINKED_KEY, true);\n return { posted: true };\n },\n }),\n };\n}\n\n/**\n * The session link the first reply of a new session ends with. With reply\n * text hidden, `FactoryChannelSessionLinkProcessor`'s text delta never\n * reaches the thread, so the tool carries the link instead.\n */\nfunction firstReplyLink(requestContext: RequestContext): string | undefined {\n if (!requestContext.get(NEW_CHANNEL_SESSION_KEY) || requestContext.get(LINKED_KEY)) return undefined;\n return sessionLinkFromRequest(requestContext);\n}\n\n/** Live posts of `reply_in_thread` calls whose text is still streaming in, by tool call id. */\nconst STREAMS_KEY = 'factory__replyStreams';\n\ninterface ReplyStream {\n /** Show the text decoded from the args so far. */\n update(text: string): void;\n /** End the post with the validated text. Resolves false if the post failed. */\n finish(text: string, suffix: string): Promise<boolean>;\n /** End the post with whatever has been shown, for a call that never ran. */\n close(): Promise<boolean>;\n}\n\nfunction replyStreams(requestContext: RequestContext): Map<string, ReplyStream> {\n let streams = requestContext.get(STREAMS_KEY) as Map<string, ReplyStream> | undefined;\n if (!streams) {\n streams = new Map();\n requestContext.set(STREAMS_KEY, streams);\n }\n return streams;\n}\n\n/**\n * One streamed Slack post, opened on the first visible text so an abandoned\n * call never leaves an empty message behind.\n */\nfunction openReplyStream(chatThread: ChatChannelRenderContext['chatThread']): ReplyStream {\n const queue: string[] = [];\n let sent = '';\n let closed = false;\n let wake: (() => void) | undefined;\n let done: Promise<boolean> | undefined;\n\n async function* chunks(): AsyncGenerator<string> {\n while (true) {\n while (queue.length > 0) yield queue.shift()!;\n if (closed) return;\n await new Promise<void>(resolve => (wake = resolve));\n }\n }\n const push = (text: string) => {\n if (!text) return;\n queue.push(text);\n done ??= chatThread.post(chunks()).then(\n () => true,\n error => {\n console.warn('[factory] streamed reply failed; posting it whole', { error });\n return false;\n },\n );\n wake?.();\n };\n\n return {\n update(text) {\n const visible = text.trimStart();\n if (!visible.startsWith(sent)) return;\n push(visible.slice(sent.length));\n sent = visible;\n },\n async finish(text, suffix) {\n if (text.startsWith(sent)) push(text.slice(sent.length));\n if (!done) return false;\n push(suffix);\n closed = true;\n wake?.();\n return done;\n },\n async close() {\n closed = true;\n wake?.();\n return done ?? false;\n },\n };\n}\n\n/**\n * Streams a `reply_in_thread` call's `text` into the thread while the model is\n * still writing it, so a long reply appears as it is written instead of all at\n * once when the call completes. The tool finishes the post with the validated\n * text. Runs whose adapter doesn't stream, or that show reply text, are left\n * alone.\n */\nexport class FactoryReplyStreamProcessor implements Processor {\n readonly id = 'factory-reply-stream';\n\n constructor(private readonly deps: ShareFileToolDeps = {}) {}\n\n async processOutputStream({ part, state, requestContext }: ProcessOutputStreamArgs): Promise<ChunkType> {\n if (!requestContext) return part;\n const args = (state.args ??= {}) as Record<string, string>;\n if (part.type === 'tool-call-input-streaming-start' && part.payload.toolName === REPLY_TOOL_NAME) {\n // Resolve the thread the same way the tool does, so a run started from\n // the web UI or woken by a signal streams too, not only a Slack-started one.\n const render = await resolveRenderContext(requestContext, this.deps);\n // A static adapter (`streaming: false`) posts each reply whole, so the tool does too.\n if (render?.textDisplay !== 'hidden' || !render.streaming?.enabled) return part;\n args[part.payload.toolCallId] = '';\n replyStreams(requestContext).set(part.payload.toolCallId, openReplyStream(render.chatThread));\n } else if (part.type === 'tool-call-delta' && part.payload.toolCallId in args) {\n const json = (args[part.payload.toolCallId] += part.payload.argsTextDelta);\n const text = partialStringField(json, 'text');\n if (text !== undefined) replyStreams(requestContext).get(part.payload.toolCallId)?.update(text);\n } else if ((part.type === 'tool-error' || part.type === 'tool-result') && part.payload.toolCallId in args) {\n // A call that failed validation never reaches `execute`; a successful one already finished its post.\n delete args[part.payload.toolCallId];\n await closeStream(requestContext, part.payload.toolCallId);\n } else if (RUN_END.has(part.type)) {\n for (const toolCallId of Object.keys(args)) await closeStream(requestContext, toolCallId);\n state.args = {};\n }\n return part;\n }\n}\n\n/** Chunks after which no `reply_in_thread` call of this run will still execute. */\nconst RUN_END = new Set<string>(['finish', 'abort', 'error', 'tripwire']);\n\n/**\n * Ends a streamed post whose call will never reach `execute`, keeping the text\n * shown so far, so Slack isn't left with a message that never finishes.\n */\nasync function closeStream(requestContext: RequestContext, toolCallId: string): Promise<void> {\n const streams = replyStreams(requestContext);\n const stream = streams.get(toolCallId);\n if (!stream) return;\n streams.delete(toolCallId);\n await stream.close();\n}\n\nconst ESCAPES: Record<string, string> = { '\"': '\"', '\\\\': '\\\\', '/': '/', b: '\\b', f: '\\f', n: '\\n', r: '\\r', t: '\\t' };\n\n/** The decoded value of a top-level string field in JSON that may still be cut off mid-string. */\nexport function partialStringField(json: string, field: string): string | undefined {\n const start = new RegExp(`\"${field}\"\\\\s*:\\\\s*\"`).exec(json);\n if (!start) return undefined;\n let out = '';\n for (let i = start.index + start[0].length; i < json.length; i++) {\n const ch = json[i]!;\n if (ch === '\"') return out;\n if (ch !== '\\\\') {\n out += ch;\n continue;\n }\n const next = json[i + 1];\n if (next === undefined) return out;\n if (next === 'u') {\n const hex = json.slice(i + 2, i + 6);\n if (hex.length < 4) return out;\n out += String.fromCharCode(parseInt(hex, 16));\n i += 5;\n } else {\n out += ESCAPES[next] ?? next;\n i += 1;\n }\n }\n return out;\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ChannelErrorContext } from '@mastra/core/channels';
2
+ export declare function slackRunErrorCard(error: Error, { requestContext }: ChannelErrorContext): import("chat").CardElement;
3
+ //# sourceMappingURL=run-error-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-error-card.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/run-error-card.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAWjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,mBAAmB,8BAatF"}
@@ -0,0 +1,44 @@
1
+ import { readFactorySessionScope } from "../../rules/binding-context.js";
2
+ import { describeRunError } from "../../run-error.js";
3
+ import { modelSettingsLink } from "./channel-reply-processor.js";
4
+ import { sessionLinkFromRequest } from "./channel-session-link-processor.js";
5
+ import { providerRequestId } from "@mastra/core/error";
6
+ import { Actions, Card, CardText, LinkButton } from "chat";
7
+ // src/integrations/slack/run-error-card.ts
8
+ function slackRunErrorCard(error, { requestContext }) {
9
+ const { title, message, recoveries, model } = describeRunError(error);
10
+ const requestId = providerRequestId(error);
11
+ const facts = [model && `Model: ${model}`, requestId && `Request id: ${requestId}`].filter(Boolean).join(" · ");
12
+ const links = requestContext ? recoveryLinks(requestContext, recoveries) : [];
13
+ return Card({
14
+ title: `❌ ${title}`,
15
+ children: [
16
+ CardText(message),
17
+ ...facts ? [CardText(facts, { style: "muted" })] : [],
18
+ ...links.length ? [Actions(links.map((link) => LinkButton(link)))] : []
19
+ ]
20
+ });
21
+ }
22
+ function recoveryLinks(requestContext, recoveries) {
23
+ const factoryProjectId = readFactorySessionScope(requestContext)?.factoryProjectId;
24
+ const settingsUrl = factoryProjectId && modelSettingsLink(factoryProjectId);
25
+ const sessionUrl = sessionLinkFromRequest(requestContext);
26
+ const links = recoveries.flatMap((recovery) => {
27
+ if (recovery === "credentials") return settingsUrl ? [{
28
+ url: settingsUrl,
29
+ label: "Review credentials"
30
+ }] : [];
31
+ return sessionUrl ? [{
32
+ url: sessionUrl,
33
+ label: "Change model"
34
+ }] : [];
35
+ });
36
+ if (links.length || !sessionUrl) return links;
37
+ return [{
38
+ url: sessionUrl,
39
+ label: "Open session"
40
+ }];
41
+ }
42
+ export { slackRunErrorCard };
43
+
44
+ //# sourceMappingURL=run-error-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-error-card.js","names":[],"sources":["../../../src/integrations/slack/run-error-card.ts"],"sourcesContent":["import type { ChannelErrorContext } from '@mastra/core/channels';\nimport { providerRequestId } from '@mastra/core/error';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Actions, Card, CardText, LinkButton } from 'chat';\n\nimport { readFactorySessionScope } from '../../rules/binding-context.js';\nimport type { RunErrorRecovery } from '../../run-error.js';\nimport { describeRunError } from '../../run-error.js';\nimport { modelSettingsLink } from './channel-reply-processor.js';\nimport { sessionLinkFromRequest } from './channel-session-link-processor.js';\n\nexport function slackRunErrorCard(error: Error, { requestContext }: ChannelErrorContext) {\n const { title, message, recoveries, model } = describeRunError(error);\n const requestId = providerRequestId(error);\n const facts = [model && `Model: ${model}`, requestId && `Request id: ${requestId}`].filter(Boolean).join(' · ');\n const links = requestContext ? recoveryLinks(requestContext, recoveries) : [];\n return Card({\n title: `❌ ${title}`,\n children: [\n CardText(message),\n ...(facts ? [CardText(facts, { style: 'muted' })] : []),\n ...(links.length ? [Actions(links.map(link => LinkButton(link)))] : []),\n ],\n });\n}\n\nfunction recoveryLinks(requestContext: RequestContext, recoveries: RunErrorRecovery[]) {\n const factoryProjectId = readFactorySessionScope(requestContext)?.factoryProjectId;\n const settingsUrl = factoryProjectId && modelSettingsLink(factoryProjectId);\n const sessionUrl = sessionLinkFromRequest(requestContext);\n const links = recoveries.flatMap(recovery => {\n if (recovery === 'credentials') return settingsUrl ? [{ url: settingsUrl, label: 'Review credentials' }] : [];\n return sessionUrl ? [{ url: sessionUrl, label: 'Change model' }] : [];\n });\n if (links.length || !sessionUrl) return links;\n return [{ url: sessionUrl, label: 'Open session' }];\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,kBAAkB,OAAc,EAAE,kBAAuC;CACvF,MAAM,EAAE,OAAO,SAAS,YAAY,UAAU,iBAAiB,KAAK;CACpE,MAAM,YAAY,kBAAkB,KAAK;CACzC,MAAM,QAAQ,CAAC,SAAS,UAAU,SAAS,aAAa,eAAe,WAAW,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CAC9G,MAAM,QAAQ,iBAAiB,cAAc,gBAAgB,UAAU,IAAI,CAAC;CAC5E,OAAO,KAAK;EACV,OAAO,KAAK;EACZ,UAAU;GACR,SAAS,OAAO;GAChB,GAAI,QAAQ,CAAC,SAAS,OAAO,EAAE,OAAO,QAAQ,CAAC,CAAC,IAAI,CAAC;GACrD,GAAI,MAAM,SAAS,CAAC,QAAQ,MAAM,KAAI,SAAQ,WAAW,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;EACvE;CACF,CAAC;AACH;AAEA,SAAS,cAAc,gBAAgC,YAAgC;CACrF,MAAM,mBAAmB,wBAAwB,cAAc,CAAC,EAAE;CAClE,MAAM,cAAc,oBAAoB,kBAAkB,gBAAgB;CAC1E,MAAM,aAAa,uBAAuB,cAAc;CACxD,MAAM,QAAQ,WAAW,SAAQ,aAAY;EAC3C,IAAI,aAAa,eAAe,OAAO,cAAc,CAAC;GAAE,KAAK;GAAa,OAAO;EAAqB,CAAC,IAAI,CAAC;EAC5G,OAAO,aAAa,CAAC;GAAE,KAAK;GAAY,OAAO;EAAe,CAAC,IAAI,CAAC;CACtE,CAAC;CACD,IAAI,MAAM,UAAU,CAAC,YAAY,OAAO;CACxC,OAAO,CAAC;EAAE,KAAK;EAAY,OAAO;CAAe,CAAC;AACpD"}
@@ -1,7 +1,7 @@
1
1
  import { CHAT_CHANNEL_RENDER_CONTEXT_KEY } from "@mastra/core/channels";
2
2
  import { z } from "zod";
3
3
  import { createTool } from "@mastra/core/tools";
4
- //#region src/integrations/slack/share-file-tool.ts
4
+ // src/integrations/slack/share-file-tool.ts
5
5
  /** Slack rejects larger uploads well before this; keep the sandbox read bounded too. */
6
6
  const SHARE_FILE_MAX_BYTES = 52428800;
7
7
  /**
@@ -100,7 +100,6 @@ function createShareFileTool(requestContext, deps = {}) {
100
100
  }
101
101
  }) };
102
102
  }
103
- //#endregion
104
103
  export { SHARE_FILE_MAX_BYTES, createShareFileTool, isSlackThread, postTarget, resolveRenderContext };
105
104
 
106
105
  //# sourceMappingURL=share-file-tool.js.map
@@ -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,19 +1,20 @@
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";
4
3
  import { primeTenantCredentialsForRequestContext, primeTenantCredentialsForTenant } from "../../routes/tenant-credentials.js";
5
4
  import { readRequestContextOrgId, seedSessionOrg } from "../../session/org-seed.js";
6
5
  import { prepareSessionRunContext } from "../subscription-session.js";
7
6
  import { FactorySourceControlConflictError, createSourceControlSessionLookup, hydrateFactorySession, resolveFactorySessionModel, resolveFactorySourceControl, resolvePrimaryEnvironmentRepository } from "../../session/factory-session.js";
7
+ import { isRunBound, memoryResourceOf, sessionStampCounts } from "../../session/session-threads.js";
8
8
  import { FACTORY_SLACK_SENDER_KEY } from "../../tools/scope.js";
9
9
  import { markNewChannelSession } from "./channel-session-link-processor.js";
10
10
  import { resolveEmojiShortcodes } from "./emoji.js";
11
11
  import { slackCommentSource } from "./feed-publisher.js";
12
+ import { slackRunErrorCard } from "./run-error-card.js";
12
13
  import { ChannelSessionRejectedError } from "@mastra/core/channels";
13
14
  import { migratePersistedModelSelection } from "@mastra/core/agent-controller";
14
15
  import { createSlackAdapter } from "@mastra/slack";
15
16
  import { Actions, Card, CardText, LinkButton } from "chat";
16
- //#region src/integrations/slack/slack.ts
17
+ // src/integrations/slack/slack.ts
17
18
  const SLACK_REQUEST_TIMEOUT_MS = 15e3;
18
19
  var SlackSessionStartError = class extends Error {};
19
20
  function configuredSourceControls(deps) {
@@ -753,12 +754,15 @@ function createSlackChannelsConfig(deps) {
753
754
  ...deps.slack,
754
755
  webClientOptions: { timeout: SLACK_REQUEST_TIMEOUT_MS }
755
756
  });
757
+ const formatError = deps.adapterOptions?.formatError ?? slackRunErrorCard;
756
758
  const slack = deps.adapterOptions?.streaming === false ? {
757
759
  adapter,
758
- ...deps.adapterOptions
760
+ ...deps.adapterOptions,
761
+ formatError
759
762
  } : {
760
763
  adapter,
761
764
  ...deps.adapterOptions,
765
+ formatError,
762
766
  streaming: deps.adapterOptions?.streaming ?? true
763
767
  };
764
768
  const startedSessions = /* @__PURE__ */ new WeakMap();
@@ -771,7 +775,6 @@ function createSlackChannelsConfig(deps) {
771
775
  onSessionStart: createChannelSessionStartHook(deps)
772
776
  };
773
777
  }
774
- //#endregion
775
778
  export { createChannelResourceIdResolver, createChannelSessionResolver, createChannelSessionStartHook, createChannelThreadIdResolver, createHandlers, createSlackChannelsConfig, resolveFactoryForLink, resolveLinkedSender, slackThreadSource };
776
779
 
777
780
  //# sourceMappingURL=slack.js.map