@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.10

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 (406) hide show
  1. package/README.md +106 -4
  2. package/dist/auth.d.ts +14 -0
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +45 -9
  5. package/dist/auth.js.map +1 -1
  6. package/dist/boards/review.d.ts.map +1 -1
  7. package/dist/boards/review.js +21 -1
  8. package/dist/boards/review.js.map +1 -1
  9. package/dist/boards/work-transition-policy.d.ts.map +1 -1
  10. package/dist/boards/work-transition-policy.js +12 -1
  11. package/dist/boards/work-transition-policy.js.map +1 -1
  12. package/dist/boards/work.d.ts +14 -0
  13. package/dist/boards/work.d.ts.map +1 -1
  14. package/dist/boards/work.js +70 -7
  15. package/dist/boards/work.js.map +1 -1
  16. package/dist/capabilities/version-control.d.ts +26 -0
  17. package/dist/capabilities/version-control.d.ts.map +1 -1
  18. package/dist/factory.d.ts +8 -0
  19. package/dist/factory.d.ts.map +1 -1
  20. package/dist/factory.js +354 -27
  21. package/dist/factory.js.map +1 -1
  22. package/dist/integrations/base.d.ts +1 -1
  23. package/dist/integrations/base.d.ts.map +1 -1
  24. package/dist/integrations/github/default-rules.d.ts.map +1 -1
  25. package/dist/integrations/github/default-rules.js +1 -16
  26. package/dist/integrations/github/default-rules.js.map +1 -1
  27. package/dist/integrations/github/integration.d.ts.map +1 -1
  28. package/dist/integrations/github/integration.js +37 -1
  29. package/dist/integrations/github/integration.js.map +1 -1
  30. package/dist/integrations/github/routes.d.ts +1 -1
  31. package/dist/integrations/github/routes.d.ts.map +1 -1
  32. package/dist/integrations/github/routes.js +19 -11
  33. package/dist/integrations/github/routes.js.map +1 -1
  34. package/dist/integrations/github/rules.d.ts.map +1 -1
  35. package/dist/integrations/github/rules.js +26 -22
  36. package/dist/integrations/github/rules.js.map +1 -1
  37. package/dist/integrations/github/sandbox-release.d.ts +15 -3
  38. package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
  39. package/dist/integrations/github/sandbox-release.js +20 -3
  40. package/dist/integrations/github/sandbox-release.js.map +1 -1
  41. package/dist/integrations/github/sandbox.d.ts +2 -3
  42. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  43. package/dist/integrations/github/sandbox.js +78 -7
  44. package/dist/integrations/github/sandbox.js.map +1 -1
  45. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  46. package/dist/integrations/github/session-subscriptions.js +1 -2
  47. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  48. package/dist/integrations/github/subscriptions.d.ts +7 -4
  49. package/dist/integrations/github/subscriptions.d.ts.map +1 -1
  50. package/dist/integrations/github/subscriptions.js +5 -5
  51. package/dist/integrations/github/subscriptions.js.map +1 -1
  52. package/dist/integrations/github/webhook.d.ts +4 -2
  53. package/dist/integrations/github/webhook.d.ts.map +1 -1
  54. package/dist/integrations/github/webhook.js +59 -36
  55. package/dist/integrations/github/webhook.js.map +1 -1
  56. package/dist/integrations/gitlab/api.d.ts +16 -1
  57. package/dist/integrations/gitlab/api.d.ts.map +1 -1
  58. package/dist/integrations/gitlab/api.js +9 -0
  59. package/dist/integrations/gitlab/api.js.map +1 -1
  60. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  61. package/dist/integrations/gitlab/routes.js +10 -6
  62. package/dist/integrations/gitlab/routes.js.map +1 -1
  63. package/dist/integrations/gitlab/rules.d.ts.map +1 -1
  64. package/dist/integrations/gitlab/rules.js +28 -21
  65. package/dist/integrations/gitlab/rules.js.map +1 -1
  66. package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
  67. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  68. package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
  69. package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
  70. package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
  71. package/dist/integrations/gitlab/subscriptions.js +5 -5
  72. package/dist/integrations/gitlab/subscriptions.js.map +1 -1
  73. package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
  74. package/dist/integrations/gitlab/version-control.js +37 -3
  75. package/dist/integrations/gitlab/version-control.js.map +1 -1
  76. package/dist/integrations/incidentio/api.d.ts +9 -0
  77. package/dist/integrations/incidentio/api.d.ts.map +1 -1
  78. package/dist/integrations/incidentio/api.js +3 -0
  79. package/dist/integrations/incidentio/api.js.map +1 -1
  80. package/dist/integrations/incidentio/intake.d.ts +1 -1
  81. package/dist/integrations/incidentio/intake.d.ts.map +1 -1
  82. package/dist/integrations/incidentio/intake.js.map +1 -1
  83. package/dist/integrations/incidentio/integration.d.ts.map +1 -1
  84. package/dist/integrations/incidentio/integration.js +1 -0
  85. package/dist/integrations/incidentio/integration.js.map +1 -1
  86. package/dist/integrations/jira/api.d.ts +7 -0
  87. package/dist/integrations/jira/api.d.ts.map +1 -1
  88. package/dist/integrations/jira/api.js +4 -0
  89. package/dist/integrations/jira/api.js.map +1 -1
  90. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  91. package/dist/integrations/linear/agent-tools.js.map +1 -1
  92. package/dist/integrations/platform/api-client.d.ts +6 -0
  93. package/dist/integrations/platform/api-client.d.ts.map +1 -1
  94. package/dist/integrations/platform/api-client.js +6 -1
  95. package/dist/integrations/platform/api-client.js.map +1 -1
  96. package/dist/integrations/platform/connect/routes.d.ts +1 -1
  97. package/dist/integrations/platform/connect/routes.d.ts.map +1 -1
  98. package/dist/integrations/platform/connect/routes.js.map +1 -1
  99. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  100. package/dist/integrations/platform/github/integration.js +27 -1
  101. package/dist/integrations/platform/github/integration.js.map +1 -1
  102. package/dist/integrations/platform/incidentio/integration.d.ts +1 -1
  103. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  104. package/dist/integrations/platform/incidentio/integration.js +1 -0
  105. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  106. package/dist/integrations/platform/jira/integration.d.ts +1 -1
  107. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  108. package/dist/integrations/platform/jira/integration.js +1 -0
  109. package/dist/integrations/platform/jira/integration.js.map +1 -1
  110. package/dist/integrations/platform/linear/event-worker.d.ts +1 -1
  111. package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
  112. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  113. package/dist/integrations/slack/integration.d.ts.map +1 -1
  114. package/dist/integrations/slack/integration.js +33 -6
  115. package/dist/integrations/slack/integration.js.map +1 -1
  116. package/dist/integrations/slack/slack.d.ts +16 -5
  117. package/dist/integrations/slack/slack.d.ts.map +1 -1
  118. package/dist/integrations/slack/slack.js +137 -64
  119. package/dist/integrations/slack/slack.js.map +1 -1
  120. package/dist/integrations/subscription-session.d.ts +14 -0
  121. package/dist/integrations/subscription-session.d.ts.map +1 -1
  122. package/dist/integrations/subscription-session.js +30 -16
  123. package/dist/integrations/subscription-session.js.map +1 -1
  124. package/dist/mcp/disconnect.d.ts +10 -0
  125. package/dist/mcp/disconnect.d.ts.map +1 -0
  126. package/dist/mcp/disconnect.js +44 -0
  127. package/dist/mcp/disconnect.js.map +1 -0
  128. package/dist/mcp/routes.d.ts +46 -0
  129. package/dist/mcp/routes.d.ts.map +1 -0
  130. package/dist/mcp/routes.js +243 -0
  131. package/dist/mcp/routes.js.map +1 -0
  132. package/dist/provider-display-name.d.ts +2 -0
  133. package/dist/provider-display-name.d.ts.map +1 -0
  134. package/dist/provider-display-name.js +16 -0
  135. package/dist/provider-display-name.js.map +1 -0
  136. package/dist/review-verdict.d.ts +11 -0
  137. package/dist/review-verdict.d.ts.map +1 -1
  138. package/dist/review-verdict.js +16 -1
  139. package/dist/review-verdict.js.map +1 -1
  140. package/dist/routes/attention-activity.d.ts.map +1 -1
  141. package/dist/routes/attention-activity.js +5 -2
  142. package/dist/routes/attention-activity.js.map +1 -1
  143. package/dist/routes/attention-providers.d.ts +0 -1
  144. package/dist/routes/attention-providers.d.ts.map +1 -1
  145. package/dist/routes/attention-providers.js +5 -6
  146. package/dist/routes/attention-providers.js.map +1 -1
  147. package/dist/routes/config.d.ts +44 -5
  148. package/dist/routes/config.d.ts.map +1 -1
  149. package/dist/routes/config.js +181 -58
  150. package/dist/routes/config.js.map +1 -1
  151. package/dist/routes/contracts.d.ts +4 -0
  152. package/dist/routes/contracts.d.ts.map +1 -1
  153. package/dist/routes/contracts.js +3 -1
  154. package/dist/routes/contracts.js.map +1 -1
  155. package/dist/routes/custom-provider-source.d.ts +8 -0
  156. package/dist/routes/custom-provider-source.d.ts.map +1 -1
  157. package/dist/routes/custom-provider-source.js +22 -1
  158. package/dist/routes/custom-provider-source.js.map +1 -1
  159. package/dist/routes/oauth.js +1 -1
  160. package/dist/routes/oauth.js.map +1 -1
  161. package/dist/routes/projects.d.ts +1 -1
  162. package/dist/routes/projects.d.ts.map +1 -1
  163. package/dist/routes/projects.js +1 -1
  164. package/dist/routes/projects.js.map +1 -1
  165. package/dist/routes/provider-credentials.d.ts +7 -2
  166. package/dist/routes/provider-credentials.d.ts.map +1 -1
  167. package/dist/routes/provider-credentials.js +30 -8
  168. package/dist/routes/provider-credentials.js.map +1 -1
  169. package/dist/routes/skills.d.ts +2 -4
  170. package/dist/routes/skills.d.ts.map +1 -1
  171. package/dist/routes/skills.js +19 -65
  172. package/dist/routes/skills.js.map +1 -1
  173. package/dist/routes/source-control-sessions.d.ts +9 -1
  174. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  175. package/dist/routes/source-control-sessions.js +131 -7
  176. package/dist/routes/source-control-sessions.js.map +1 -1
  177. package/dist/routes/surface.d.ts +3 -2
  178. package/dist/routes/surface.d.ts.map +1 -1
  179. package/dist/routes/surface.js +7 -3
  180. package/dist/routes/surface.js.map +1 -1
  181. package/dist/routes/work-items.d.ts.map +1 -1
  182. package/dist/routes/work-items.js +60 -120
  183. package/dist/routes/work-items.js.map +1 -1
  184. package/dist/rules/binding-context.js +7 -7
  185. package/dist/rules/binding-context.js.map +1 -1
  186. package/dist/rules/dispatch-errors.d.ts.map +1 -1
  187. package/dist/rules/dispatch-errors.js +12 -0
  188. package/dist/rules/dispatch-errors.js.map +1 -1
  189. package/dist/rules/dispatcher.d.ts +8 -1
  190. package/dist/rules/dispatcher.d.ts.map +1 -1
  191. package/dist/rules/dispatcher.js +117 -12
  192. package/dist/rules/dispatcher.js.map +1 -1
  193. package/dist/rules/processor.d.ts.map +1 -1
  194. package/dist/rules/processor.js +2 -1
  195. package/dist/rules/processor.js.map +1 -1
  196. package/dist/rules/start-coordinator.d.ts +4 -0
  197. package/dist/rules/start-coordinator.d.ts.map +1 -1
  198. package/dist/rules/start-coordinator.js +36 -4
  199. package/dist/rules/start-coordinator.js.map +1 -1
  200. package/dist/rules/tools.d.ts.map +1 -1
  201. package/dist/rules/tools.js +82 -3
  202. package/dist/rules/tools.js.map +1 -1
  203. package/dist/rules/transition-service.d.ts +17 -0
  204. package/dist/rules/transition-service.d.ts.map +1 -1
  205. package/dist/rules/transition-service.js +25 -0
  206. package/dist/rules/transition-service.js.map +1 -1
  207. package/dist/rules/types.d.ts +2 -4
  208. package/dist/rules/types.d.ts.map +1 -1
  209. package/dist/rules/types.js +1 -4
  210. package/dist/rules/types.js.map +1 -1
  211. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  212. package/dist/sandbox/local-factory-sandbox.js +1 -0
  213. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  214. package/dist/sandbox/session-retirement.d.ts +27 -3
  215. package/dist/sandbox/session-retirement.d.ts.map +1 -1
  216. package/dist/sandbox/session-retirement.js +158 -20
  217. package/dist/sandbox/session-retirement.js.map +1 -1
  218. package/dist/secret-encryption.d.ts.map +1 -1
  219. package/dist/secret-encryption.js +65 -25
  220. package/dist/secret-encryption.js.map +1 -1
  221. package/dist/session/channel-reply-processor.d.ts +36 -0
  222. package/dist/session/channel-reply-processor.d.ts.map +1 -0
  223. package/dist/session/channel-reply-processor.js +46 -0
  224. package/dist/session/channel-reply-processor.js.map +1 -0
  225. package/dist/session/channel-session-link-processor.d.ts +39 -0
  226. package/dist/session/channel-session-link-processor.d.ts.map +1 -0
  227. package/dist/session/channel-session-link-processor.js +100 -0
  228. package/dist/session/channel-session-link-processor.js.map +1 -0
  229. package/dist/session/credential-policy.d.ts +1 -1
  230. package/dist/session/credential-policy.d.ts.map +1 -1
  231. package/dist/session/credential-policy.js +7 -6
  232. package/dist/session/credential-policy.js.map +1 -1
  233. package/dist/session/default-model-hydration.d.ts +1 -0
  234. package/dist/session/default-model-hydration.d.ts.map +1 -1
  235. package/dist/session/default-model-hydration.js +3 -2
  236. package/dist/session/default-model-hydration.js.map +1 -1
  237. package/dist/session/environment-state-processor.js +1 -1
  238. package/dist/session/environment-state-processor.js.map +1 -1
  239. package/dist/session/factory-session.d.ts +19 -1
  240. package/dist/session/factory-session.d.ts.map +1 -1
  241. package/dist/session/factory-session.js +5 -2
  242. package/dist/session/factory-session.js.map +1 -1
  243. package/dist/session/filesystem-capture.d.ts +3 -3
  244. package/dist/session/filesystem-capture.d.ts.map +1 -1
  245. package/dist/session/filesystem-capture.js +16 -17
  246. package/dist/session/filesystem-capture.js.map +1 -1
  247. package/dist/session/first-exec-capture.d.ts +1 -1
  248. package/dist/session/first-exec-capture.d.ts.map +1 -1
  249. package/dist/session/first-exec-capture.js +1 -1
  250. package/dist/session/first-exec-capture.js.map +1 -1
  251. package/dist/session/first-message-capture.d.ts +1 -1
  252. package/dist/session/first-message-capture.d.ts.map +1 -1
  253. package/dist/session/first-message-capture.js +1 -1
  254. package/dist/session/first-message-capture.js.map +1 -1
  255. package/dist/session/live-sessions.d.ts +1 -1
  256. package/dist/session/live-sessions.js +1 -1
  257. package/dist/session/live-sessions.js.map +1 -1
  258. package/dist/session/memory-settings-hydration.d.ts +1 -1
  259. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  260. package/dist/session/memory-settings-hydration.js +1 -1
  261. package/dist/session/memory-settings-hydration.js.map +1 -1
  262. package/dist/session/reply-credential-processor.d.ts +15 -0
  263. package/dist/session/reply-credential-processor.d.ts.map +1 -0
  264. package/dist/session/reply-credential-processor.js +38 -0
  265. package/dist/session/reply-credential-processor.js.map +1 -0
  266. package/dist/session/resource-migration.d.ts +87 -0
  267. package/dist/session/resource-migration.d.ts.map +1 -0
  268. package/dist/session/resource-migration.js +163 -0
  269. package/dist/session/resource-migration.js.map +1 -0
  270. package/dist/session/run-audit.d.ts +24 -0
  271. package/dist/session/run-audit.d.ts.map +1 -1
  272. package/dist/session/run-audit.js +93 -7
  273. package/dist/session/run-audit.js.map +1 -1
  274. package/dist/session/session-threads.d.ts +49 -0
  275. package/dist/session/session-threads.d.ts.map +1 -0
  276. package/dist/session/session-threads.js +70 -0
  277. package/dist/session/session-threads.js.map +1 -0
  278. package/dist/session/source-control-tools.d.ts +19 -1
  279. package/dist/session/source-control-tools.d.ts.map +1 -1
  280. package/dist/session/source-control-tools.js +78 -11
  281. package/dist/session/source-control-tools.js.map +1 -1
  282. package/dist/session/thinking-default.d.ts.map +1 -1
  283. package/dist/session/thinking-default.js +3 -4
  284. package/dist/session/thinking-default.js.map +1 -1
  285. package/dist/session/thread-session-stamp.d.ts +21 -0
  286. package/dist/session/thread-session-stamp.d.ts.map +1 -0
  287. package/dist/session/thread-session-stamp.js +20 -0
  288. package/dist/session/thread-session-stamp.js.map +1 -0
  289. package/dist/session/thread-title-mirror.d.ts +1 -1
  290. package/dist/session/thread-title-mirror.d.ts.map +1 -1
  291. package/dist/session/thread-title-mirror.js +1 -1
  292. package/dist/session/thread-title-mirror.js.map +1 -1
  293. package/dist/session/work-item-repository.d.ts +1 -1
  294. package/dist/session/work-item-repository.d.ts.map +1 -1
  295. package/dist/session/work-item-repository.js.map +1 -1
  296. package/dist/skills/service.d.ts +6 -8
  297. package/dist/skills/service.d.ts.map +1 -1
  298. package/dist/skills/service.js +3 -3
  299. package/dist/skills/service.js.map +1 -1
  300. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  301. package/dist/storage/domains/audit/agent-audit.js +2 -2
  302. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  303. package/dist/storage/domains/audit/domain.js +1 -1
  304. package/dist/storage/domains/filesystem/base.d.ts +4 -0
  305. package/dist/storage/domains/filesystem/base.d.ts.map +1 -1
  306. package/dist/storage/domains/filesystem/base.js +5 -0
  307. package/dist/storage/domains/filesystem/base.js.map +1 -1
  308. package/dist/storage/domains/integrations/base.d.ts +8 -0
  309. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  310. package/dist/storage/domains/integrations/base.js +7 -0
  311. package/dist/storage/domains/integrations/base.js.map +1 -1
  312. package/dist/storage/domains/projects/base.d.ts +6 -0
  313. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  314. package/dist/storage/domains/projects/base.js +12 -0
  315. package/dist/storage/domains/projects/base.js.map +1 -1
  316. package/dist/storage/domains/source-control/base.d.ts +28 -0
  317. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  318. package/dist/storage/domains/source-control/base.js +48 -0
  319. package/dist/storage/domains/source-control/base.js.map +1 -1
  320. package/dist/storage/domains/source-control/inmemory.d.ts +8 -0
  321. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  322. package/dist/storage/domains/source-control/inmemory.js +13 -1
  323. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  324. package/dist/storage/domains/work-items/base.d.ts +79 -7
  325. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  326. package/dist/storage/domains/work-items/base.js +172 -30
  327. package/dist/storage/domains/work-items/base.js.map +1 -1
  328. package/dist/timing.d.ts +23 -0
  329. package/dist/timing.d.ts.map +1 -1
  330. package/dist/timing.js +85 -6
  331. package/dist/timing.js.map +1 -1
  332. package/dist/tools/annotations.d.ts +7 -0
  333. package/dist/tools/annotations.d.ts.map +1 -0
  334. package/dist/tools/annotations.js +102 -0
  335. package/dist/tools/annotations.js.map +1 -0
  336. package/dist/tools/approval-tools.d.ts +15 -0
  337. package/dist/tools/approval-tools.d.ts.map +1 -0
  338. package/dist/tools/approval-tools.js +79 -0
  339. package/dist/tools/approval-tools.js.map +1 -0
  340. package/dist/tools/deliver.d.ts +71 -0
  341. package/dist/tools/deliver.d.ts.map +1 -0
  342. package/dist/tools/deliver.js +129 -0
  343. package/dist/tools/deliver.js.map +1 -0
  344. package/dist/tools/index.d.ts +30 -0
  345. package/dist/tools/index.d.ts.map +1 -0
  346. package/dist/tools/index.js +36 -0
  347. package/dist/tools/index.js.map +1 -0
  348. package/dist/tools/messaging-tools.d.ts +28 -0
  349. package/dist/tools/messaging-tools.d.ts.map +1 -0
  350. package/dist/tools/messaging-tools.js +396 -0
  351. package/dist/tools/messaging-tools.js.map +1 -0
  352. package/dist/tools/permissions.d.ts +31 -0
  353. package/dist/tools/permissions.d.ts.map +1 -0
  354. package/dist/tools/permissions.js +48 -0
  355. package/dist/tools/permissions.js.map +1 -0
  356. package/dist/tools/read-tools.d.ts +30 -0
  357. package/dist/tools/read-tools.d.ts.map +1 -0
  358. package/dist/tools/read-tools.js +293 -0
  359. package/dist/tools/read-tools.js.map +1 -0
  360. package/dist/tools/reports.d.ts +174 -0
  361. package/dist/tools/reports.d.ts.map +1 -0
  362. package/dist/tools/reports.js +380 -0
  363. package/dist/tools/reports.js.map +1 -0
  364. package/dist/tools/run-watch.d.ts +46 -0
  365. package/dist/tools/run-watch.d.ts.map +1 -0
  366. package/dist/tools/run-watch.js +102 -0
  367. package/dist/tools/run-watch.js.map +1 -0
  368. package/dist/tools/scope.d.ts +54 -0
  369. package/dist/tools/scope.d.ts.map +1 -0
  370. package/dist/tools/scope.js +87 -0
  371. package/dist/tools/scope.js.map +1 -0
  372. package/dist/tools/session-tools.d.ts +43 -0
  373. package/dist/tools/session-tools.d.ts.map +1 -0
  374. package/dist/tools/session-tools.js +410 -0
  375. package/dist/tools/session-tools.js.map +1 -0
  376. package/dist/tools/sessions-state-processor.d.ts +41 -0
  377. package/dist/tools/sessions-state-processor.d.ts.map +1 -0
  378. package/dist/tools/sessions-state-processor.js +190 -0
  379. package/dist/tools/sessions-state-processor.js.map +1 -0
  380. package/dist/tools/side-sessions.d.ts +53 -0
  381. package/dist/tools/side-sessions.d.ts.map +1 -0
  382. package/dist/tools/side-sessions.js +83 -0
  383. package/dist/tools/side-sessions.js.map +1 -0
  384. package/dist/tools/work-item-tools.d.ts +28 -0
  385. package/dist/tools/work-item-tools.d.ts.map +1 -0
  386. package/dist/tools/work-item-tools.js +342 -0
  387. package/dist/tools/work-item-tools.js.map +1 -0
  388. package/dist/work-items/create-work-item.d.ts +55 -0
  389. package/dist/work-items/create-work-item.d.ts.map +1 -0
  390. package/dist/work-items/create-work-item.js +101 -0
  391. package/dist/work-items/create-work-item.js.map +1 -0
  392. package/dist/work-items/decision-actions.d.ts +42 -0
  393. package/dist/work-items/decision-actions.d.ts.map +1 -0
  394. package/dist/work-items/decision-actions.js +70 -0
  395. package/dist/work-items/decision-actions.js.map +1 -0
  396. package/dist/workspace.d.ts +1 -1
  397. package/dist/workspace.d.ts.map +1 -1
  398. package/dist/workspace.js +15 -7
  399. package/dist/workspace.js.map +1 -1
  400. package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
  401. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  402. package/factory-skills/factory-plan/SKILL.md +4 -4
  403. package/factory-skills/factory-rereview/SKILL.md +3 -1
  404. package/factory-skills/factory-review/SKILL.md +3 -1
  405. package/factory-skills/factory-self-review/SKILL.md +46 -0
  406. package/package.json +15 -15
@@ -0,0 +1,380 @@
1
+ import { FACTORY_SESSION_NOTIFICATION_SOURCE } from "./deliver.js";
2
+ import { workItemNumber } from "./read-tools.js";
3
+ //#region src/tools/reports.ts
4
+ /** How each report status travels. Final reports never coalesce, so a later report can't overwrite them. */
5
+ const FACTORY_REPORT_FORMS = {
6
+ failed: {
7
+ kind: "failed",
8
+ priority: "urgent",
9
+ coalesce: false
10
+ },
11
+ blocked: {
12
+ kind: "blocked",
13
+ priority: "urgent",
14
+ coalesce: false
15
+ },
16
+ done: {
17
+ kind: "done",
18
+ priority: "high",
19
+ coalesce: false
20
+ },
21
+ progress: {
22
+ kind: "progress",
23
+ priority: "low",
24
+ coalesce: true
25
+ },
26
+ report: {
27
+ kind: "report",
28
+ priority: "high",
29
+ coalesce: false
30
+ }
31
+ };
32
+ /** How many recent `factory-session` notifications on a recipient thread the final-report check reads. */
33
+ const FINAL_REPORT_SCAN = 100;
34
+ /**
35
+ * Whether `senderSessionId` already put a report of one of `kinds` on the
36
+ * recipient thread since `since`. Reads the durable notifications store, so it
37
+ * holds across restarts. `listNotifications` has no time filter: records come
38
+ * newest-updated first, and the scan stops at the first one last updated
39
+ * before `since` (nothing after it can have been created later).
40
+ */
41
+ async function hasFinalReport(notifications, { senderSessionId, recipientThreadId, since, kinds, agentOnly = false }) {
42
+ const records = await notifications.listNotifications({
43
+ threadId: recipientThreadId,
44
+ source: FACTORY_SESSION_NOTIFICATION_SOURCE,
45
+ limit: FINAL_REPORT_SCAN
46
+ });
47
+ for (const record of records) {
48
+ if (record.updatedAt < since) break;
49
+ if (record.attributes?.["from-session"] === senderSessionId && !(agentOnly && record.attributes?.backstop === "true") && (kinds.includes(record.kind) || record.kind === "reply" && typeof record.attributes?.status === "string" && kinds.includes(record.attributes.status)) && record.createdAt >= since) return true;
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Whether the newest report `senderSessionId` put on the recipient thread is
55
+ * already this exact `failed` backstop. A session stuck failing the same way
56
+ * (a run that dies before the model, restarted over and over) then reports
57
+ * once, not once per attempt; any other report in between resets it.
58
+ */
59
+ async function repeatsLastFailure(notifications, { senderSessionId, recipientThreadId, summary }) {
60
+ const last = (await notifications.listNotifications({
61
+ threadId: recipientThreadId,
62
+ source: FACTORY_SESSION_NOTIFICATION_SOURCE,
63
+ limit: FINAL_REPORT_SCAN
64
+ })).filter((record) => record.attributes?.["from-session"] === senderSessionId).reduce((newest, record) => !newest || record.createdAt > newest.createdAt ? record : newest, void 0);
65
+ return last?.kind === "failed" && last.attributes?.backstop === "true" && last.summary === summary;
66
+ }
67
+ function reportNotification({ sender, status, message, attributes = {} }) {
68
+ const form = FACTORY_REPORT_FORMS[status ?? "report"];
69
+ return {
70
+ kind: form.kind,
71
+ priority: form.priority,
72
+ summary: message,
73
+ payload: { message },
74
+ sourceId: sender.sessionId,
75
+ ...form.coalesce ? { coalesceKey: `factory-session:${sender.sessionId}:${form.kind}` } : {},
76
+ attributes: {
77
+ ...attributes,
78
+ ...status ? { status } : {},
79
+ "from-session": sender.sessionId,
80
+ "from-thread": sender.threadId,
81
+ "from-user": sender.userId
82
+ }
83
+ };
84
+ }
85
+ /** Everyone following a work item, except the session doing the reporting. */
86
+ async function workItemReportRecipients(workItems, { orgId, workItemId, senderSessionId }) {
87
+ return (await workItems.listWorkItemSubscribers({
88
+ orgId,
89
+ workItemId
90
+ })).filter((subscriber) => subscriber.sessionId !== senderSessionId).map((subscriber) => ({
91
+ sessionId: subscriber.sessionId,
92
+ threadId: subscriber.threadId
93
+ }));
94
+ }
95
+ /** How reports name an item: `#n`, else its quoted title, else its id. */
96
+ async function workItemReference(workItems, { orgId, factoryProjectId, workItemId }) {
97
+ const item = await workItems.getForProject(orgId, factoryProjectId, workItemId);
98
+ const number = item ? workItemNumber(item) : null;
99
+ if (number !== null) return {
100
+ label: `#${number}`,
101
+ ref: `#${number}`
102
+ };
103
+ return {
104
+ label: item ? `"${item.title}"` : workItemId,
105
+ ref: workItemId
106
+ };
107
+ }
108
+ /** Attributes that say which item and role a worker's report is about. */
109
+ async function workItemReportAttributes(workItems, { orgId, factoryProjectId, workItemId, role }) {
110
+ const { label, ref } = await workItemReference(workItems, {
111
+ orgId,
112
+ factoryProjectId,
113
+ workItemId
114
+ });
115
+ return {
116
+ label,
117
+ attributes: {
118
+ "work-item": ref,
119
+ role
120
+ }
121
+ };
122
+ }
123
+ /**
124
+ * The run-end backstop: send `outcome` to each recipient that hasn't had a
125
+ * final report from this sender since the run started. An agent's own `done`
126
+ * or `failed` report wins. A run that completes after the agent's own
127
+ * `blocked` sends nothing: the agent ended its turn to wait for an answer, and
128
+ * its `done` comes from a later run. A `failed` backstop still goes out after an
129
+ * earlier `blocked`.
130
+ */
131
+ async function sendRunEndReport(reporting, { sender, recipients, since, outcome, subject, attributes = {} }) {
132
+ const kinds = outcome.status === "blocked" ? ["blocked"] : ["done", "failed"];
133
+ const message = outcome.status === "done" ? `${subject} finished.` : outcome.status === "failed" ? `${subject} failed${outcome.failureCode ? ` (${outcome.failureCode})` : ""}: ${outcome.reason}` : outcome.approval ? `${subject} is waiting for someone to approve ${outcome.toolName ?? "a tool call"} in that session.` : `${subject} is waiting${outcome.toolName ? ` on ${outcome.toolName}` : ""} and needs an answer.`;
134
+ const notification = reportNotification({
135
+ sender,
136
+ status: outcome.status,
137
+ message,
138
+ attributes: {
139
+ ...attributes,
140
+ backstop: "true",
141
+ ...outcome.status === "failed" && outcome.failureCode ? { "failure-code": outcome.failureCode } : {},
142
+ ...outcome.status === "blocked" && outcome.toolName ? { "parked-tool": outcome.toolName } : {},
143
+ ...outcome.status === "blocked" && outcome.approval ? { "waiting-for": "approval" } : {}
144
+ }
145
+ });
146
+ const report = (recipient, reportKinds, agentOnly = false) => hasFinalReport(reporting.notifications, {
147
+ senderSessionId: sender.sessionId,
148
+ recipientThreadId: recipient.threadId,
149
+ since,
150
+ kinds: reportKinds,
151
+ agentOnly
152
+ });
153
+ let waiting = outcome.status === "blocked";
154
+ const deliveries = [];
155
+ for (const recipient of recipients) {
156
+ if (await report(recipient, outcome.status === "done" ? [...kinds, "reply"] : kinds, true)) continue;
157
+ if (outcome.status === "done" && await report(recipient, ["blocked"], true)) {
158
+ waiting = true;
159
+ continue;
160
+ }
161
+ if (outcome.status === "failed" && await repeatsLastFailure(reporting.notifications, {
162
+ senderSessionId: sender.sessionId,
163
+ recipientThreadId: recipient.threadId,
164
+ summary: message
165
+ })) continue;
166
+ deliveries.push(await reporting.deliver(recipient, notification));
167
+ }
168
+ return {
169
+ waiting,
170
+ deliveries
171
+ };
172
+ }
173
+ /** The backstop for a work-item run: reports to the item's subscribers. Failures are logged, never thrown. */
174
+ function createWorkItemRunEndReporter({ workItems, reporting }) {
175
+ return async (input) => {
176
+ try {
177
+ const resolved = await reporting();
178
+ if (!resolved) return { waiting: false };
179
+ const recipients = await workItemReportRecipients(workItems, {
180
+ orgId: input.orgId,
181
+ workItemId: input.workItemId,
182
+ senderSessionId: input.sender.sessionId
183
+ });
184
+ if (recipients.length === 0) return { waiting: false };
185
+ const { label, attributes } = await workItemReportAttributes(workItems, input);
186
+ return await sendRunEndReport(resolved, {
187
+ sender: input.sender,
188
+ recipients,
189
+ since: input.since,
190
+ outcome: input.outcome,
191
+ subject: `The ${input.role} session for ${label}`,
192
+ attributes
193
+ });
194
+ } catch (error) {
195
+ console.warn("[Factory reports] Unable to send the run-end report.", error);
196
+ return { waiting: false };
197
+ }
198
+ };
199
+ }
200
+ /**
201
+ * Tells a work item's subscribers it moved, at `low` priority. Only sessions
202
+ * already live hear it: a stage change never brings a session online; an
203
+ * offline subscriber sees the stage in its state signal on its next turn.
204
+ */
205
+ function createStageChangeNotifier({ workItems, reporting }) {
206
+ return async (change) => {
207
+ const subscribers = await workItems.listWorkItemSubscribers({
208
+ orgId: change.orgId,
209
+ workItemId: change.workItemId
210
+ });
211
+ if (subscribers.length === 0) return;
212
+ const resolved = await reporting();
213
+ if (!resolved) return;
214
+ const { label, ref } = await workItemReference(workItems, change);
215
+ const summary = change.fromStage ? `${label} moved from ${change.fromStage} to ${change.toStage}.` : `${label} moved to ${change.toStage}.`;
216
+ const notification = {
217
+ kind: "stage",
218
+ priority: "low",
219
+ summary,
220
+ payload: { message: summary },
221
+ sourceId: change.workItemId,
222
+ coalesceKey: `factory-stage:${change.workItemId}`,
223
+ attributes: {
224
+ "work-item": ref,
225
+ ...change.fromStage ? { "from-stage": change.fromStage } : {},
226
+ "to-stage": change.toStage
227
+ }
228
+ };
229
+ for (const subscriber of subscribers) try {
230
+ await resolved.deliver({
231
+ sessionId: subscriber.sessionId,
232
+ threadId: subscriber.threadId
233
+ }, notification, { onlyIfLive: true });
234
+ } catch (error) {
235
+ console.warn(`[Factory reports] Unable to tell ${subscriber.sessionId} that ${label} moved.`, error);
236
+ }
237
+ };
238
+ }
239
+ /** How much of a gated call's arguments an approval notice quotes. */
240
+ const APPROVAL_ARGS_LENGTH = 3e3;
241
+ /**
242
+ * Who hears from a session: a work session (one with an active run binding)
243
+ * reports to its item's followers, a side session to the session that started
244
+ * it. Any other session (a person's own) has nobody to tell.
245
+ */
246
+ async function sessionAudience({ sessions, workItems }, sessionId, threadId, since) {
247
+ const row = await sessions.getBySessionId(sessionId);
248
+ if (!row?.factoryProjectId) return null;
249
+ let binding = await workItems.findActiveRunBindingForSession({
250
+ orgId: row.orgId,
251
+ factoryProjectId: row.factoryProjectId,
252
+ sessionId: row.sessionId
253
+ });
254
+ if (!binding && since && threadId) for (const resourceId of /* @__PURE__ */ new Set([
255
+ row.factoryProjectId,
256
+ row.sessionId,
257
+ row.userId
258
+ ])) {
259
+ const latest = await workItems.findRunBindingBySession({
260
+ factoryProjectId: row.factoryProjectId,
261
+ threadId,
262
+ resourceId,
263
+ sessionId: row.sessionId
264
+ });
265
+ if (latest?.orgId === row.orgId && latest.revokedAt && latest.revokedAt >= since) {
266
+ binding = latest;
267
+ break;
268
+ }
269
+ }
270
+ if (binding) return {
271
+ kind: "work-item",
272
+ binding,
273
+ sender: {
274
+ sessionId: row.sessionId,
275
+ threadId: binding.threadId,
276
+ userId: row.userId
277
+ }
278
+ };
279
+ if (!row.originSessionId || !row.originThreadId || !threadId) return null;
280
+ return {
281
+ kind: "origin",
282
+ sender: {
283
+ sessionId: row.sessionId,
284
+ threadId,
285
+ userId: row.userId
286
+ },
287
+ recipient: {
288
+ sessionId: row.originSessionId,
289
+ threadId: row.originThreadId
290
+ },
291
+ subject: `The side session "${row.title ?? row.sessionId}"`,
292
+ mutedAt: row.originMutedAt ?? null
293
+ };
294
+ }
295
+ /** Reports each run end of a work or side session to whoever follows it. */
296
+ function createSessionRunEndReporter(deps) {
297
+ const workItemRunEnd = createWorkItemRunEndReporter(deps);
298
+ return async ({ sessionId, threadId, outcome, since }) => {
299
+ const audience = await sessionAudience(deps, sessionId, threadId, since);
300
+ if (!audience) return;
301
+ if (audience.kind === "work-item") {
302
+ const { binding, sender } = audience;
303
+ await workItemRunEnd({
304
+ orgId: binding.orgId,
305
+ factoryProjectId: binding.factoryProjectId,
306
+ workItemId: binding.workItemId,
307
+ role: binding.role,
308
+ sender,
309
+ since,
310
+ outcome
311
+ });
312
+ return;
313
+ }
314
+ if (audience.mutedAt && outcome.status !== "blocked") return;
315
+ const resolved = await deps.reporting();
316
+ if (!resolved) return;
317
+ await sendRunEndReport(resolved, {
318
+ sender: audience.sender,
319
+ recipients: [audience.recipient],
320
+ since,
321
+ outcome,
322
+ subject: audience.subject
323
+ });
324
+ };
325
+ }
326
+ /**
327
+ * Tells whoever follows a session, urgently, the moment its run holds a tool
328
+ * call for approval. The run stays open on the gate, so no run end would say
329
+ * so; the notice names the session and call so a follower can answer it with
330
+ * `factory_answer_approval`.
331
+ */
332
+ function createSessionApprovalNotifier(deps) {
333
+ return async ({ sessionId, threadId, toolName, toolCallId, args }) => {
334
+ const audience = await sessionAudience(deps, sessionId, threadId);
335
+ if (!audience) return;
336
+ const resolved = await deps.reporting();
337
+ if (!resolved) return;
338
+ let subject;
339
+ let recipients;
340
+ let attributes = {};
341
+ if (audience.kind === "work-item") {
342
+ const { binding } = audience;
343
+ recipients = await workItemReportRecipients(deps.workItems, {
344
+ orgId: binding.orgId,
345
+ workItemId: binding.workItemId,
346
+ senderSessionId: audience.sender.sessionId
347
+ });
348
+ const named = await workItemReportAttributes(deps.workItems, binding);
349
+ subject = `The ${binding.role} session for ${named.label}`;
350
+ attributes = named.attributes;
351
+ } else {
352
+ recipients = [audience.recipient];
353
+ subject = audience.subject;
354
+ }
355
+ if (recipients.length === 0) return;
356
+ const argsText = JSON.stringify(args ?? {}) ?? "{}";
357
+ const quoted = argsText.length > APPROVAL_ARGS_LENGTH ? `${argsText.slice(0, APPROVAL_ARGS_LENGTH)}…` : argsText;
358
+ const message = [`${subject} is waiting for approval to run ${toolName} with ${quoted}.`, `Answer it with factory_answer_approval (sessionId "${audience.sender.sessionId}", toolCallId "${toolCallId}"), or open that session to approve or decline it there.`].join(" ");
359
+ const notification = {
360
+ ...reportNotification({
361
+ sender: audience.sender,
362
+ message,
363
+ attributes: {
364
+ ...attributes,
365
+ "waiting-for": "approval",
366
+ "tool-name": toolName,
367
+ "tool-call-id": toolCallId
368
+ }
369
+ }),
370
+ kind: "approval",
371
+ priority: "urgent",
372
+ dedupeKey: `factory-approval:${toolCallId}`
373
+ };
374
+ for (const recipient of recipients) await resolved.deliver(recipient, notification);
375
+ };
376
+ }
377
+ //#endregion
378
+ export { FACTORY_REPORT_FORMS, createSessionApprovalNotifier, createSessionRunEndReporter, createStageChangeNotifier, createWorkItemRunEndReporter, hasFinalReport, repeatsLastFailure, reportNotification, sendRunEndReport, workItemReportAttributes, workItemReportRecipients };
379
+
380
+ //# sourceMappingURL=reports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reports.js","names":[],"sources":["../../src/tools/reports.ts"],"sourcesContent":["/**\n * Reports from sessions doing the work back to whoever follows it: a side\n * session's origin, or every subscriber of a work item. The agent reports with\n * `factory_message_origin`; when a run ends without one, the run-end backstop\n * sends it, so each run produces exactly one final report per recipient.\n */\n\nimport type {\n NotificationPriority,\n NotificationsStorage,\n SendNotificationSignalInput,\n} from '@mastra/core/notifications';\n\nimport type { SourceControlSessionLookup } from '../session/factory-session.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { FACTORY_SESSION_NOTIFICATION_SOURCE } from './deliver.js';\nimport type { FactoryDeliveryResult, FactoryDeliveryTarget } from './deliver.js';\nimport { workItemNumber } from './read-tools.js';\n\nexport type FactoryReportStatus = 'progress' | 'blocked' | 'done' | 'failed';\n\n/** How each report status travels. Final reports never coalesce, so a later report can't overwrite them. */\nexport const FACTORY_REPORT_FORMS: Record<\n FactoryReportStatus | 'report',\n { kind: string; priority: NotificationPriority; coalesce: boolean }\n> = {\n failed: { kind: 'failed', priority: 'urgent', coalesce: false },\n blocked: { kind: 'blocked', priority: 'urgent', coalesce: false },\n done: { kind: 'done', priority: 'high', coalesce: false },\n progress: { kind: 'progress', priority: 'low', coalesce: true },\n // Not `message`: only messages can be answered, so a report can't start a reply loop.\n report: { kind: 'report', priority: 'high', coalesce: false },\n};\n\n/** How many recent `factory-session` notifications on a recipient thread the final-report check reads. */\nconst FINAL_REPORT_SCAN = 100;\n\nexport interface FactoryReportSender {\n sessionId: string;\n threadId: string;\n userId: string;\n}\n\nexport type FactoryReportDeliver = (\n target: FactoryDeliveryTarget,\n notification: Omit<SendNotificationSignalInput, 'source'>,\n options?: { onlyIfLive?: boolean },\n) => Promise<FactoryDeliveryResult>;\n\nexport interface FactoryReporting {\n deliver: FactoryReportDeliver;\n notifications: Pick<NotificationsStorage, 'listNotifications'>;\n}\n\n/**\n * Whether `senderSessionId` already put a report of one of `kinds` on the\n * recipient thread since `since`. Reads the durable notifications store, so it\n * holds across restarts. `listNotifications` has no time filter: records come\n * newest-updated first, and the scan stops at the first one last updated\n * before `since` (nothing after it can have been created later).\n */\nexport async function hasFinalReport(\n notifications: FactoryReporting['notifications'],\n {\n senderSessionId,\n recipientThreadId,\n since,\n kinds,\n agentOnly = false,\n }: {\n senderSessionId: string;\n recipientThreadId: string;\n since: Date;\n kinds: readonly string[];\n /** Ignore reports Factory sent on the session's behalf (the backstop). */\n agentOnly?: boolean;\n },\n): Promise<boolean> {\n const records = await notifications.listNotifications({\n threadId: recipientThreadId,\n source: FACTORY_SESSION_NOTIFICATION_SOURCE,\n limit: FINAL_REPORT_SCAN,\n });\n for (const record of records) {\n if (record.updatedAt < since) break;\n if (\n record.attributes?.['from-session'] === senderSessionId &&\n !(agentOnly && record.attributes?.backstop === 'true') &&\n // A reply carrying a final status counts like the report itself.\n (kinds.includes(record.kind) ||\n (record.kind === 'reply' &&\n typeof record.attributes?.status === 'string' &&\n kinds.includes(record.attributes.status))) &&\n record.createdAt >= since\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Whether the newest report `senderSessionId` put on the recipient thread is\n * already this exact `failed` backstop. A session stuck failing the same way\n * (a run that dies before the model, restarted over and over) then reports\n * once, not once per attempt; any other report in between resets it.\n */\nexport async function repeatsLastFailure(\n notifications: FactoryReporting['notifications'],\n {\n senderSessionId,\n recipientThreadId,\n summary,\n }: { senderSessionId: string; recipientThreadId: string; summary: string },\n): Promise<boolean> {\n const records = await notifications.listNotifications({\n threadId: recipientThreadId,\n source: FACTORY_SESSION_NOTIFICATION_SOURCE,\n limit: FINAL_REPORT_SCAN,\n });\n // Storage orders by `updatedAt`, which moves when a report is read; the\n // newest report is the one created last.\n const last = records\n .filter(record => record.attributes?.['from-session'] === senderSessionId)\n .reduce<(typeof records)[number] | undefined>(\n (newest, record) => (!newest || record.createdAt > newest.createdAt ? record : newest),\n undefined,\n );\n return last?.kind === 'failed' && last.attributes?.backstop === 'true' && last.summary === summary;\n}\n\nexport function reportNotification({\n sender,\n status,\n message,\n attributes = {},\n}: {\n sender: FactoryReportSender;\n status?: FactoryReportStatus;\n message: string;\n attributes?: Record<string, string>;\n}): Omit<SendNotificationSignalInput, 'source'> {\n const form = FACTORY_REPORT_FORMS[status ?? 'report'];\n return {\n kind: form.kind,\n priority: form.priority,\n // The whole text: a notification's summary is all the recipient's agent ever sees, even\n // through notification_inbox (which hides payloads), so a cut-off report loses its verdict.\n summary: message,\n payload: { message },\n sourceId: sender.sessionId,\n ...(form.coalesce ? { coalesceKey: `factory-session:${sender.sessionId}:${form.kind}` } : {}),\n attributes: {\n ...attributes,\n ...(status ? { status } : {}),\n // Last, so nothing above can claim another sender.\n 'from-session': sender.sessionId,\n 'from-thread': sender.threadId,\n 'from-user': sender.userId,\n },\n };\n}\n\n/** Everyone following a work item, except the session doing the reporting. */\nexport async function workItemReportRecipients(\n workItems: Pick<WorkItemsStorage, 'listWorkItemSubscribers'>,\n { orgId, workItemId, senderSessionId }: { orgId: string; workItemId: string; senderSessionId: string },\n): Promise<FactoryDeliveryTarget[]> {\n const subscribers = await workItems.listWorkItemSubscribers({ orgId, workItemId });\n return subscribers\n .filter(subscriber => subscriber.sessionId !== senderSessionId)\n .map(subscriber => ({ sessionId: subscriber.sessionId, threadId: subscriber.threadId }));\n}\n\n/** How reports name an item: `#n`, else its quoted title, else its id. */\nasync function workItemReference(\n workItems: Pick<WorkItemsStorage, 'getForProject'>,\n { orgId, factoryProjectId, workItemId }: { orgId: string; factoryProjectId: string; workItemId: string },\n): Promise<{ label: string; ref: string }> {\n const item = await workItems.getForProject(orgId, factoryProjectId, workItemId);\n const number = item ? workItemNumber(item) : null;\n if (number !== null) return { label: `#${number}`, ref: `#${number}` };\n return { label: item ? `\"${item.title}\"` : workItemId, ref: workItemId };\n}\n\n/** Attributes that say which item and role a worker's report is about. */\nexport async function workItemReportAttributes(\n workItems: Pick<WorkItemsStorage, 'getForProject'>,\n {\n orgId,\n factoryProjectId,\n workItemId,\n role,\n }: { orgId: string; factoryProjectId: string; workItemId: string; role: string },\n): Promise<{ label: string; attributes: Record<string, string> }> {\n const { label, ref } = await workItemReference(workItems, { orgId, factoryProjectId, workItemId });\n return { label, attributes: { 'work-item': ref, role } };\n}\n\nexport type FactoryRunOutcome =\n | { status: 'done' }\n | { status: 'failed'; reason: string; failureCode?: string }\n | { status: 'blocked'; toolName?: string; approval?: boolean };\n\n/** `waiting`: the session is waiting for an answer, so a later run owes the final report. */\nexport interface FactoryRunEndResult {\n waiting: boolean;\n deliveries?: FactoryDeliveryResult[];\n}\n\n/**\n * The run-end backstop: send `outcome` to each recipient that hasn't had a\n * final report from this sender since the run started. An agent's own `done`\n * or `failed` report wins. A run that completes after the agent's own\n * `blocked` sends nothing: the agent ended its turn to wait for an answer, and\n * its `done` comes from a later run. A `failed` backstop still goes out after an\n * earlier `blocked`.\n */\nexport async function sendRunEndReport(\n reporting: FactoryReporting,\n {\n sender,\n recipients,\n since,\n outcome,\n subject,\n attributes = {},\n }: {\n sender: FactoryReportSender;\n recipients: FactoryDeliveryTarget[];\n since: Date;\n outcome: FactoryRunOutcome;\n /** Who finished, e.g. \"The work session for #12\". */\n subject: string;\n attributes?: Record<string, string>;\n },\n): Promise<FactoryRunEndResult> {\n const kinds = outcome.status === 'blocked' ? ['blocked'] : ['done', 'failed'];\n const message =\n outcome.status === 'done'\n ? `${subject} finished.`\n : outcome.status === 'failed'\n ? `${subject} failed${outcome.failureCode ? ` (${outcome.failureCode})` : ''}: ${outcome.reason}`\n : outcome.approval\n ? `${subject} is waiting for someone to approve ${outcome.toolName ?? 'a tool call'} in that session.`\n : `${subject} is waiting${outcome.toolName ? ` on ${outcome.toolName}` : ''} and needs an answer.`;\n const notification = reportNotification({\n sender,\n status: outcome.status,\n message,\n attributes: {\n ...attributes,\n backstop: 'true',\n ...(outcome.status === 'failed' && outcome.failureCode ? { 'failure-code': outcome.failureCode } : {}),\n ...(outcome.status === 'blocked' && outcome.toolName ? { 'parked-tool': outcome.toolName } : {}),\n ...(outcome.status === 'blocked' && outcome.approval ? { 'waiting-for': 'approval' } : {}),\n },\n });\n const report = (recipient: FactoryDeliveryTarget, reportKinds: string[], agentOnly = false) =>\n hasFinalReport(reporting.notifications, {\n senderSessionId: sender.sessionId,\n recipientThreadId: recipient.threadId,\n since,\n kinds: reportKinds,\n agentOnly,\n });\n let waiting = outcome.status === 'blocked';\n const deliveries: FactoryDeliveryResult[] = [];\n for (const recipient of recipients) {\n // Only the agent's own reports count: an earlier run's backstop lands after this\n // run's window opened. A reply already told that follower how a finished run went.\n if (await report(recipient, outcome.status === 'done' ? [...kinds, 'reply'] : kinds, true)) continue;\n // Only the agent's own `blocked` means it ended its turn to wait; a backstop `blocked` from a parked run doesn't.\n if (outcome.status === 'done' && (await report(recipient, ['blocked'], true))) {\n waiting = true;\n continue;\n }\n if (\n outcome.status === 'failed' &&\n (await repeatsLastFailure(reporting.notifications, {\n senderSessionId: sender.sessionId,\n recipientThreadId: recipient.threadId,\n summary: message,\n }))\n ) {\n continue;\n }\n deliveries.push(await reporting.deliver(recipient, notification));\n }\n return { waiting, deliveries };\n}\n\nexport interface FactoryRunEndInput {\n orgId: string;\n factoryProjectId: string;\n workItemId: string;\n role: string;\n sender: FactoryReportSender;\n since: Date;\n outcome: FactoryRunOutcome;\n}\n\nexport type FactoryRunEndReporter = (input: FactoryRunEndInput) => Promise<FactoryRunEndResult>;\n\n/** The backstop for a work-item run: reports to the item's subscribers. Failures are logged, never thrown. */\nexport function createWorkItemRunEndReporter({\n workItems,\n reporting,\n}: {\n workItems: Pick<WorkItemsStorage, 'listWorkItemSubscribers' | 'getForProject'>;\n /** Resolved per report: the controller exists only once the mount is up. */\n reporting: () => Promise<FactoryReporting | null>;\n}): FactoryRunEndReporter {\n return async input => {\n try {\n const resolved = await reporting();\n if (!resolved) return { waiting: false };\n const recipients = await workItemReportRecipients(workItems, {\n orgId: input.orgId,\n workItemId: input.workItemId,\n senderSessionId: input.sender.sessionId,\n });\n if (recipients.length === 0) return { waiting: false };\n const { label, attributes } = await workItemReportAttributes(workItems, input);\n return await sendRunEndReport(resolved, {\n sender: input.sender,\n recipients,\n since: input.since,\n outcome: input.outcome,\n subject: `The ${input.role} session for ${label}`,\n attributes,\n });\n } catch (error) {\n console.warn('[Factory reports] Unable to send the run-end report.', error);\n return { waiting: false };\n }\n };\n}\n\nexport interface FactoryStageChange {\n orgId: string;\n factoryProjectId: string;\n workItemId: string;\n fromStage: string | undefined;\n toStage: string;\n}\n\n/**\n * Tells a work item's subscribers it moved, at `low` priority. Only sessions\n * already live hear it: a stage change never brings a session online; an\n * offline subscriber sees the stage in its state signal on its next turn.\n */\nexport function createStageChangeNotifier({\n workItems,\n reporting,\n}: {\n workItems: Pick<WorkItemsStorage, 'listWorkItemSubscribers' | 'getForProject'>;\n reporting: () => Promise<FactoryReporting | null>;\n}): (change: FactoryStageChange) => Promise<void> {\n return async change => {\n const subscribers = await workItems.listWorkItemSubscribers({\n orgId: change.orgId,\n workItemId: change.workItemId,\n });\n if (subscribers.length === 0) return;\n const resolved = await reporting();\n if (!resolved) return;\n const { label, ref } = await workItemReference(workItems, change);\n const summary = change.fromStage\n ? `${label} moved from ${change.fromStage} to ${change.toStage}.`\n : `${label} moved to ${change.toStage}.`;\n const notification: Omit<SendNotificationSignalInput, 'source'> = {\n kind: 'stage',\n priority: 'low',\n summary,\n payload: { message: summary },\n sourceId: change.workItemId,\n coalesceKey: `factory-stage:${change.workItemId}`,\n attributes: {\n 'work-item': ref,\n ...(change.fromStage ? { 'from-stage': change.fromStage } : {}),\n 'to-stage': change.toStage,\n },\n };\n for (const subscriber of subscribers) {\n try {\n await resolved.deliver({ sessionId: subscriber.sessionId, threadId: subscriber.threadId }, notification, {\n onlyIfLive: true,\n });\n } catch (error) {\n console.warn(`[Factory reports] Unable to tell ${subscriber.sessionId} that ${label} moved.`, error);\n }\n }\n };\n}\n\n/** Reports one run end of a session; the thread is the one the run used. */\nexport type FactorySessionRunEndReporter = (input: {\n sessionId: string;\n threadId: string | null;\n outcome: FactoryRunOutcome;\n since: Date;\n}) => Promise<void>;\n\n/** A tool call a session's run is holding until someone approves or declines it. */\nexport interface FactoryApprovalRequest {\n sessionId: string;\n threadId: string | null;\n toolName: string;\n toolCallId: string;\n args: unknown;\n}\n\nexport type FactoryApprovalNotifier = (request: FactoryApprovalRequest) => Promise<void>;\n\ninterface SessionReportDependencies {\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n workItems: Pick<\n WorkItemsStorage,\n 'findActiveRunBindingForSession' | 'findRunBindingBySession' | 'listWorkItemSubscribers' | 'getForProject'\n >;\n reporting: () => Promise<FactoryReporting | null>;\n}\n\n/** How much of a gated call's arguments an approval notice quotes. */\nconst APPROVAL_ARGS_LENGTH = 3000;\n\n/**\n * Who hears from a session: a work session (one with an active run binding)\n * reports to its item's followers, a side session to the session that started\n * it. Any other session (a person's own) has nobody to tell.\n */\nasync function sessionAudience(\n { sessions, workItems }: Omit<SessionReportDependencies, 'reporting'>,\n sessionId: string,\n threadId: string | null,\n /** When the run started: a binding revoked since then (the run moved its card to a finished stage) still counts. */\n since?: Date,\n): Promise<\n | {\n kind: 'work-item';\n binding: NonNullable<Awaited<ReturnType<WorkItemsStorage['findActiveRunBindingForSession']>>>;\n sender: FactoryReportSender;\n }\n | {\n kind: 'origin';\n sender: FactoryReportSender;\n recipient: FactoryDeliveryTarget;\n subject: string;\n /** Set while the origin has muted this side session's reports (approval notices still go through). */\n mutedAt: Date | null;\n }\n | null\n> {\n const row = await sessions.getBySessionId(sessionId);\n if (!row?.factoryProjectId) return null;\n let binding = await workItems.findActiveRunBindingForSession({\n orgId: row.orgId,\n factoryProjectId: row.factoryProjectId,\n sessionId: row.sessionId,\n });\n if (!binding && since && threadId) {\n // The binding records the memory resource the run used: the Factory's, its\n // own id for a run kept on its own resource, or its owner's for a person's\n // session the run started in.\n for (const resourceId of new Set([row.factoryProjectId, row.sessionId, row.userId])) {\n const latest = await workItems.findRunBindingBySession({\n factoryProjectId: row.factoryProjectId,\n threadId,\n resourceId,\n sessionId: row.sessionId,\n });\n if (latest?.orgId === row.orgId && latest.revokedAt && latest.revokedAt >= since) {\n binding = latest;\n break;\n }\n }\n }\n if (binding) {\n return {\n kind: 'work-item',\n binding,\n sender: { sessionId: row.sessionId, threadId: binding.threadId, userId: row.userId },\n };\n }\n if (!row.originSessionId || !row.originThreadId || !threadId) return null;\n return {\n kind: 'origin',\n sender: { sessionId: row.sessionId, threadId, userId: row.userId },\n recipient: { sessionId: row.originSessionId, threadId: row.originThreadId },\n subject: `The side session \"${row.title ?? row.sessionId}\"`,\n mutedAt: row.originMutedAt ?? null,\n };\n}\n\n/** Reports each run end of a work or side session to whoever follows it. */\nexport function createSessionRunEndReporter(deps: SessionReportDependencies): FactorySessionRunEndReporter {\n const workItemRunEnd = createWorkItemRunEndReporter(deps);\n return async ({ sessionId, threadId, outcome, since }) => {\n const audience = await sessionAudience(deps, sessionId, threadId, since);\n if (!audience) return;\n if (audience.kind === 'work-item') {\n const { binding, sender } = audience;\n await workItemRunEnd({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: binding.workItemId,\n role: binding.role,\n sender,\n since,\n outcome,\n });\n return;\n }\n // A muted origin still hears when the session is blocked: nothing else would tell it the session is waiting.\n if (audience.mutedAt && outcome.status !== 'blocked') return;\n const resolved = await deps.reporting();\n if (!resolved) return;\n await sendRunEndReport(resolved, {\n sender: audience.sender,\n recipients: [audience.recipient],\n since,\n outcome,\n subject: audience.subject,\n });\n };\n}\n\n/**\n * Tells whoever follows a session, urgently, the moment its run holds a tool\n * call for approval. The run stays open on the gate, so no run end would say\n * so; the notice names the session and call so a follower can answer it with\n * `factory_answer_approval`.\n */\nexport function createSessionApprovalNotifier(deps: SessionReportDependencies): FactoryApprovalNotifier {\n return async ({ sessionId, threadId, toolName, toolCallId, args }) => {\n const audience = await sessionAudience(deps, sessionId, threadId);\n if (!audience) return;\n const resolved = await deps.reporting();\n if (!resolved) return;\n let subject: string;\n let recipients: FactoryDeliveryTarget[];\n let attributes: Record<string, string> = {};\n if (audience.kind === 'work-item') {\n const { binding } = audience;\n recipients = await workItemReportRecipients(deps.workItems, {\n orgId: binding.orgId,\n workItemId: binding.workItemId,\n senderSessionId: audience.sender.sessionId,\n });\n const named = await workItemReportAttributes(deps.workItems, binding);\n subject = `The ${binding.role} session for ${named.label}`;\n attributes = named.attributes;\n } else {\n recipients = [audience.recipient];\n subject = audience.subject;\n }\n if (recipients.length === 0) return;\n const argsText = JSON.stringify(args ?? {}) ?? '{}';\n const quoted = argsText.length > APPROVAL_ARGS_LENGTH ? `${argsText.slice(0, APPROVAL_ARGS_LENGTH)}…` : argsText;\n const message = [\n `${subject} is waiting for approval to run ${toolName} with ${quoted}.`,\n `Answer it with factory_answer_approval (sessionId \"${audience.sender.sessionId}\", toolCallId \"${toolCallId}\"), or open that session to approve or decline it there.`,\n ].join(' ');\n const notification = {\n ...reportNotification({\n sender: audience.sender,\n message,\n attributes: { ...attributes, 'waiting-for': 'approval', 'tool-name': toolName, 'tool-call-id': toolCallId },\n }),\n kind: 'approval',\n priority: 'urgent' as const,\n // One notice per held call, even if the gate is announced again.\n dedupeKey: `factory-approval:${toolCallId}`,\n };\n for (const recipient of recipients) await resolved.deliver(recipient, notification);\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { AgentControllerEvent } from '@mastra/core/agent-controller';
2
+ import type { RunEndCaptureSession } from '../session/run-audit.js';
3
+ import type { FactoryRunOutcome } from './reports.js';
4
+ type RunEndReason = Extract<AgentControllerEvent, {
5
+ type: 'agent_end';
6
+ }>['reason'];
7
+ /** What a run stopped on: a tool waiting for an answer, or one waiting for approval. */
8
+ export interface ParkedTool {
9
+ toolName: string;
10
+ toolCallId: string;
11
+ approval: boolean;
12
+ }
13
+ export interface RunReportSession {
14
+ subscribe(listener: (event: AgentControllerEvent) => void): () => void;
15
+ }
16
+ /** Reports one run's end; `since` is when the run's window opened. Should not throw. */
17
+ export type SessionRunEndReport = (outcome: FactoryRunOutcome, since: Date) => Promise<void>;
18
+ /** Told when the run holds a tool call for approval; the run stays open meanwhile. */
19
+ export type SessionApprovalReport = (request: {
20
+ toolName: string;
21
+ toolCallId: string;
22
+ args: unknown;
23
+ }) => Promise<void>;
24
+ /**
25
+ * Report every run end of a session, however the run started: the board, a
26
+ * typed message, a wake, or a run after a restart. Attached to each Factory
27
+ * session when it is created, so it outlives any one start. A run someone
28
+ * stopped or steered says nothing, and its window carries into the next run,
29
+ * so reports the agent made in it still count.
30
+ */
31
+ export declare function observeSessionRunReports(session: RunReportSession, report: SessionRunEndReport, { onApprovalRequested }?: {
32
+ onApprovalRequested?: SessionApprovalReport;
33
+ }): () => void;
34
+ /**
35
+ * Renew a kicked-off run's open-run entry until the run ends, like the
36
+ * dispatcher's heartbeat, so no dispatcher starts the role again meanwhile.
37
+ * `endReason` is how that run ended, once it has; `stop` is for a kickoff that fails.
38
+ */
39
+ export declare function heartbeatOpenRunUntilEnd(session: RunEndCaptureSession, kickoffId: string): {
40
+ endReason: () => RunEndReason;
41
+ stop: () => void;
42
+ };
43
+ /** `null` for an aborted run: someone stopped or steered it, which is not an outcome. */
44
+ export declare function runOutcome(reason: RunEndReason, parked: Pick<ParkedTool, 'toolName' | 'approval'> | undefined, errorMessage?: string): FactoryRunOutcome | null;
45
+ export {};
46
+ //# sourceMappingURL=run-watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-watch.d.ts","sourceRoot":"","sources":["../../src/tools/run-watch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAG1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,KAAK,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEnF,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,wFAAwF;AACxF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE7F,sFAAsF;AACtF,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAKxH;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,mBAAmB,EAC3B,EAAE,mBAAmB,EAAE,GAAE;IAAE,mBAAmB,CAAC,EAAE,qBAAqB,CAAA;CAAO,GAC5E,MAAM,IAAI,CAwCZ;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,GAChB;IAAE,SAAS,EAAE,MAAM,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CAqBrD;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,SAAS,EAC7D,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,GAAG,IAAI,CAqB1B"}
@@ -0,0 +1,102 @@
1
+ import { FACTORY_OPEN_RUN_STALE_MS, heartbeatSessionOpenRun } from "../session/run-audit.js";
2
+ //#region src/tools/run-watch.ts
3
+ /** How much of a run's error message a `failed` report quotes. */
4
+ const ERROR_MESSAGE_LENGTH = 400;
5
+ /**
6
+ * Report every run end of a session, however the run started: the board, a
7
+ * typed message, a wake, or a run after a restart. Attached to each Factory
8
+ * session when it is created, so it outlives any one start. A run someone
9
+ * stopped or steered says nothing, and its window carries into the next run,
10
+ * so reports the agent made in it still count.
11
+ */
12
+ function observeSessionRunReports(session, report, { onApprovalRequested } = {}) {
13
+ let parked;
14
+ let lastError;
15
+ let since = /* @__PURE__ */ new Date();
16
+ const settle = (work) => void work.catch((error) => {
17
+ console.warn("[Factory reports] Unable to report on a session run.", error);
18
+ });
19
+ return session.subscribe((event) => {
20
+ if (event.type === "tool_suspended" || event.type === "tool_approval_required") {
21
+ parked = {
22
+ toolName: event.toolName,
23
+ toolCallId: event.toolCallId,
24
+ approval: event.type === "tool_approval_required"
25
+ };
26
+ if (event.type === "tool_approval_required" && onApprovalRequested) settle(onApprovalRequested({
27
+ toolName: event.toolName,
28
+ toolCallId: event.toolCallId,
29
+ args: event.args
30
+ }));
31
+ return;
32
+ }
33
+ if (event.type === "error") {
34
+ lastError = event.error?.message;
35
+ return;
36
+ }
37
+ if (event.type === "tool_suspension_cancelled" && parked?.toolCallId === event.toolCallId) {
38
+ parked = void 0;
39
+ return;
40
+ }
41
+ if (event.type !== "agent_end") return;
42
+ const outcome = runOutcome(event.reason, parked, lastError);
43
+ parked = void 0;
44
+ lastError = void 0;
45
+ if (!outcome) return;
46
+ const runSince = since;
47
+ since = new Date(Date.now() + 1);
48
+ settle(report(outcome, runSince));
49
+ });
50
+ }
51
+ /**
52
+ * Renew a kicked-off run's open-run entry until the run ends, like the
53
+ * dispatcher's heartbeat, so no dispatcher starts the role again meanwhile.
54
+ * `endReason` is how that run ended, once it has; `stop` is for a kickoff that fails.
55
+ */
56
+ function heartbeatOpenRunUntilEnd(session, kickoffId) {
57
+ let endReason;
58
+ let unsubscribe = () => {};
59
+ const timer = setInterval(() => void heartbeatSessionOpenRun(session, kickoffId), Math.floor(FACTORY_OPEN_RUN_STALE_MS / 3));
60
+ timer.unref?.();
61
+ let stopped = false;
62
+ const stop = () => {
63
+ if (stopped) return;
64
+ stopped = true;
65
+ clearInterval(timer);
66
+ unsubscribe();
67
+ };
68
+ unsubscribe = session.subscribe((event) => {
69
+ if (event.type !== "agent_end") return;
70
+ endReason ??= event.reason;
71
+ stop();
72
+ });
73
+ return {
74
+ endReason: () => endReason,
75
+ stop
76
+ };
77
+ }
78
+ /** `null` for an aborted run: someone stopped or steered it, which is not an outcome. */
79
+ function runOutcome(reason, parked, errorMessage) {
80
+ switch (reason) {
81
+ case "error": {
82
+ const message = errorMessage?.trim();
83
+ return {
84
+ status: "failed",
85
+ reason: message ? `the run ended in error: ${message.slice(0, ERROR_MESSAGE_LENGTH)}` : "the run ended in error."
86
+ };
87
+ }
88
+ case "aborted": return null;
89
+ case "suspended": return {
90
+ status: "blocked",
91
+ ...parked ? {
92
+ toolName: parked.toolName,
93
+ ...parked.approval ? { approval: true } : {}
94
+ } : {}
95
+ };
96
+ default: return { status: "done" };
97
+ }
98
+ }
99
+ //#endregion
100
+ export { heartbeatOpenRunUntilEnd, observeSessionRunReports, runOutcome };
101
+
102
+ //# sourceMappingURL=run-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-watch.js","names":[],"sources":["../../src/tools/run-watch.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\nimport { FACTORY_OPEN_RUN_STALE_MS, heartbeatSessionOpenRun } from '../session/run-audit.js';\nimport type { RunEndCaptureSession } from '../session/run-audit.js';\nimport type { FactoryRunOutcome } from './reports.js';\n\ntype RunEndReason = Extract<AgentControllerEvent, { type: 'agent_end' }>['reason'];\n\n/** What a run stopped on: a tool waiting for an answer, or one waiting for approval. */\nexport interface ParkedTool {\n toolName: string;\n toolCallId: string;\n approval: boolean;\n}\n\nexport interface RunReportSession {\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\n/** Reports one run's end; `since` is when the run's window opened. Should not throw. */\nexport type SessionRunEndReport = (outcome: FactoryRunOutcome, since: Date) => Promise<void>;\n\n/** Told when the run holds a tool call for approval; the run stays open meanwhile. */\nexport type SessionApprovalReport = (request: { toolName: string; toolCallId: string; args: unknown }) => Promise<void>;\n\n/** How much of a run's error message a `failed` report quotes. */\nconst ERROR_MESSAGE_LENGTH = 400;\n\n/**\n * Report every run end of a session, however the run started: the board, a\n * typed message, a wake, or a run after a restart. Attached to each Factory\n * session when it is created, so it outlives any one start. A run someone\n * stopped or steered says nothing, and its window carries into the next run,\n * so reports the agent made in it still count.\n */\nexport function observeSessionRunReports(\n session: RunReportSession,\n report: SessionRunEndReport,\n { onApprovalRequested }: { onApprovalRequested?: SessionApprovalReport } = {},\n): () => void {\n let parked: ParkedTool | undefined;\n let lastError: string | undefined;\n let since = new Date();\n const settle = (work: Promise<void>) =>\n void work.catch(error => {\n console.warn('[Factory reports] Unable to report on a session run.', error);\n });\n return session.subscribe(event => {\n if (event.type === 'tool_suspended' || event.type === 'tool_approval_required') {\n parked = {\n toolName: event.toolName,\n toolCallId: event.toolCallId,\n approval: event.type === 'tool_approval_required',\n };\n // An approval gate holds the run open, so it is announced now, not at the run's end.\n if (event.type === 'tool_approval_required' && onApprovalRequested) {\n settle(onApprovalRequested({ toolName: event.toolName, toolCallId: event.toolCallId, args: event.args }));\n }\n return;\n }\n if (event.type === 'error') {\n lastError = event.error?.message;\n return;\n }\n if (event.type === 'tool_suspension_cancelled' && parked?.toolCallId === event.toolCallId) {\n parked = undefined;\n return;\n }\n if (event.type !== 'agent_end') return;\n const outcome = runOutcome(event.reason, parked, lastError);\n parked = undefined;\n lastError = undefined;\n if (!outcome) return;\n // Move the window now, so a run that starts before this report settles is judged on its own;\n // reports made up to the end, including its last millisecond, belong to the run that ended.\n const runSince = since;\n since = new Date(Date.now() + 1);\n settle(report(outcome, runSince));\n });\n}\n\n/**\n * Renew a kicked-off run's open-run entry until the run ends, like the\n * dispatcher's heartbeat, so no dispatcher starts the role again meanwhile.\n * `endReason` is how that run ended, once it has; `stop` is for a kickoff that fails.\n */\nexport function heartbeatOpenRunUntilEnd(\n session: RunEndCaptureSession,\n kickoffId: string,\n): { endReason: () => RunEndReason; stop: () => void } {\n let endReason: RunEndReason;\n let unsubscribe = () => {};\n const timer = setInterval(\n () => void heartbeatSessionOpenRun(session, kickoffId),\n Math.floor(FACTORY_OPEN_RUN_STALE_MS / 3),\n );\n timer.unref?.();\n let stopped = false;\n const stop = () => {\n if (stopped) return;\n stopped = true;\n clearInterval(timer);\n unsubscribe();\n };\n unsubscribe = session.subscribe(event => {\n if (event.type !== 'agent_end') return;\n endReason ??= event.reason;\n stop();\n });\n return { endReason: () => endReason, stop };\n}\n\n/** `null` for an aborted run: someone stopped or steered it, which is not an outcome. */\nexport function runOutcome(\n reason: RunEndReason,\n parked: Pick<ParkedTool, 'toolName' | 'approval'> | undefined,\n errorMessage?: string,\n): FactoryRunOutcome | null {\n switch (reason) {\n case 'error': {\n const message = errorMessage?.trim();\n return {\n status: 'failed',\n reason: message\n ? `the run ended in error: ${message.slice(0, ERROR_MESSAGE_LENGTH)}`\n : 'the run ended in error.',\n };\n }\n case 'aborted':\n return null;\n case 'suspended':\n return {\n status: 'blocked',\n ...(parked ? { toolName: parked.toolName, ...(parked.approval ? { approval: true } : {}) } : {}),\n };\n default:\n return { status: 'done' };\n }\n}\n"],"mappings":";;;AA0BA,MAAM,uBAAuB;;;;;;;;AAS7B,SAAgB,yBACd,SACA,QACA,EAAE,wBAAyE,CAAC,GAChE;CACZ,IAAI;CACJ,IAAI;CACJ,IAAI,wBAAQ,IAAI,KAAK;CACrB,MAAM,UAAU,SACd,KAAK,KAAK,OAAM,UAAS;EACvB,QAAQ,KAAK,wDAAwD,KAAK;CAC5E,CAAC;CACH,OAAO,QAAQ,WAAU,UAAS;EAChC,IAAI,MAAM,SAAS,oBAAoB,MAAM,SAAS,0BAA0B;GAC9E,SAAS;IACP,UAAU,MAAM;IAChB,YAAY,MAAM;IAClB,UAAU,MAAM,SAAS;GAC3B;GAEA,IAAI,MAAM,SAAS,4BAA4B,qBAC7C,OAAO,oBAAoB;IAAE,UAAU,MAAM;IAAU,YAAY,MAAM;IAAY,MAAM,MAAM;GAAK,CAAC,CAAC;GAE1G;EACF;EACA,IAAI,MAAM,SAAS,SAAS;GAC1B,YAAY,MAAM,OAAO;GACzB;EACF;EACA,IAAI,MAAM,SAAS,+BAA+B,QAAQ,eAAe,MAAM,YAAY;GACzF,SAAS,KAAA;GACT;EACF;EACA,IAAI,MAAM,SAAS,aAAa;EAChC,MAAM,UAAU,WAAW,MAAM,QAAQ,QAAQ,SAAS;EAC1D,SAAS,KAAA;EACT,YAAY,KAAA;EACZ,IAAI,CAAC,SAAS;EAGd,MAAM,WAAW;EACjB,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC;EAC/B,OAAO,OAAO,SAAS,QAAQ,CAAC;CAClC,CAAC;AACH;;;;;;AAOA,SAAgB,yBACd,SACA,WACqD;CACrD,IAAI;CACJ,IAAI,oBAAoB,CAAC;CACzB,MAAM,QAAQ,kBACN,KAAK,wBAAwB,SAAS,SAAS,GACrD,KAAK,MAAM,4BAA4B,CAAC,CAC1C;CACA,MAAM,QAAQ;CACd,IAAI,UAAU;CACd,MAAM,aAAa;EACjB,IAAI,SAAS;EACb,UAAU;EACV,cAAc,KAAK;EACnB,YAAY;CACd;CACA,cAAc,QAAQ,WAAU,UAAS;EACvC,IAAI,MAAM,SAAS,aAAa;EAChC,cAAc,MAAM;EACpB,KAAK;CACP,CAAC;CACD,OAAO;EAAE,iBAAiB;EAAW;CAAK;AAC5C;;AAGA,SAAgB,WACd,QACA,QACA,cAC0B;CAC1B,QAAQ,QAAR;EACE,KAAK,SAAS;GACZ,MAAM,UAAU,cAAc,KAAK;GACnC,OAAO;IACL,QAAQ;IACR,QAAQ,UACJ,2BAA2B,QAAQ,MAAM,GAAG,oBAAoB,MAChE;GACN;EACF;EACA,KAAK,WACH,OAAO;EACT,KAAK,aACH,OAAO;GACL,QAAQ;GACR,GAAI,SAAS;IAAE,UAAU,OAAO;IAAU,GAAI,OAAO,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GAAG,IAAI,CAAC;EAChG;EACF,SACE,OAAO,EAAE,QAAQ,OAAO;CAC5B;AACF"}