@mastra/factory 0.24.0-alpha.1 → 0.24.0-alpha.11

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 (389) 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 +355 -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 +76 -29
  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/intake.d.ts +1 -1
  77. package/dist/integrations/incidentio/intake.d.ts.map +1 -1
  78. package/dist/integrations/incidentio/intake.js.map +1 -1
  79. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  80. package/dist/integrations/linear/agent-tools.js.map +1 -1
  81. package/dist/integrations/platform/connect/routes.d.ts +1 -1
  82. package/dist/integrations/platform/connect/routes.d.ts.map +1 -1
  83. package/dist/integrations/platform/connect/routes.js.map +1 -1
  84. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  85. package/dist/integrations/platform/github/integration.js +27 -1
  86. package/dist/integrations/platform/github/integration.js.map +1 -1
  87. package/dist/integrations/platform/incidentio/integration.d.ts +1 -1
  88. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  89. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  90. package/dist/integrations/platform/jira/integration.d.ts +1 -1
  91. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  92. package/dist/integrations/platform/jira/integration.js.map +1 -1
  93. package/dist/integrations/platform/linear/event-worker.d.ts +1 -1
  94. package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
  95. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  96. package/dist/integrations/slack/integration.d.ts.map +1 -1
  97. package/dist/integrations/slack/integration.js +33 -6
  98. package/dist/integrations/slack/integration.js.map +1 -1
  99. package/dist/integrations/slack/slack.d.ts +16 -5
  100. package/dist/integrations/slack/slack.d.ts.map +1 -1
  101. package/dist/integrations/slack/slack.js +137 -64
  102. package/dist/integrations/slack/slack.js.map +1 -1
  103. package/dist/integrations/subscription-session.d.ts +14 -0
  104. package/dist/integrations/subscription-session.d.ts.map +1 -1
  105. package/dist/integrations/subscription-session.js +30 -16
  106. package/dist/integrations/subscription-session.js.map +1 -1
  107. package/dist/mcp/disconnect.d.ts +10 -0
  108. package/dist/mcp/disconnect.d.ts.map +1 -0
  109. package/dist/mcp/disconnect.js +44 -0
  110. package/dist/mcp/disconnect.js.map +1 -0
  111. package/dist/mcp/routes.d.ts +46 -0
  112. package/dist/mcp/routes.d.ts.map +1 -0
  113. package/dist/mcp/routes.js +243 -0
  114. package/dist/mcp/routes.js.map +1 -0
  115. package/dist/provider-display-name.d.ts +2 -0
  116. package/dist/provider-display-name.d.ts.map +1 -0
  117. package/dist/provider-display-name.js +16 -0
  118. package/dist/provider-display-name.js.map +1 -0
  119. package/dist/review-verdict.d.ts +11 -0
  120. package/dist/review-verdict.d.ts.map +1 -1
  121. package/dist/review-verdict.js +16 -1
  122. package/dist/review-verdict.js.map +1 -1
  123. package/dist/routes/attention-activity.d.ts.map +1 -1
  124. package/dist/routes/attention-activity.js +5 -2
  125. package/dist/routes/attention-activity.js.map +1 -1
  126. package/dist/routes/attention-providers.d.ts +0 -1
  127. package/dist/routes/attention-providers.d.ts.map +1 -1
  128. package/dist/routes/attention-providers.js +5 -6
  129. package/dist/routes/attention-providers.js.map +1 -1
  130. package/dist/routes/config.d.ts +44 -5
  131. package/dist/routes/config.d.ts.map +1 -1
  132. package/dist/routes/config.js +181 -58
  133. package/dist/routes/config.js.map +1 -1
  134. package/dist/routes/contracts.d.ts +4 -0
  135. package/dist/routes/contracts.d.ts.map +1 -1
  136. package/dist/routes/contracts.js +3 -1
  137. package/dist/routes/contracts.js.map +1 -1
  138. package/dist/routes/custom-provider-source.d.ts +8 -0
  139. package/dist/routes/custom-provider-source.d.ts.map +1 -1
  140. package/dist/routes/custom-provider-source.js +22 -1
  141. package/dist/routes/custom-provider-source.js.map +1 -1
  142. package/dist/routes/oauth.js +1 -1
  143. package/dist/routes/oauth.js.map +1 -1
  144. package/dist/routes/projects.d.ts +1 -1
  145. package/dist/routes/projects.d.ts.map +1 -1
  146. package/dist/routes/projects.js +1 -1
  147. package/dist/routes/projects.js.map +1 -1
  148. package/dist/routes/provider-credentials.d.ts +7 -2
  149. package/dist/routes/provider-credentials.d.ts.map +1 -1
  150. package/dist/routes/provider-credentials.js +30 -8
  151. package/dist/routes/provider-credentials.js.map +1 -1
  152. package/dist/routes/skills.d.ts +2 -4
  153. package/dist/routes/skills.d.ts.map +1 -1
  154. package/dist/routes/skills.js +19 -65
  155. package/dist/routes/skills.js.map +1 -1
  156. package/dist/routes/source-control-sessions.d.ts +9 -1
  157. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  158. package/dist/routes/source-control-sessions.js +131 -7
  159. package/dist/routes/source-control-sessions.js.map +1 -1
  160. package/dist/routes/surface.d.ts +3 -2
  161. package/dist/routes/surface.d.ts.map +1 -1
  162. package/dist/routes/surface.js +7 -3
  163. package/dist/routes/surface.js.map +1 -1
  164. package/dist/routes/work-items.d.ts.map +1 -1
  165. package/dist/routes/work-items.js +60 -120
  166. package/dist/routes/work-items.js.map +1 -1
  167. package/dist/rules/binding-context.js +7 -7
  168. package/dist/rules/binding-context.js.map +1 -1
  169. package/dist/rules/dispatch-errors.d.ts.map +1 -1
  170. package/dist/rules/dispatch-errors.js +12 -0
  171. package/dist/rules/dispatch-errors.js.map +1 -1
  172. package/dist/rules/dispatcher.d.ts +8 -1
  173. package/dist/rules/dispatcher.d.ts.map +1 -1
  174. package/dist/rules/dispatcher.js +117 -12
  175. package/dist/rules/dispatcher.js.map +1 -1
  176. package/dist/rules/processor.d.ts.map +1 -1
  177. package/dist/rules/processor.js +2 -1
  178. package/dist/rules/processor.js.map +1 -1
  179. package/dist/rules/start-coordinator.d.ts +4 -0
  180. package/dist/rules/start-coordinator.d.ts.map +1 -1
  181. package/dist/rules/start-coordinator.js +36 -4
  182. package/dist/rules/start-coordinator.js.map +1 -1
  183. package/dist/rules/tools.d.ts.map +1 -1
  184. package/dist/rules/tools.js +82 -3
  185. package/dist/rules/tools.js.map +1 -1
  186. package/dist/rules/transition-service.d.ts +17 -0
  187. package/dist/rules/transition-service.d.ts.map +1 -1
  188. package/dist/rules/transition-service.js +25 -0
  189. package/dist/rules/transition-service.js.map +1 -1
  190. package/dist/rules/types.d.ts +2 -4
  191. package/dist/rules/types.d.ts.map +1 -1
  192. package/dist/rules/types.js +1 -4
  193. package/dist/rules/types.js.map +1 -1
  194. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  195. package/dist/sandbox/local-factory-sandbox.js +1 -0
  196. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  197. package/dist/sandbox/session-retirement.d.ts +27 -3
  198. package/dist/sandbox/session-retirement.d.ts.map +1 -1
  199. package/dist/sandbox/session-retirement.js +158 -20
  200. package/dist/sandbox/session-retirement.js.map +1 -1
  201. package/dist/secret-encryption.d.ts.map +1 -1
  202. package/dist/secret-encryption.js +65 -25
  203. package/dist/secret-encryption.js.map +1 -1
  204. package/dist/session/channel-reply-processor.d.ts +36 -0
  205. package/dist/session/channel-reply-processor.d.ts.map +1 -0
  206. package/dist/session/channel-reply-processor.js +46 -0
  207. package/dist/session/channel-reply-processor.js.map +1 -0
  208. package/dist/session/channel-session-link-processor.d.ts +39 -0
  209. package/dist/session/channel-session-link-processor.d.ts.map +1 -0
  210. package/dist/session/channel-session-link-processor.js +100 -0
  211. package/dist/session/channel-session-link-processor.js.map +1 -0
  212. package/dist/session/credential-policy.d.ts +1 -1
  213. package/dist/session/credential-policy.d.ts.map +1 -1
  214. package/dist/session/credential-policy.js +7 -6
  215. package/dist/session/credential-policy.js.map +1 -1
  216. package/dist/session/default-model-hydration.d.ts +1 -0
  217. package/dist/session/default-model-hydration.d.ts.map +1 -1
  218. package/dist/session/default-model-hydration.js +3 -2
  219. package/dist/session/default-model-hydration.js.map +1 -1
  220. package/dist/session/environment-state-processor.js +1 -1
  221. package/dist/session/environment-state-processor.js.map +1 -1
  222. package/dist/session/factory-session.d.ts +19 -1
  223. package/dist/session/factory-session.d.ts.map +1 -1
  224. package/dist/session/factory-session.js +5 -2
  225. package/dist/session/factory-session.js.map +1 -1
  226. package/dist/session/filesystem-capture.d.ts +3 -3
  227. package/dist/session/filesystem-capture.d.ts.map +1 -1
  228. package/dist/session/filesystem-capture.js +16 -17
  229. package/dist/session/filesystem-capture.js.map +1 -1
  230. package/dist/session/first-exec-capture.d.ts +1 -1
  231. package/dist/session/first-exec-capture.d.ts.map +1 -1
  232. package/dist/session/first-exec-capture.js +1 -1
  233. package/dist/session/first-exec-capture.js.map +1 -1
  234. package/dist/session/first-message-capture.d.ts +1 -1
  235. package/dist/session/first-message-capture.d.ts.map +1 -1
  236. package/dist/session/first-message-capture.js +1 -1
  237. package/dist/session/first-message-capture.js.map +1 -1
  238. package/dist/session/live-sessions.d.ts +1 -1
  239. package/dist/session/live-sessions.js +1 -1
  240. package/dist/session/live-sessions.js.map +1 -1
  241. package/dist/session/memory-settings-hydration.d.ts +1 -1
  242. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  243. package/dist/session/memory-settings-hydration.js +1 -1
  244. package/dist/session/memory-settings-hydration.js.map +1 -1
  245. package/dist/session/reply-credential-processor.d.ts +15 -0
  246. package/dist/session/reply-credential-processor.d.ts.map +1 -0
  247. package/dist/session/reply-credential-processor.js +38 -0
  248. package/dist/session/reply-credential-processor.js.map +1 -0
  249. package/dist/session/resource-migration.d.ts +87 -0
  250. package/dist/session/resource-migration.d.ts.map +1 -0
  251. package/dist/session/resource-migration.js +163 -0
  252. package/dist/session/resource-migration.js.map +1 -0
  253. package/dist/session/run-audit.d.ts +24 -0
  254. package/dist/session/run-audit.d.ts.map +1 -1
  255. package/dist/session/run-audit.js +93 -7
  256. package/dist/session/run-audit.js.map +1 -1
  257. package/dist/session/session-threads.d.ts +49 -0
  258. package/dist/session/session-threads.d.ts.map +1 -0
  259. package/dist/session/session-threads.js +70 -0
  260. package/dist/session/session-threads.js.map +1 -0
  261. package/dist/session/source-control-tools.d.ts +19 -1
  262. package/dist/session/source-control-tools.d.ts.map +1 -1
  263. package/dist/session/source-control-tools.js +78 -11
  264. package/dist/session/source-control-tools.js.map +1 -1
  265. package/dist/session/thinking-default.d.ts.map +1 -1
  266. package/dist/session/thinking-default.js +3 -4
  267. package/dist/session/thinking-default.js.map +1 -1
  268. package/dist/session/thread-session-stamp.d.ts +21 -0
  269. package/dist/session/thread-session-stamp.d.ts.map +1 -0
  270. package/dist/session/thread-session-stamp.js +20 -0
  271. package/dist/session/thread-session-stamp.js.map +1 -0
  272. package/dist/session/thread-title-mirror.d.ts +1 -1
  273. package/dist/session/thread-title-mirror.d.ts.map +1 -1
  274. package/dist/session/thread-title-mirror.js +1 -1
  275. package/dist/session/thread-title-mirror.js.map +1 -1
  276. package/dist/session/work-item-repository.d.ts +1 -1
  277. package/dist/session/work-item-repository.d.ts.map +1 -1
  278. package/dist/session/work-item-repository.js.map +1 -1
  279. package/dist/skills/service.d.ts +6 -8
  280. package/dist/skills/service.d.ts.map +1 -1
  281. package/dist/skills/service.js +3 -3
  282. package/dist/skills/service.js.map +1 -1
  283. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  284. package/dist/storage/domains/audit/agent-audit.js +2 -2
  285. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  286. package/dist/storage/domains/audit/domain.js +1 -1
  287. package/dist/storage/domains/filesystem/base.d.ts +4 -0
  288. package/dist/storage/domains/filesystem/base.d.ts.map +1 -1
  289. package/dist/storage/domains/filesystem/base.js +5 -0
  290. package/dist/storage/domains/filesystem/base.js.map +1 -1
  291. package/dist/storage/domains/integrations/base.d.ts +8 -0
  292. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  293. package/dist/storage/domains/integrations/base.js +7 -0
  294. package/dist/storage/domains/integrations/base.js.map +1 -1
  295. package/dist/storage/domains/projects/base.d.ts +6 -0
  296. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  297. package/dist/storage/domains/projects/base.js +12 -0
  298. package/dist/storage/domains/projects/base.js.map +1 -1
  299. package/dist/storage/domains/source-control/base.d.ts +28 -0
  300. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  301. package/dist/storage/domains/source-control/base.js +48 -0
  302. package/dist/storage/domains/source-control/base.js.map +1 -1
  303. package/dist/storage/domains/source-control/inmemory.d.ts +8 -0
  304. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  305. package/dist/storage/domains/source-control/inmemory.js +13 -1
  306. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  307. package/dist/storage/domains/work-items/base.d.ts +79 -7
  308. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  309. package/dist/storage/domains/work-items/base.js +172 -30
  310. package/dist/storage/domains/work-items/base.js.map +1 -1
  311. package/dist/timing.d.ts +23 -0
  312. package/dist/timing.d.ts.map +1 -1
  313. package/dist/timing.js +85 -6
  314. package/dist/timing.js.map +1 -1
  315. package/dist/tools/annotations.d.ts +7 -0
  316. package/dist/tools/annotations.d.ts.map +1 -0
  317. package/dist/tools/annotations.js +102 -0
  318. package/dist/tools/annotations.js.map +1 -0
  319. package/dist/tools/approval-tools.d.ts +15 -0
  320. package/dist/tools/approval-tools.d.ts.map +1 -0
  321. package/dist/tools/approval-tools.js +79 -0
  322. package/dist/tools/approval-tools.js.map +1 -0
  323. package/dist/tools/deliver.d.ts +71 -0
  324. package/dist/tools/deliver.d.ts.map +1 -0
  325. package/dist/tools/deliver.js +129 -0
  326. package/dist/tools/deliver.js.map +1 -0
  327. package/dist/tools/index.d.ts +30 -0
  328. package/dist/tools/index.d.ts.map +1 -0
  329. package/dist/tools/index.js +36 -0
  330. package/dist/tools/index.js.map +1 -0
  331. package/dist/tools/messaging-tools.d.ts +28 -0
  332. package/dist/tools/messaging-tools.d.ts.map +1 -0
  333. package/dist/tools/messaging-tools.js +396 -0
  334. package/dist/tools/messaging-tools.js.map +1 -0
  335. package/dist/tools/permissions.d.ts +31 -0
  336. package/dist/tools/permissions.d.ts.map +1 -0
  337. package/dist/tools/permissions.js +48 -0
  338. package/dist/tools/permissions.js.map +1 -0
  339. package/dist/tools/read-tools.d.ts +30 -0
  340. package/dist/tools/read-tools.d.ts.map +1 -0
  341. package/dist/tools/read-tools.js +293 -0
  342. package/dist/tools/read-tools.js.map +1 -0
  343. package/dist/tools/reports.d.ts +174 -0
  344. package/dist/tools/reports.d.ts.map +1 -0
  345. package/dist/tools/reports.js +380 -0
  346. package/dist/tools/reports.js.map +1 -0
  347. package/dist/tools/run-watch.d.ts +46 -0
  348. package/dist/tools/run-watch.d.ts.map +1 -0
  349. package/dist/tools/run-watch.js +102 -0
  350. package/dist/tools/run-watch.js.map +1 -0
  351. package/dist/tools/scope.d.ts +54 -0
  352. package/dist/tools/scope.d.ts.map +1 -0
  353. package/dist/tools/scope.js +87 -0
  354. package/dist/tools/scope.js.map +1 -0
  355. package/dist/tools/session-tools.d.ts +43 -0
  356. package/dist/tools/session-tools.d.ts.map +1 -0
  357. package/dist/tools/session-tools.js +410 -0
  358. package/dist/tools/session-tools.js.map +1 -0
  359. package/dist/tools/sessions-state-processor.d.ts +41 -0
  360. package/dist/tools/sessions-state-processor.d.ts.map +1 -0
  361. package/dist/tools/sessions-state-processor.js +190 -0
  362. package/dist/tools/sessions-state-processor.js.map +1 -0
  363. package/dist/tools/side-sessions.d.ts +53 -0
  364. package/dist/tools/side-sessions.d.ts.map +1 -0
  365. package/dist/tools/side-sessions.js +83 -0
  366. package/dist/tools/side-sessions.js.map +1 -0
  367. package/dist/tools/work-item-tools.d.ts +28 -0
  368. package/dist/tools/work-item-tools.d.ts.map +1 -0
  369. package/dist/tools/work-item-tools.js +342 -0
  370. package/dist/tools/work-item-tools.js.map +1 -0
  371. package/dist/work-items/create-work-item.d.ts +55 -0
  372. package/dist/work-items/create-work-item.d.ts.map +1 -0
  373. package/dist/work-items/create-work-item.js +101 -0
  374. package/dist/work-items/create-work-item.js.map +1 -0
  375. package/dist/work-items/decision-actions.d.ts +42 -0
  376. package/dist/work-items/decision-actions.d.ts.map +1 -0
  377. package/dist/work-items/decision-actions.js +70 -0
  378. package/dist/work-items/decision-actions.js.map +1 -0
  379. package/dist/workspace.d.ts +1 -1
  380. package/dist/workspace.d.ts.map +1 -1
  381. package/dist/workspace.js +15 -7
  382. package/dist/workspace.js.map +1 -1
  383. package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
  384. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  385. package/factory-skills/factory-plan/SKILL.md +4 -4
  386. package/factory-skills/factory-rereview/SKILL.md +3 -1
  387. package/factory-skills/factory-review/SKILL.md +3 -1
  388. package/factory-skills/factory-self-review/SKILL.md +46 -0
  389. package/package.json +14 -14
@@ -1 +1 @@
1
- {"version":3,"file":"live-sessions.js","names":["#byId","#parkedListeners","#track","#lastParkStamp","#nextParkStamp"],"sources":["../../src/session/live-sessions.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\n/** The slice of a live session this registry needs: its id, its run, its parked tools, and who it works for. */\nexport interface LiveSession {\n readonly identity: { getId(): string };\n readonly run: { isRunning(): boolean };\n readonly state: { get(): Readonly<{ factoryOrgId?: string; factoryProjectId?: string }> };\n readonly displayState: { get(): { pendingSuspensions: ReadonlyMap<string, { toolName: string }> } };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\ninterface SessionNotifier {\n onSessionCreated(listener: (session: LiveSession) => void): () => void;\n onSessionDeleted(listener: (session: LiveSession) => void): () => void;\n}\n\n/** The tool a session has waited on longest, and since when (epoch ms). */\nexport interface ParkedRun {\n toolName: string;\n suspendedAt: number;\n}\n\ninterface TrackedSession {\n session: LiveSession;\n seenAt: number;\n suspendedAt: Map<string, number>;\n unsubscribe: () => void;\n}\n\n/**\n * The sessions this process has materialized, by session id.\n *\n * Membership comes from the controller's notifications rather than\n * `getSessionByResource`, which hands back the pending creation — awaiting it\n * from a request blocks for as long as the session takes to materialize its\n * sandbox, minutes on a cold clone. Notifications only fire once a session\n * exists, so a read never waits.\n *\n * Whether a session is *running* or *parked* is read from the session itself,\n * never tracked here: no missed run event can leave the answer stuck. Only the\n * moment each suspension arrived is kept, since the session does not date them.\n */\nexport class LiveSessions {\n readonly #byId = new Map<string, TrackedSession>();\n readonly #parkedListeners = new Set<(session: LiveSession) => void>();\n /** A park stamp doubles as the receipt key, so two suspensions in one millisecond must not share one. */\n #lastParkStamp = 0;\n\n constructor(controller: SessionNotifier) {\n controller.onSessionCreated(session => this.#track(session));\n controller.onSessionDeleted(session => {\n const id = session.identity.getId();\n const wasParked = this.parked(id) !== undefined;\n this.#byId.get(id)?.unsubscribe();\n this.#byId.delete(id);\n if (wasParked) for (const listener of this.#parkedListeners) listener(session);\n });\n }\n\n #nextParkStamp(): number {\n this.#lastParkStamp = Math.max(Date.now(), this.#lastParkStamp + 1);\n return this.#lastParkStamp;\n }\n\n #track(session: LiveSession): void {\n const suspendedAt = new Map<string, number>();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'tool_suspended') suspendedAt.set(event.toolCallId, this.#nextParkStamp());\n else if (event.type === 'tool_suspension_cancelled') suspendedAt.delete(event.toolCallId);\n else if (event.type === 'agent_end' && event.reason !== 'suspended') suspendedAt.clear();\n else return;\n for (const listener of this.#parkedListeners) listener(session);\n });\n this.#byId.set(session.identity.getId(), { session, seenAt: Date.now(), suspendedAt, unsubscribe });\n }\n\n /** Whether the session has an agent run in flight right now. */\n isRunning(sessionId: string): boolean {\n return this.#byId.get(sessionId)?.session.run.isRunning() ?? false;\n }\n\n /** The tool the session is parked on, oldest first, or nothing when no answer is owed. */\n parked(sessionId: string): ParkedRun | undefined {\n const tracked = this.#byId.get(sessionId);\n if (!tracked) return undefined;\n let oldest: ParkedRun | undefined;\n for (const [toolCallId, { toolName }] of tracked.session.displayState.get().pendingSuspensions) {\n const suspendedAt = tracked.suspendedAt.get(toolCallId) ?? tracked.seenAt;\n if (!oldest || suspendedAt < oldest.suspendedAt) oldest = { toolName, suspendedAt };\n }\n return oldest;\n }\n\n /** Every parked session of a project, so the inbox reads no card when nothing waits. */\n parkedIn(factoryProjectId: string): Array<{ sessionId: string; run: ParkedRun }> {\n const parked: Array<{ sessionId: string; run: ParkedRun }> = [];\n for (const [sessionId, tracked] of this.#byId) {\n if (tracked.session.state.get().factoryProjectId !== factoryProjectId) continue;\n const run = this.parked(sessionId);\n if (run) parked.push({ sessionId, run });\n }\n return parked;\n }\n\n /** Fires when a session parks, is answered, or finishes a turn: whoever shows parked runs re-reads. */\n onParkedChanged(listener: (session: LiveSession) => void): () => void {\n this.#parkedListeners.add(listener);\n return () => this.#parkedListeners.delete(listener);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA0CA,IAAa,eAAb,MAA0B;CACxB,wBAAiB,IAAI,IAA4B;CACjD,mCAA4B,IAAI,IAAoC;;CAEpE,iBAAiB;CAEjB,YAAY,YAA6B;EACvC,WAAW,kBAAiB,YAAW,KAAKE,OAAO,OAAO,CAAC;EAC3D,WAAW,kBAAiB,YAAW;GACrC,MAAM,KAAK,QAAQ,SAAS,MAAM;GAClC,MAAM,YAAY,KAAK,OAAO,EAAE,MAAM,KAAA;GACtC,KAAKF,MAAM,IAAI,EAAE,CAAC,EAAE,YAAY;GAChC,KAAKA,MAAM,OAAO,EAAE;GACpB,IAAI,WAAW,KAAK,MAAM,YAAY,KAAKC,kBAAkB,SAAS,OAAO;EAC/E,CAAC;CACH;CAEA,iBAAyB;EACvB,KAAKE,iBAAiB,KAAK,IAAI,KAAK,IAAI,GAAG,KAAKA,iBAAiB,CAAC;EAClE,OAAO,KAAKA;CACd;CAEA,OAAO,SAA4B;EACjC,MAAM,8BAAc,IAAI,IAAoB;EAC5C,MAAM,cAAc,QAAQ,WAAU,UAAS;GAC7C,IAAI,MAAM,SAAS,kBAAkB,YAAY,IAAI,MAAM,YAAY,KAAKC,eAAe,CAAC;QACvF,IAAI,MAAM,SAAS,6BAA6B,YAAY,OAAO,MAAM,UAAU;QACnF,IAAI,MAAM,SAAS,eAAe,MAAM,WAAW,aAAa,YAAY,MAAM;QAClF;GACL,KAAK,MAAM,YAAY,KAAKH,kBAAkB,SAAS,OAAO;EAChE,CAAC;EACD,KAAKD,MAAM,IAAI,QAAQ,SAAS,MAAM,GAAG;GAAE;GAAS,QAAQ,KAAK,IAAI;GAAG;GAAa;EAAY,CAAC;CACpG;;CAGA,UAAU,WAA4B;EACpC,OAAO,KAAKA,MAAM,IAAI,SAAS,CAAC,EAAE,QAAQ,IAAI,UAAU,KAAK;CAC/D;;CAGA,OAAO,WAA0C;EAC/C,MAAM,UAAU,KAAKA,MAAM,IAAI,SAAS;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,IAAI;EACJ,KAAK,MAAM,CAAC,YAAY,EAAE,eAAe,QAAQ,QAAQ,aAAa,IAAI,CAAC,CAAC,oBAAoB;GAC9F,MAAM,cAAc,QAAQ,YAAY,IAAI,UAAU,KAAK,QAAQ;GACnE,IAAI,CAAC,UAAU,cAAc,OAAO,aAAa,SAAS;IAAE;IAAU;GAAY;EACpF;EACA,OAAO;CACT;;CAGA,SAAS,kBAAwE;EAC/E,MAAM,SAAuD,CAAC;EAC9D,KAAK,MAAM,CAAC,WAAW,YAAY,KAAKA,OAAO;GAC7C,IAAI,QAAQ,QAAQ,MAAM,IAAI,CAAC,CAAC,qBAAqB,kBAAkB;GACvE,MAAM,MAAM,KAAK,OAAO,SAAS;GACjC,IAAI,KAAK,OAAO,KAAK;IAAE;IAAW;GAAI,CAAC;EACzC;EACA,OAAO;CACT;;CAGA,gBAAgB,UAAsD;EACpE,KAAKC,iBAAiB,IAAI,QAAQ;EAClC,aAAa,KAAKA,iBAAiB,OAAO,QAAQ;CACpD;AACF"}
1
+ {"version":3,"file":"live-sessions.js","names":["#byId","#parkedListeners","#track","#lastParkStamp","#nextParkStamp"],"sources":["../../src/session/live-sessions.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\n/** The slice of a live session this registry needs: its id, its run, its parked tools, and who it works for. */\nexport interface LiveSession {\n readonly identity: { getId(): string };\n readonly run: { isRunning(): boolean };\n readonly state: { get(): Readonly<{ factoryOrgId?: string; factoryProjectId?: string }> };\n readonly displayState: { get(): { pendingSuspensions: ReadonlyMap<string, { toolName: string }> } };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\ninterface SessionNotifier {\n onSessionCreated(listener: (session: LiveSession) => void): () => void;\n onSessionDeleted(listener: (session: LiveSession) => void): () => void;\n}\n\n/** The tool a session has waited on longest, and since when (epoch ms). */\nexport interface ParkedRun {\n toolName: string;\n suspendedAt: number;\n}\n\ninterface TrackedSession {\n session: LiveSession;\n seenAt: number;\n suspendedAt: Map<string, number>;\n unsubscribe: () => void;\n}\n\n/**\n * The sessions this process has materialized, by session id.\n *\n * Membership comes from the controller's notifications rather than\n * `getSession`, which hands back the pending creation — awaiting it\n * from a request blocks for as long as the session takes to materialize its\n * sandbox, minutes on a cold clone. Notifications only fire once a session\n * exists, so a read never waits.\n *\n * Whether a session is *running* or *parked* is read from the session itself,\n * never tracked here: no missed run event can leave the answer stuck. Only the\n * moment each suspension arrived is kept, since the session does not date them.\n */\nexport class LiveSessions {\n readonly #byId = new Map<string, TrackedSession>();\n readonly #parkedListeners = new Set<(session: LiveSession) => void>();\n /** A park stamp doubles as the receipt key, so two suspensions in one millisecond must not share one. */\n #lastParkStamp = 0;\n\n constructor(controller: SessionNotifier) {\n controller.onSessionCreated(session => this.#track(session));\n controller.onSessionDeleted(session => {\n const id = session.identity.getId();\n const wasParked = this.parked(id) !== undefined;\n this.#byId.get(id)?.unsubscribe();\n this.#byId.delete(id);\n if (wasParked) for (const listener of this.#parkedListeners) listener(session);\n });\n }\n\n #nextParkStamp(): number {\n this.#lastParkStamp = Math.max(Date.now(), this.#lastParkStamp + 1);\n return this.#lastParkStamp;\n }\n\n #track(session: LiveSession): void {\n const suspendedAt = new Map<string, number>();\n const unsubscribe = session.subscribe(event => {\n if (event.type === 'tool_suspended') suspendedAt.set(event.toolCallId, this.#nextParkStamp());\n else if (event.type === 'tool_suspension_cancelled') suspendedAt.delete(event.toolCallId);\n else if (event.type === 'agent_end' && event.reason !== 'suspended') suspendedAt.clear();\n else return;\n for (const listener of this.#parkedListeners) listener(session);\n });\n this.#byId.set(session.identity.getId(), { session, seenAt: Date.now(), suspendedAt, unsubscribe });\n }\n\n /** Whether the session has an agent run in flight right now. */\n isRunning(sessionId: string): boolean {\n return this.#byId.get(sessionId)?.session.run.isRunning() ?? false;\n }\n\n /** The tool the session is parked on, oldest first, or nothing when no answer is owed. */\n parked(sessionId: string): ParkedRun | undefined {\n const tracked = this.#byId.get(sessionId);\n if (!tracked) return undefined;\n let oldest: ParkedRun | undefined;\n for (const [toolCallId, { toolName }] of tracked.session.displayState.get().pendingSuspensions) {\n const suspendedAt = tracked.suspendedAt.get(toolCallId) ?? tracked.seenAt;\n if (!oldest || suspendedAt < oldest.suspendedAt) oldest = { toolName, suspendedAt };\n }\n return oldest;\n }\n\n /** Every parked session of a project, so the inbox reads no card when nothing waits. */\n parkedIn(factoryProjectId: string): Array<{ sessionId: string; run: ParkedRun }> {\n const parked: Array<{ sessionId: string; run: ParkedRun }> = [];\n for (const [sessionId, tracked] of this.#byId) {\n if (tracked.session.state.get().factoryProjectId !== factoryProjectId) continue;\n const run = this.parked(sessionId);\n if (run) parked.push({ sessionId, run });\n }\n return parked;\n }\n\n /** Fires when a session parks, is answered, or finishes a turn: whoever shows parked runs re-reads. */\n onParkedChanged(listener: (session: LiveSession) => void): () => void {\n this.#parkedListeners.add(listener);\n return () => this.#parkedListeners.delete(listener);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA0CA,IAAa,eAAb,MAA0B;CACxB,wBAAiB,IAAI,IAA4B;CACjD,mCAA4B,IAAI,IAAoC;;CAEpE,iBAAiB;CAEjB,YAAY,YAA6B;EACvC,WAAW,kBAAiB,YAAW,KAAKE,OAAO,OAAO,CAAC;EAC3D,WAAW,kBAAiB,YAAW;GACrC,MAAM,KAAK,QAAQ,SAAS,MAAM;GAClC,MAAM,YAAY,KAAK,OAAO,EAAE,MAAM,KAAA;GACtC,KAAKF,MAAM,IAAI,EAAE,CAAC,EAAE,YAAY;GAChC,KAAKA,MAAM,OAAO,EAAE;GACpB,IAAI,WAAW,KAAK,MAAM,YAAY,KAAKC,kBAAkB,SAAS,OAAO;EAC/E,CAAC;CACH;CAEA,iBAAyB;EACvB,KAAKE,iBAAiB,KAAK,IAAI,KAAK,IAAI,GAAG,KAAKA,iBAAiB,CAAC;EAClE,OAAO,KAAKA;CACd;CAEA,OAAO,SAA4B;EACjC,MAAM,8BAAc,IAAI,IAAoB;EAC5C,MAAM,cAAc,QAAQ,WAAU,UAAS;GAC7C,IAAI,MAAM,SAAS,kBAAkB,YAAY,IAAI,MAAM,YAAY,KAAKC,eAAe,CAAC;QACvF,IAAI,MAAM,SAAS,6BAA6B,YAAY,OAAO,MAAM,UAAU;QACnF,IAAI,MAAM,SAAS,eAAe,MAAM,WAAW,aAAa,YAAY,MAAM;QAClF;GACL,KAAK,MAAM,YAAY,KAAKH,kBAAkB,SAAS,OAAO;EAChE,CAAC;EACD,KAAKD,MAAM,IAAI,QAAQ,SAAS,MAAM,GAAG;GAAE;GAAS,QAAQ,KAAK,IAAI;GAAG;GAAa;EAAY,CAAC;CACpG;;CAGA,UAAU,WAA4B;EACpC,OAAO,KAAKA,MAAM,IAAI,SAAS,CAAC,EAAE,QAAQ,IAAI,UAAU,KAAK;CAC/D;;CAGA,OAAO,WAA0C;EAC/C,MAAM,UAAU,KAAKA,MAAM,IAAI,SAAS;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,IAAI;EACJ,KAAK,MAAM,CAAC,YAAY,EAAE,eAAe,QAAQ,QAAQ,aAAa,IAAI,CAAC,CAAC,oBAAoB;GAC9F,MAAM,cAAc,QAAQ,YAAY,IAAI,UAAU,KAAK,QAAQ;GACnE,IAAI,CAAC,UAAU,cAAc,OAAO,aAAa,SAAS;IAAE;IAAU;GAAY;EACpF;EACA,OAAO;CACT;;CAGA,SAAS,kBAAwE;EAC/E,MAAM,SAAuD,CAAC;EAC9D,KAAK,MAAM,CAAC,WAAW,YAAY,KAAKA,OAAO;GAC7C,IAAI,QAAQ,QAAQ,MAAM,IAAI,CAAC,CAAC,qBAAqB,kBAAkB;GACvE,MAAM,MAAM,KAAK,OAAO,SAAS;GACjC,IAAI,KAAK,OAAO,KAAK;IAAE;IAAW;GAAI,CAAC;EACzC;EACA,OAAO;CACT;;CAGA,gBAAgB,UAAsD;EACpE,KAAKC,iBAAiB,IAAI,QAAQ;EAClC,aAAa,KAAKA,iBAAiB,OAAO,QAAQ;CACpD;AACF"}
@@ -13,7 +13,7 @@ export declare const DEFAULT_REFLECTION_THRESHOLD = 40000;
13
13
  export declare function layerPersonalMemorySettings(project: MemorySettingsRecord | null, personal: MemorySettingsRecord | null): MemorySettingsRecord | null;
14
14
  export interface MemorySettingsHydrationSession {
15
15
  readonly identity: {
16
- getResourceId(): string;
16
+ getId(): string;
17
17
  };
18
18
  state: {
19
19
  get: () => MastraCodeState | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"memory-settings-hydration.d.ts","sourceRoot":"","sources":["../../src/session/memory-settings-hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAG5F,yDAAyD;AACzD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,oBAAoB,GAAG,IAAI,EACpC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GACpC,oBAAoB,GAAG,IAAI,CAW7B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,eAAe,GAAG,SAAS,CAAC;QACvC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAClE,CAAC;CACH;AAED,MAAM,WAAW,mCAAmC;IAClD,4FAA4F;IAC5F,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,EACvC,EAAE,aAAa,EAAE,EAAE,mCAAmC,GACrD,OAAO,CAAC,IAAI,CAAC,CAYf"}
1
+ {"version":3,"file":"memory-settings-hydration.d.ts","sourceRoot":"","sources":["../../src/session/memory-settings-hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAG5F,yDAAyD;AACzD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,oBAAoB,GAAG,IAAI,EACpC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GACpC,oBAAoB,GAAG,IAAI,CAW7B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,QAAQ,EAAE;QAAE,KAAK,IAAI,MAAM,CAAA;KAAE,CAAC;IACvC,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,eAAe,GAAG,SAAS,CAAC;QACvC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAClE,CAAC;CACH;AAED,MAAM,WAAW,mCAAmC;IAClD,4FAA4F;IAC5F,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,8BAA8B,EACvC,EAAE,aAAa,EAAE,EAAE,mCAAmC,GACrD,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -31,7 +31,7 @@ async function hydrateSessionMemorySettings(session, { sourceControl }) {
31
31
  const state = session.state.get() ?? {};
32
32
  if (Boolean(state.factoryProjectId) && hasResolvedOrg(state.factoryOrgId)) return;
33
33
  try {
34
- await seedSessionOrg(session, (await sourceControl.sessions.getBySessionId(session.identity.getResourceId()))?.orgId);
34
+ await seedSessionOrg(session, (await sourceControl.sessions.getBySessionId(session.identity.getId()))?.orgId);
35
35
  } catch (error) {
36
36
  console.warn("[Factory session hydration] Unable to resolve the session organization.", error);
37
37
  if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"memory-settings-hydration.js","names":[],"sources":["../../src/session/memory-settings-hydration.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\n\nimport type { MemorySettingsRecord } from '../storage/domains/memory-settings/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { hasResolvedOrg, seedSessionOrg } from './org-seed.js';\n\n/** Default thresholds mirror the TUI `/om` fallbacks. */\nexport const DEFAULT_OBSERVATION_THRESHOLD = 30_000;\nexport const DEFAULT_REFLECTION_THRESHOLD = 40_000;\n\n/**\n * Layer a channel sender's own row over the project's shared row. The sender\n * wins only for knobs they saved; an unsaved (`null`) knob keeps the project's\n * value rather than resetting to Auto or the built-in default, because the\n * project's choice may be the only credentialed one.\n */\nexport function layerPersonalMemorySettings(\n project: MemorySettingsRecord | null,\n personal: MemorySettingsRecord | null,\n): MemorySettingsRecord | null {\n if (!personal) return project;\n if (!project) return personal;\n return {\n ...project,\n observerModelId: personal.observerModelId ?? project.observerModelId,\n reflectorModelId: personal.reflectorModelId ?? project.reflectorModelId,\n observationThreshold: personal.observationThreshold ?? project.observationThreshold,\n reflectionThreshold: personal.reflectionThreshold ?? project.reflectionThreshold,\n observeAttachments: personal.observeAttachments ?? project.observeAttachments,\n };\n}\n\nexport interface MemorySettingsHydrationSession {\n readonly identity: { getResourceId(): string };\n state: {\n get: () => MastraCodeState | undefined;\n set: (updates: Partial<MastraCodeState>) => Promise<void> | void;\n };\n}\n\nexport interface MemorySettingsHydrationDependencies {\n /** GitHub-integration source-control rows — the only creator of web user sessions today. */\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n}\n\n/**\n * Seed a freshly created controller session's tenant org from its source-control\n * row. Memory settings remain DB-authoritative and are loaded into request\n * context for each invocation instead of being copied into mutable session\n * state.\n */\nexport async function hydrateSessionMemorySettings(\n session: MemorySettingsHydrationSession,\n { sourceControl }: MemorySettingsHydrationDependencies,\n): Promise<void> {\n const state: Partial<MastraCodeState> = session.state.get() ?? {};\n const isFactoryRun = Boolean(state.factoryProjectId);\n if (isFactoryRun && hasResolvedOrg(state.factoryOrgId)) return;\n\n try {\n const record = await sourceControl.sessions.getBySessionId(session.identity.getResourceId());\n await seedSessionOrg(session, record?.orgId);\n } catch (error) {\n console.warn('[Factory session hydration] Unable to resolve the session organization.', error);\n if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, undefined);\n }\n}\n"],"mappings":";;;AAOA,MAAa,gCAAgC;AAC7C,MAAa,+BAA+B;;;;;;;AAQ5C,SAAgB,4BACd,SACA,UAC6B;CAC7B,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,GAAG;EACH,iBAAiB,SAAS,mBAAmB,QAAQ;EACrD,kBAAkB,SAAS,oBAAoB,QAAQ;EACvD,sBAAsB,SAAS,wBAAwB,QAAQ;EAC/D,qBAAqB,SAAS,uBAAuB,QAAQ;EAC7D,oBAAoB,SAAS,sBAAsB,QAAQ;CAC7D;AACF;;;;;;;AAuBA,eAAsB,6BACpB,SACA,EAAE,iBACa;CACf,MAAM,QAAkC,QAAQ,MAAM,IAAI,KAAK,CAAC;CAEhE,IADqB,QAAQ,MAAM,gBACpB,KAAK,eAAe,MAAM,YAAY,GAAG;CAExD,IAAI;EAEF,MAAM,eAAe,UAAS,MADT,cAAc,SAAS,eAAe,QAAQ,SAAS,cAAc,CAAC,EAAA,EACrD,KAAK;CAC7C,SAAS,OAAO;EACd,QAAQ,KAAK,2EAA2E,KAAK;EAC7F,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,EAAE,cAAc,MAAM,eAAe,SAAS,KAAA,CAAS;CACjF;AACF"}
1
+ {"version":3,"file":"memory-settings-hydration.js","names":[],"sources":["../../src/session/memory-settings-hydration.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\n\nimport type { MemorySettingsRecord } from '../storage/domains/memory-settings/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport { hasResolvedOrg, seedSessionOrg } from './org-seed.js';\n\n/** Default thresholds mirror the TUI `/om` fallbacks. */\nexport const DEFAULT_OBSERVATION_THRESHOLD = 30_000;\nexport const DEFAULT_REFLECTION_THRESHOLD = 40_000;\n\n/**\n * Layer a channel sender's own row over the project's shared row. The sender\n * wins only for knobs they saved; an unsaved (`null`) knob keeps the project's\n * value rather than resetting to Auto or the built-in default, because the\n * project's choice may be the only credentialed one.\n */\nexport function layerPersonalMemorySettings(\n project: MemorySettingsRecord | null,\n personal: MemorySettingsRecord | null,\n): MemorySettingsRecord | null {\n if (!personal) return project;\n if (!project) return personal;\n return {\n ...project,\n observerModelId: personal.observerModelId ?? project.observerModelId,\n reflectorModelId: personal.reflectorModelId ?? project.reflectorModelId,\n observationThreshold: personal.observationThreshold ?? project.observationThreshold,\n reflectionThreshold: personal.reflectionThreshold ?? project.reflectionThreshold,\n observeAttachments: personal.observeAttachments ?? project.observeAttachments,\n };\n}\n\nexport interface MemorySettingsHydrationSession {\n readonly identity: { getId(): string };\n state: {\n get: () => MastraCodeState | undefined;\n set: (updates: Partial<MastraCodeState>) => Promise<void> | void;\n };\n}\n\nexport interface MemorySettingsHydrationDependencies {\n /** GitHub-integration source-control rows — the only creator of web user sessions today. */\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n}\n\n/**\n * Seed a freshly created controller session's tenant org from its source-control\n * row. Memory settings remain DB-authoritative and are loaded into request\n * context for each invocation instead of being copied into mutable session\n * state.\n */\nexport async function hydrateSessionMemorySettings(\n session: MemorySettingsHydrationSession,\n { sourceControl }: MemorySettingsHydrationDependencies,\n): Promise<void> {\n const state: Partial<MastraCodeState> = session.state.get() ?? {};\n const isFactoryRun = Boolean(state.factoryProjectId);\n if (isFactoryRun && hasResolvedOrg(state.factoryOrgId)) return;\n\n try {\n const record = await sourceControl.sessions.getBySessionId(session.identity.getId());\n await seedSessionOrg(session, record?.orgId);\n } catch (error) {\n console.warn('[Factory session hydration] Unable to resolve the session organization.', error);\n if (!session.state.get()?.factoryOrgId) await seedSessionOrg(session, undefined);\n }\n}\n"],"mappings":";;;AAOA,MAAa,gCAAgC;AAC7C,MAAa,+BAA+B;;;;;;;AAQ5C,SAAgB,4BACd,SACA,UAC6B;CAC7B,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EACL,GAAG;EACH,iBAAiB,SAAS,mBAAmB,QAAQ;EACrD,kBAAkB,SAAS,oBAAoB,QAAQ;EACvD,sBAAsB,SAAS,wBAAwB,QAAQ;EAC/D,qBAAqB,SAAS,uBAAuB,QAAQ;EAC7D,oBAAoB,SAAS,sBAAsB,QAAQ;CAC7D;AACF;;;;;;;AAuBA,eAAsB,6BACpB,SACA,EAAE,iBACa;CACf,MAAM,QAAkC,QAAQ,MAAM,IAAI,KAAK,CAAC;CAEhE,IADqB,QAAQ,MAAM,gBACpB,KAAK,eAAe,MAAM,YAAY,GAAG;CAExD,IAAI;EAEF,MAAM,eAAe,UAAS,MADT,cAAc,SAAS,eAAe,QAAQ,SAAS,MAAM,CAAC,EAAA,EAC7C,KAAK;CAC7C,SAAS,OAAO;EACd,QAAQ,KAAK,2EAA2E,KAAK;EAC7F,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,EAAE,cAAc,MAAM,eAAe,SAAS,KAAA,CAAS;CACjF;AACF"}
@@ -0,0 +1,15 @@
1
+ import type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';
2
+ /**
3
+ * Stamps each assistant reply with the credential that answered it, as
4
+ * `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
5
+ * and, for a personal credential, `content.metadata.credentialOwnerId`. A
6
+ * reply without an owner id was answered by the organization's credential.
7
+ * Stamped at send time so a reply keeps its label after the thread's
8
+ * credential scope changes.
9
+ */
10
+ export declare class FactoryReplyCredentialProcessor implements Processor {
11
+ readonly id = "factory-reply-credential";
12
+ readonly name = "Factory reply credential";
13
+ processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs): import("@mastra/core/agent").MessageList;
14
+ }
15
+ //# sourceMappingURL=reply-credential-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply-credential-processor.d.ts","sourceRoot":"","sources":["../../src/session/reply-credential-processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAIhF;;;;;;;GAOG;AACH,qBAAa,+BAAgC,YAAW,SAAS;IAC/D,QAAQ,CAAC,EAAE,8BAA8B;IACzC,QAAQ,CAAC,IAAI,8BAA8B;IAE3C,iBAAiB,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,qBAAqB;CAkBzE"}
@@ -0,0 +1,38 @@
1
+ import { credentialLabel, getAuthProviderId } from "../routes/provider-credentials.js";
2
+ import { resolveCredentialStore, resolveTenantFromRequestContext } from "@mastra/code-sdk/agents/credential-resolver";
3
+ import { normalizeLegacyProviderId } from "@mastra/code-sdk/providers/model-ids";
4
+ //#region src/session/reply-credential-processor.ts
5
+ /**
6
+ * Stamps each assistant reply with the credential that answered it, as
7
+ * `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
8
+ * and, for a personal credential, `content.metadata.credentialOwnerId`. A
9
+ * reply without an owner id was answered by the organization's credential.
10
+ * Stamped at send time so a reply keeps its label after the thread's
11
+ * credential scope changes.
12
+ */
13
+ var FactoryReplyCredentialProcessor = class {
14
+ id = "factory-reply-credential";
15
+ name = "Factory reply credential";
16
+ processOutputStep({ messageList, requestContext }) {
17
+ const tenant = resolveTenantFromRequestContext(requestContext);
18
+ const store = resolveCredentialStore(requestContext);
19
+ if (!tenant || !store) return messageList;
20
+ for (const message of messageList.get.response.db()) {
21
+ const metadata = message.content.metadata;
22
+ if (message.role !== "assistant" || !metadata || typeof metadata.provider !== "string") continue;
23
+ if (metadata.credentialLabel !== void 0) continue;
24
+ const [sdkProviderRoot = metadata.provider] = metadata.provider.split(".");
25
+ const provider = normalizeLegacyProviderId(sdkProviderRoot);
26
+ const credential = store.get(getAuthProviderId(provider));
27
+ const label = credential && credentialLabel(provider, credential.type);
28
+ if (!label) continue;
29
+ metadata.credentialLabel = label;
30
+ if (tenant.credentialScope === "user") metadata.credentialOwnerId = tenant.userId;
31
+ }
32
+ return messageList;
33
+ }
34
+ };
35
+ //#endregion
36
+ export { FactoryReplyCredentialProcessor };
37
+
38
+ //# sourceMappingURL=reply-credential-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply-credential-processor.js","names":[],"sources":["../../src/session/reply-credential-processor.ts"],"sourcesContent":["import { resolveCredentialStore, resolveTenantFromRequestContext } from '@mastra/code-sdk/agents/credential-resolver';\nimport { normalizeLegacyProviderId } from '@mastra/code-sdk/providers/model-ids';\nimport type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';\n\nimport { credentialLabel, getAuthProviderId } from '../routes/provider-credentials.js';\n\n/**\n * Stamps each assistant reply with the credential that answered it, as\n * `content.metadata.credentialLabel` (\"Claude Pro/Max\", \"Anthropic API key\")\n * and, for a personal credential, `content.metadata.credentialOwnerId`. A\n * reply without an owner id was answered by the organization's credential.\n * Stamped at send time so a reply keeps its label after the thread's\n * credential scope changes.\n */\nexport class FactoryReplyCredentialProcessor implements Processor {\n readonly id = 'factory-reply-credential';\n readonly name = 'Factory reply credential';\n\n processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs) {\n const tenant = resolveTenantFromRequestContext(requestContext);\n const store = resolveCredentialStore(requestContext);\n if (!tenant || !store) return messageList;\n for (const message of messageList.get.response.db()) {\n const metadata = message.content.metadata;\n if (message.role !== 'assistant' || !metadata || typeof metadata.provider !== 'string') continue;\n if (metadata.credentialLabel !== undefined) continue;\n const [sdkProviderRoot = metadata.provider] = metadata.provider.split('.');\n const provider = normalizeLegacyProviderId(sdkProviderRoot);\n const credential = store.get(getAuthProviderId(provider));\n const label = credential && credentialLabel(provider, credential.type);\n if (!label) continue;\n metadata.credentialLabel = label;\n if (tenant.credentialScope === 'user') metadata.credentialOwnerId = tenant.userId;\n }\n return messageList;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,IAAa,kCAAb,MAAkE;CAChE,KAAc;CACd,OAAgB;CAEhB,kBAAkB,EAAE,aAAa,kBAAyC;EACxE,MAAM,SAAS,gCAAgC,cAAc;EAC7D,MAAM,QAAQ,uBAAuB,cAAc;EACnD,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO;EAC9B,KAAK,MAAM,WAAW,YAAY,IAAI,SAAS,GAAG,GAAG;GACnD,MAAM,WAAW,QAAQ,QAAQ;GACjC,IAAI,QAAQ,SAAS,eAAe,CAAC,YAAY,OAAO,SAAS,aAAa,UAAU;GACxF,IAAI,SAAS,oBAAoB,KAAA,GAAW;GAC5C,MAAM,CAAC,kBAAkB,SAAS,YAAY,SAAS,SAAS,MAAM,GAAG;GACzE,MAAM,WAAW,0BAA0B,eAAe;GAC1D,MAAM,aAAa,MAAM,IAAI,kBAAkB,QAAQ,CAAC;GACxD,MAAM,QAAQ,cAAc,gBAAgB,UAAU,WAAW,IAAI;GACrE,IAAI,CAAC,OAAO;GACZ,SAAS,kBAAkB;GAC3B,IAAI,OAAO,oBAAoB,QAAQ,SAAS,oBAAoB,OAAO;EAC7E;EACA,OAAO;CACT;AACF"}
@@ -0,0 +1,87 @@
1
+ import type { StorageThreadType } from '@mastra/core/memory';
2
+ import type { MemoryStorage } from '@mastra/core/storage';
3
+ import type { SourceControlSession } from '../storage/domains/source-control/base.js';
4
+ import type { WorkItemRow } from '../storage/domains/work-items/base.js';
5
+ export interface SessionResourceMigrationDeps {
6
+ sessions: {
7
+ listSessionsAwaitingResourceMigration(): Promise<SourceControlSession[]>;
8
+ markResourcesMigrated(sessionId: string): Promise<void>;
9
+ };
10
+ workItems: {
11
+ listRunBoundItems(sessionId: string): Promise<(Pick<WorkItemRow, 'externalSource' | 'metadata'> | null)[]>;
12
+ moveRunBindingsToResource(input: {
13
+ sessionId: string;
14
+ resourceId: string;
15
+ }): Promise<number>;
16
+ };
17
+ /** Absent when the integrations domain is not ready; subscriptions then stay where they are. */
18
+ integrations?: {
19
+ moveSubscriptionsToResource(input: {
20
+ fromResourceId: string;
21
+ toResourceId: string;
22
+ }): Promise<number>;
23
+ };
24
+ memory: Pick<MemoryStorage, 'listThreads' | 'patchThread' | 'getObservationalMemory' | 'supportsObservationalMemory'>;
25
+ /**
26
+ * Moves a thread with everything keyed by its resource, such as observation vectors. Pass the
27
+ * sessions' `Memory`; the bare memory store only moves the thread and its messages.
28
+ */
29
+ threads: {
30
+ updateThreadResourceId(input: {
31
+ threadId: string;
32
+ resourceId: string;
33
+ }): Promise<StorageThreadType>;
34
+ };
35
+ }
36
+ export interface SessionResourceMigrationReport {
37
+ sessions: number;
38
+ factorySessions: number;
39
+ userSessions: number;
40
+ /** Runs on untrusted content; they keep their own resource. */
41
+ isolatedSessions: number;
42
+ /** Factory-owned sessions with no Factory to move them onto; left untouched. */
43
+ skipped: string[];
44
+ /** Sessions whose move threw; the next startup retries them. */
45
+ failed: string[];
46
+ /**
47
+ * Moved sessions whose subscriptions couldn't move because the integrations
48
+ * domain wasn't ready; left unmarked so a later startup moves them.
49
+ */
50
+ subscriptionsPending: string[];
51
+ threads: number;
52
+ runBindings: number;
53
+ subscriptions: number;
54
+ /** Moved sessions whose resource-scoped observational memory stays under their old resource. */
55
+ resourceObservationsLeft: string[];
56
+ }
57
+ /**
58
+ * The memory resource a session belongs to: its Factory for runs, side
59
+ * sessions and Slack channel threads, its owner for sessions a person opened,
60
+ * private side sessions and Slack DMs. A run on untrusted content (a pull request, or a card
61
+ * written outside the write-access circle) keeps its own resource, like a new
62
+ * run would; so does a run whose work item no longer loads, since nothing then
63
+ * shows its content was trusted. `null` when a Factory-owned session has no
64
+ * Factory recorded — guessing would merge it into the wrong resource.
65
+ */
66
+ export declare function sessionMemoryResource(session: SourceControlSession, boundItems: (Pick<WorkItemRow, 'externalSource' | 'metadata'> | null)[]): {
67
+ owner: 'factory' | 'user' | 'isolated';
68
+ resourceId: string | null;
69
+ };
70
+ /**
71
+ * Move sessions stored under the legacy `resourceId = sessionId` onto their
72
+ * Factory or user resource. Threads keep their id (`threadId === sessionId`)
73
+ * and get `factorySessionId` so per-session listings still find them.
74
+ *
75
+ * Threads move through the sessions' `Memory`, so their observation vectors
76
+ * follow them and recall search finds their past observations under the new
77
+ * resource. Every write only touches values still equal to the session id, so
78
+ * a rerun is a no-op and a crash leaves a state the next run resumes from. A
79
+ * session is marked once moved, so later starts only read sessions that are not
80
+ * (new sessions are created marked).
81
+ * Nothing is deleted. Thread-scoped observational memory is keyed by thread id
82
+ * and keeps working. Resource-scoped observational memory written under
83
+ * `resource:<sessionId>` stays under that key and is no longer read: several
84
+ * sessions merge into one resource, and their records cannot be merged.
85
+ */
86
+ export declare function migrateSessionResources(deps: SessionResourceMigrationDeps): Promise<SessionResourceMigrationReport>;
87
+ //# sourceMappingURL=resource-migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-migration.d.ts","sourceRoot":"","sources":["../../src/session/resource-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAazE,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE;QACR,qCAAqC,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;QACzE,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACzD,CAAC;IACF,SAAS,EAAE;QACT,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,gBAAgB,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3G,yBAAyB,CAAC,KAAK,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KAC9F,CAAC;IACF,gGAAgG;IAChG,YAAY,CAAC,EAAE;QACb,2BAA2B,CAAC,KAAK,EAAE;YAAE,cAAc,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KACvG,CAAC;IACF,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,GAAG,wBAAwB,GAAG,6BAA6B,CAAC,CAAC;IACtH;;;OAGG;IACH,OAAO,EAAE;QAAE,sBAAsB,CAAC,KAAK,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;KAAE,CAAC;CAClH;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gEAAgE;IAChE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,wBAAwB,EAAE,MAAM,EAAE,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,gBAAgB,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,EAAE,GACtE;IAAE,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAYvE;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,8BAA8B,CAAC,CAwGzC"}
@@ -0,0 +1,163 @@
1
+ import { externallyAuthoredWorkItem } from "../rules/types.js";
2
+ import { sessionStampCounts } from "./session-threads.js";
3
+ //#region src/session/resource-migration.ts
4
+ /** Slack-started sessions live on a `slack/<thread>` branch (see `threadBranch` in the Slack integration). */
5
+ const SLACK_SESSION_BRANCH_PREFIX = "slack/";
6
+ /**
7
+ * Set on a thread while it moves and turned off once the move, vectors included, is done. A
8
+ * crash in between leaves the thread moved in storage but no longer listed under its
9
+ * session id, so the next start finds it by this flag and its session stamp and finishes the move.
10
+ */
11
+ const MOVE_PENDING_KEY = "factoryResourceMovePending";
12
+ /**
13
+ * The memory resource a session belongs to: its Factory for runs, side
14
+ * sessions and Slack channel threads, its owner for sessions a person opened,
15
+ * private side sessions and Slack DMs. A run on untrusted content (a pull request, or a card
16
+ * written outside the write-access circle) keeps its own resource, like a new
17
+ * run would; so does a run whose work item no longer loads, since nothing then
18
+ * shows its content was trusted. `null` when a Factory-owned session has no
19
+ * Factory recorded — guessing would merge it into the wrong resource.
20
+ */
21
+ function sessionMemoryResource(session, boundItems) {
22
+ if (boundItems.some((item) => item === null || untrustedWorkItem(item))) return {
23
+ owner: "isolated",
24
+ resourceId: session.sessionId
25
+ };
26
+ const sharedWithOrg = session.visibility !== "private" && (session.originSessionId !== null || session.branch.startsWith(SLACK_SESSION_BRANCH_PREFIX));
27
+ return boundItems.length > 0 || sharedWithOrg ? {
28
+ owner: "factory",
29
+ resourceId: session.factoryProjectId
30
+ } : {
31
+ owner: "user",
32
+ resourceId: session.userId
33
+ };
34
+ }
35
+ function untrustedWorkItem(item) {
36
+ return item.externalSource?.type === "pull-request" || externallyAuthoredWorkItem(item);
37
+ }
38
+ /**
39
+ * Move sessions stored under the legacy `resourceId = sessionId` onto their
40
+ * Factory or user resource. Threads keep their id (`threadId === sessionId`)
41
+ * and get `factorySessionId` so per-session listings still find them.
42
+ *
43
+ * Threads move through the sessions' `Memory`, so their observation vectors
44
+ * follow them and recall search finds their past observations under the new
45
+ * resource. Every write only touches values still equal to the session id, so
46
+ * a rerun is a no-op and a crash leaves a state the next run resumes from. A
47
+ * session is marked once moved, so later starts only read sessions that are not
48
+ * (new sessions are created marked).
49
+ * Nothing is deleted. Thread-scoped observational memory is keyed by thread id
50
+ * and keeps working. Resource-scoped observational memory written under
51
+ * `resource:<sessionId>` stays under that key and is no longer read: several
52
+ * sessions merge into one resource, and their records cannot be merged.
53
+ */
54
+ async function migrateSessionResources(deps) {
55
+ const report = {
56
+ sessions: 0,
57
+ factorySessions: 0,
58
+ userSessions: 0,
59
+ isolatedSessions: 0,
60
+ skipped: [],
61
+ failed: [],
62
+ subscriptionsPending: [],
63
+ threads: 0,
64
+ runBindings: 0,
65
+ subscriptions: 0,
66
+ resourceObservationsLeft: []
67
+ };
68
+ const sessions = await deps.sessions.listSessionsAwaitingResourceMigration();
69
+ for (const session of sessions) {
70
+ report.sessions += 1;
71
+ const { sessionId } = session;
72
+ try {
73
+ const { owner, resourceId } = sessionMemoryResource(session, await deps.workItems.listRunBoundItems(sessionId));
74
+ if (!resourceId) {
75
+ report.skipped.push(sessionId);
76
+ continue;
77
+ }
78
+ if (owner === "factory") report.factorySessions += 1;
79
+ else if (owner === "isolated") report.isolatedSessions += 1;
80
+ else report.userSessions += 1;
81
+ if (resourceId === sessionId) {
82
+ await deps.sessions.markResourcesMigrated(sessionId);
83
+ continue;
84
+ }
85
+ const { threads: interrupted } = await deps.memory.listThreads({
86
+ filter: {
87
+ resourceId,
88
+ metadata: {
89
+ factorySessionId: sessionId,
90
+ [MOVE_PENDING_KEY]: true
91
+ }
92
+ },
93
+ perPage: false
94
+ });
95
+ for (const thread of interrupted) {
96
+ if (thread.resourceId === sessionId) continue;
97
+ await moveThread(deps, thread, thread.resourceId);
98
+ report.threads += 1;
99
+ }
100
+ const { threads } = await deps.memory.listThreads({
101
+ filter: { resourceId: sessionId },
102
+ perPage: false
103
+ });
104
+ const stampsCount = sessionStampCounts(session, resourceId);
105
+ for (const thread of threads) {
106
+ if (!stampsCount && thread.id !== sessionId) continue;
107
+ const metadata = {
108
+ ...thread.metadata,
109
+ factorySessionId: sessionId,
110
+ [MOVE_PENDING_KEY]: true
111
+ };
112
+ await deps.memory.patchThread({
113
+ id: thread.id,
114
+ metadata
115
+ });
116
+ await moveThread(deps, {
117
+ ...thread,
118
+ metadata
119
+ }, resourceId);
120
+ report.threads += 1;
121
+ }
122
+ report.runBindings += await deps.workItems.moveRunBindingsToResource({
123
+ sessionId,
124
+ resourceId
125
+ });
126
+ if (deps.integrations) report.subscriptions += await deps.integrations.moveSubscriptionsToResource({
127
+ fromResourceId: sessionId,
128
+ toResourceId: resourceId
129
+ });
130
+ if (threads.length > 0 && deps.memory.supportsObservationalMemory && await deps.memory.getObservationalMemory(null, sessionId)) {
131
+ report.resourceObservationsLeft.push(sessionId);
132
+ console.warn(`[factory] session resource migration: session ${sessionId} has resource-scoped observational memory; it stays under resource ${sessionId} and is not carried into ${resourceId}`);
133
+ }
134
+ if (!deps.integrations) {
135
+ report.subscriptionsPending.push(sessionId);
136
+ continue;
137
+ }
138
+ await deps.sessions.markResourcesMigrated(sessionId);
139
+ } catch (error) {
140
+ report.failed.push(sessionId);
141
+ console.warn(`[factory] session resource migration: session ${sessionId} not moved: ${error instanceof Error ? error.message : error}`);
142
+ }
143
+ }
144
+ if (report.threads + report.runBindings + report.subscriptions + report.skipped.length + report.failed.length + report.subscriptionsPending.length > 0) console.info(`[factory] session resource migration: ${report.threads} threads, ${report.runBindings} run bindings, ${report.subscriptions} subscriptions moved; ${report.skipped.length} Factory sessions without a Factory skipped; ${report.failed.length} sessions failed; ${report.subscriptionsPending.length} sessions wait for integrations to move their subscriptions`);
145
+ return report;
146
+ }
147
+ async function moveThread(deps, thread, resourceId) {
148
+ await deps.threads.updateThreadResourceId({
149
+ threadId: thread.id,
150
+ resourceId
151
+ });
152
+ await deps.memory.patchThread({
153
+ id: thread.id,
154
+ metadata: {
155
+ ...thread.metadata,
156
+ [MOVE_PENDING_KEY]: false
157
+ }
158
+ });
159
+ }
160
+ //#endregion
161
+ export { migrateSessionResources, sessionMemoryResource };
162
+
163
+ //# sourceMappingURL=resource-migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-migration.js","names":[],"sources":["../../src/session/resource-migration.ts"],"sourcesContent":["import type { StorageThreadType } from '@mastra/core/memory';\nimport type { MemoryStorage } from '@mastra/core/storage';\n\nimport { externallyAuthoredWorkItem } from '../rules/types.js';\nimport type { SourceControlSession } from '../storage/domains/source-control/base.js';\nimport type { WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { sessionStampCounts } from './session-threads.js';\n\n/** Slack-started sessions live on a `slack/<thread>` branch (see `threadBranch` in the Slack integration). */\nconst SLACK_SESSION_BRANCH_PREFIX = 'slack/';\n\n/**\n * Set on a thread while it moves and turned off once the move, vectors included, is done. A\n * crash in between leaves the thread moved in storage but no longer listed under its\n * session id, so the next start finds it by this flag and its session stamp and finishes the move.\n */\nconst MOVE_PENDING_KEY = 'factoryResourceMovePending';\n\nexport interface SessionResourceMigrationDeps {\n sessions: {\n listSessionsAwaitingResourceMigration(): Promise<SourceControlSession[]>;\n markResourcesMigrated(sessionId: string): Promise<void>;\n };\n workItems: {\n listRunBoundItems(sessionId: string): Promise<(Pick<WorkItemRow, 'externalSource' | 'metadata'> | null)[]>;\n moveRunBindingsToResource(input: { sessionId: string; resourceId: string }): Promise<number>;\n };\n /** Absent when the integrations domain is not ready; subscriptions then stay where they are. */\n integrations?: {\n moveSubscriptionsToResource(input: { fromResourceId: string; toResourceId: string }): Promise<number>;\n };\n memory: Pick<MemoryStorage, 'listThreads' | 'patchThread' | 'getObservationalMemory' | 'supportsObservationalMemory'>;\n /**\n * Moves a thread with everything keyed by its resource, such as observation vectors. Pass the\n * sessions' `Memory`; the bare memory store only moves the thread and its messages.\n */\n threads: { updateThreadResourceId(input: { threadId: string; resourceId: string }): Promise<StorageThreadType> };\n}\n\nexport interface SessionResourceMigrationReport {\n sessions: number;\n factorySessions: number;\n userSessions: number;\n /** Runs on untrusted content; they keep their own resource. */\n isolatedSessions: number;\n /** Factory-owned sessions with no Factory to move them onto; left untouched. */\n skipped: string[];\n /** Sessions whose move threw; the next startup retries them. */\n failed: string[];\n /**\n * Moved sessions whose subscriptions couldn't move because the integrations\n * domain wasn't ready; left unmarked so a later startup moves them.\n */\n subscriptionsPending: string[];\n threads: number;\n runBindings: number;\n subscriptions: number;\n /** Moved sessions whose resource-scoped observational memory stays under their old resource. */\n resourceObservationsLeft: string[];\n}\n\n/**\n * The memory resource a session belongs to: its Factory for runs, side\n * sessions and Slack channel threads, its owner for sessions a person opened,\n * private side sessions and Slack DMs. A run on untrusted content (a pull request, or a card\n * written outside the write-access circle) keeps its own resource, like a new\n * run would; so does a run whose work item no longer loads, since nothing then\n * shows its content was trusted. `null` when a Factory-owned session has no\n * Factory recorded — guessing would merge it into the wrong resource.\n */\nexport function sessionMemoryResource(\n session: SourceControlSession,\n boundItems: (Pick<WorkItemRow, 'externalSource' | 'metadata'> | null)[],\n): { owner: 'factory' | 'user' | 'isolated'; resourceId: string | null } {\n if (boundItems.some(item => item === null || untrustedWorkItem(item))) {\n return { owner: 'isolated', resourceId: session.sessionId };\n }\n // A Slack DM or private side session is its owner's, so it belongs with their own sessions.\n const sharedWithOrg =\n session.visibility !== 'private' &&\n (session.originSessionId !== null || session.branch.startsWith(SLACK_SESSION_BRANCH_PREFIX));\n const factoryOwned = boundItems.length > 0 || sharedWithOrg;\n return factoryOwned\n ? { owner: 'factory', resourceId: session.factoryProjectId }\n : { owner: 'user', resourceId: session.userId };\n}\n\nfunction untrustedWorkItem(item: Pick<WorkItemRow, 'externalSource' | 'metadata'>): boolean {\n return item.externalSource?.type === 'pull-request' || externallyAuthoredWorkItem(item);\n}\n\n/**\n * Move sessions stored under the legacy `resourceId = sessionId` onto their\n * Factory or user resource. Threads keep their id (`threadId === sessionId`)\n * and get `factorySessionId` so per-session listings still find them.\n *\n * Threads move through the sessions' `Memory`, so their observation vectors\n * follow them and recall search finds their past observations under the new\n * resource. Every write only touches values still equal to the session id, so\n * a rerun is a no-op and a crash leaves a state the next run resumes from. A\n * session is marked once moved, so later starts only read sessions that are not\n * (new sessions are created marked).\n * Nothing is deleted. Thread-scoped observational memory is keyed by thread id\n * and keeps working. Resource-scoped observational memory written under\n * `resource:<sessionId>` stays under that key and is no longer read: several\n * sessions merge into one resource, and their records cannot be merged.\n */\nexport async function migrateSessionResources(\n deps: SessionResourceMigrationDeps,\n): Promise<SessionResourceMigrationReport> {\n const report: SessionResourceMigrationReport = {\n sessions: 0,\n factorySessions: 0,\n userSessions: 0,\n isolatedSessions: 0,\n skipped: [],\n failed: [],\n subscriptionsPending: [],\n threads: 0,\n runBindings: 0,\n subscriptions: 0,\n resourceObservationsLeft: [],\n };\n const sessions = await deps.sessions.listSessionsAwaitingResourceMigration();\n\n for (const session of sessions) {\n report.sessions += 1;\n const { sessionId } = session;\n try {\n const { owner, resourceId } = sessionMemoryResource(session, await deps.workItems.listRunBoundItems(sessionId));\n if (!resourceId) {\n report.skipped.push(sessionId);\n continue;\n }\n if (owner === 'factory') report.factorySessions += 1;\n else if (owner === 'isolated') report.isolatedSessions += 1;\n else report.userSessions += 1;\n if (resourceId === sessionId) {\n await deps.sessions.markResourcesMigrated(sessionId);\n continue;\n }\n\n // Finish moves a crash interrupted after storage moved the thread.\n const { threads: interrupted } = await deps.memory.listThreads({\n filter: { resourceId, metadata: { factorySessionId: sessionId, [MOVE_PENDING_KEY]: true } },\n perPage: false,\n });\n for (const thread of interrupted) {\n if (thread.resourceId === sessionId) continue; // listed under the session id below\n await moveThread(deps, thread, thread.resourceId);\n report.threads += 1;\n }\n const { threads } = await deps.memory.listThreads({ filter: { resourceId: sessionId }, perPage: false });\n // A private session's stamps don't count under its Factory, so only its own\n // thread moves there; its other threads stay listed under its id.\n const stampsCount = sessionStampCounts(session, resourceId);\n for (const thread of threads) {\n if (!stampsCount && thread.id !== sessionId) continue;\n // Tag before moving: once moved, the thread is no longer found under the session id.\n const metadata = { ...thread.metadata, factorySessionId: sessionId, [MOVE_PENDING_KEY]: true };\n await deps.memory.patchThread({ id: thread.id, metadata });\n await moveThread(deps, { ...thread, metadata }, resourceId);\n report.threads += 1;\n }\n report.runBindings += await deps.workItems.moveRunBindingsToResource({ sessionId, resourceId });\n if (deps.integrations) {\n report.subscriptions += await deps.integrations.moveSubscriptionsToResource({\n fromResourceId: sessionId,\n toResourceId: resourceId,\n });\n }\n // Reported once, on the run that moves the session's threads.\n if (\n threads.length > 0 &&\n deps.memory.supportsObservationalMemory &&\n (await deps.memory.getObservationalMemory(null, sessionId))\n ) {\n report.resourceObservationsLeft.push(sessionId);\n console.warn(\n `[factory] session resource migration: session ${sessionId} has resource-scoped observational memory; ` +\n `it stays under resource ${sessionId} and is not carried into ${resourceId}`,\n );\n }\n if (!deps.integrations) {\n report.subscriptionsPending.push(sessionId);\n continue;\n }\n await deps.sessions.markResourcesMigrated(sessionId);\n } catch (error) {\n report.failed.push(sessionId);\n console.warn(\n `[factory] session resource migration: session ${sessionId} not moved: ${error instanceof Error ? error.message : error}`,\n );\n }\n }\n\n if (\n report.threads +\n report.runBindings +\n report.subscriptions +\n report.skipped.length +\n report.failed.length +\n report.subscriptionsPending.length >\n 0\n ) {\n console.info(\n `[factory] session resource migration: ${report.threads} threads, ${report.runBindings} run bindings, ` +\n `${report.subscriptions} subscriptions moved; ${report.skipped.length} Factory sessions without a Factory skipped; ` +\n `${report.failed.length} sessions failed; ${report.subscriptionsPending.length} sessions wait for integrations ` +\n `to move their subscriptions`,\n );\n }\n return report;\n}\n\nasync function moveThread(\n deps: SessionResourceMigrationDeps,\n thread: StorageThreadType,\n resourceId: string,\n): Promise<void> {\n await deps.threads.updateThreadResourceId({ threadId: thread.id, resourceId });\n // Stores merge thread metadata, so the flag is switched off rather than removed.\n await deps.memory.patchThread({ id: thread.id, metadata: { ...thread.metadata, [MOVE_PENDING_KEY]: false } });\n}\n"],"mappings":";;;;AASA,MAAM,8BAA8B;;;;;;AAOpC,MAAM,mBAAmB;;;;;;;;;;AAsDzB,SAAgB,sBACd,SACA,YACuE;CACvE,IAAI,WAAW,MAAK,SAAQ,SAAS,QAAQ,kBAAkB,IAAI,CAAC,GAClE,OAAO;EAAE,OAAO;EAAY,YAAY,QAAQ;CAAU;CAG5D,MAAM,gBACJ,QAAQ,eAAe,cACtB,QAAQ,oBAAoB,QAAQ,QAAQ,OAAO,WAAW,2BAA2B;CAE5F,OADqB,WAAW,SAAS,KAAK,gBAE1C;EAAE,OAAO;EAAW,YAAY,QAAQ;CAAiB,IACzD;EAAE,OAAO;EAAQ,YAAY,QAAQ;CAAO;AAClD;AAEA,SAAS,kBAAkB,MAAiE;CAC1F,OAAO,KAAK,gBAAgB,SAAS,kBAAkB,2BAA2B,IAAI;AACxF;;;;;;;;;;;;;;;;;AAkBA,eAAsB,wBACpB,MACyC;CACzC,MAAM,SAAyC;EAC7C,UAAU;EACV,iBAAiB;EACjB,cAAc;EACd,kBAAkB;EAClB,SAAS,CAAC;EACV,QAAQ,CAAC;EACT,sBAAsB,CAAC;EACvB,SAAS;EACT,aAAa;EACb,eAAe;EACf,0BAA0B,CAAC;CAC7B;CACA,MAAM,WAAW,MAAM,KAAK,SAAS,sCAAsC;CAE3E,KAAK,MAAM,WAAW,UAAU;EAC9B,OAAO,YAAY;EACnB,MAAM,EAAE,cAAc;EACtB,IAAI;GACF,MAAM,EAAE,OAAO,eAAe,sBAAsB,SAAS,MAAM,KAAK,UAAU,kBAAkB,SAAS,CAAC;GAC9G,IAAI,CAAC,YAAY;IACf,OAAO,QAAQ,KAAK,SAAS;IAC7B;GACF;GACA,IAAI,UAAU,WAAW,OAAO,mBAAmB;QAC9C,IAAI,UAAU,YAAY,OAAO,oBAAoB;QACrD,OAAO,gBAAgB;GAC5B,IAAI,eAAe,WAAW;IAC5B,MAAM,KAAK,SAAS,sBAAsB,SAAS;IACnD;GACF;GAGA,MAAM,EAAE,SAAS,gBAAgB,MAAM,KAAK,OAAO,YAAY;IAC7D,QAAQ;KAAE;KAAY,UAAU;MAAE,kBAAkB;OAAY,mBAAmB;KAAK;IAAE;IAC1F,SAAS;GACX,CAAC;GACD,KAAK,MAAM,UAAU,aAAa;IAChC,IAAI,OAAO,eAAe,WAAW;IACrC,MAAM,WAAW,MAAM,QAAQ,OAAO,UAAU;IAChD,OAAO,WAAW;GACpB;GACA,MAAM,EAAE,YAAY,MAAM,KAAK,OAAO,YAAY;IAAE,QAAQ,EAAE,YAAY,UAAU;IAAG,SAAS;GAAM,CAAC;GAGvG,MAAM,cAAc,mBAAmB,SAAS,UAAU;GAC1D,KAAK,MAAM,UAAU,SAAS;IAC5B,IAAI,CAAC,eAAe,OAAO,OAAO,WAAW;IAE7C,MAAM,WAAW;KAAE,GAAG,OAAO;KAAU,kBAAkB;MAAY,mBAAmB;IAAK;IAC7F,MAAM,KAAK,OAAO,YAAY;KAAE,IAAI,OAAO;KAAI;IAAS,CAAC;IACzD,MAAM,WAAW,MAAM;KAAE,GAAG;KAAQ;IAAS,GAAG,UAAU;IAC1D,OAAO,WAAW;GACpB;GACA,OAAO,eAAe,MAAM,KAAK,UAAU,0BAA0B;IAAE;IAAW;GAAW,CAAC;GAC9F,IAAI,KAAK,cACP,OAAO,iBAAiB,MAAM,KAAK,aAAa,4BAA4B;IAC1E,gBAAgB;IAChB,cAAc;GAChB,CAAC;GAGH,IACE,QAAQ,SAAS,KACjB,KAAK,OAAO,+BACX,MAAM,KAAK,OAAO,uBAAuB,MAAM,SAAS,GACzD;IACA,OAAO,yBAAyB,KAAK,SAAS;IAC9C,QAAQ,KACN,iDAAiD,UAAU,qEAC9B,UAAU,2BAA2B,YACpE;GACF;GACA,IAAI,CAAC,KAAK,cAAc;IACtB,OAAO,qBAAqB,KAAK,SAAS;IAC1C;GACF;GACA,MAAM,KAAK,SAAS,sBAAsB,SAAS;EACrD,SAAS,OAAO;GACd,OAAO,OAAO,KAAK,SAAS;GAC5B,QAAQ,KACN,iDAAiD,UAAU,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OACpH;EACF;CACF;CAEA,IACE,OAAO,UACL,OAAO,cACP,OAAO,gBACP,OAAO,QAAQ,SACf,OAAO,OAAO,SACd,OAAO,qBAAqB,SAC9B,GAEA,QAAQ,KACN,yCAAyC,OAAO,QAAQ,YAAY,OAAO,YAAY,iBAClF,OAAO,cAAc,wBAAwB,OAAO,QAAQ,OAAO,+CACnE,OAAO,OAAO,OAAO,oBAAoB,OAAO,qBAAqB,OAAO,4DAEnF;CAEF,OAAO;AACT;AAEA,eAAe,WACb,MACA,QACA,YACe;CACf,MAAM,KAAK,QAAQ,uBAAuB;EAAE,UAAU,OAAO;EAAI;CAAW,CAAC;CAE7E,MAAM,KAAK,OAAO,YAAY;EAAE,IAAI,OAAO;EAAI,UAAU;GAAE,GAAG,OAAO;IAAW,mBAAmB;EAAM;CAAE,CAAC;AAC9G"}
@@ -37,6 +37,30 @@ export interface RunEndCaptureSession {
37
37
  type RunEndReason = Extract<AgentControllerEvent, {
38
38
  type: 'agent_end';
39
39
  }>['reason'];
40
+ /**
41
+ * JSON budget for the per-step entries on a run's audit record. Audit metadata
42
+ * is capped at 4 KB as a whole and an oversized record loses all of it, so
43
+ * later steps are dropped (and counted) instead; totals always cover every step.
44
+ */
45
+ export declare const FACTORY_RUN_USAGE_STEPS_JSON_BUDGET = 2400;
46
+ declare const USAGE_FIELDS: readonly ["promptTokens", "completionTokens", "totalTokens", "reasoningTokens", "cachedInputTokens", "cacheCreationInputTokens", "cacheCreationInputTokens5m", "cacheCreationInputTokens1h"];
47
+ type UsageCounts = Partial<Record<(typeof USAGE_FIELDS)[number], number>>;
48
+ /**
49
+ * Token usage attached to a run's `factory.run.ended` audit record.
50
+ * `unreported` means this process saw no usage for the run (it ran elsewhere,
51
+ * the process restarted, or the model reported none). It is never zero spend.
52
+ */
53
+ export type FactoryRunUsage = {
54
+ status: 'recorded';
55
+ stepCount: number;
56
+ totals: UsageCounts;
57
+ steps: UsageCounts[];
58
+ stepsTruncated?: number;
59
+ /** Other runs that ended in the same agent turn; the turn's usage can't be split between them. */
60
+ sharedWithKickoffIds?: string[];
61
+ } | {
62
+ status: 'unreported';
63
+ };
40
64
  export declare function listSessionOpenRuns(session: RunEndCaptureSession): Promise<FactoryOpenRun[]>;
41
65
  export declare function waitForSessionRunAudit(session: RunEndCaptureSession): Promise<void>;
42
66
  export declare function recordSessionRunStart(session: RunEndCaptureSession, { audit, run, actorType, observedEnd, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"run-audit.d.ts","sourceRoot":"","sources":["../../src/session/run-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;iBAqBxB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,6GAA6G;AAC7G,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAOtG;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE;QACf,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClE,CAAC;IACF,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,KAAK,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;AAWnF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGlG;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzF;AA4CD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,EACE,KAAK,EACL,GAAG,EACH,SAAS,EACT,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,YAAY,CAAC;CACjC,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,EACjB,EAAE,SAAa,GACd,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,IAAI,CAKnH"}
1
+ {"version":3,"file":"run-audit.d.ts","sourceRoot":"","sources":["../../src/session/run-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;iBAqBxB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,6GAA6G;AAC7G,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAOtG;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE;QACf,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClE,CAAC;IACF,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,KAAK,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;AAInF;;;;GAIG;AACH,eAAO,MAAM,mCAAmC,OAAO,CAAC;AAExD,QAAA,MAAM,YAAY,8LASR,CAAC;AAEX,KAAK,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kGAAkG;IAClG,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,GACD;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAoE7B,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGlG;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzF;AAsED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,EACE,KAAK,EACL,GAAG,EACH,SAAS,EACT,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,YAAY,CAAC;CACjC,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,EACjB,EAAE,SAAa,GACd,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,IAAI,CAcnH"}
@@ -29,6 +29,61 @@ function isOpenRunLiveElsewhere(run, ownerId, now = Date.now()) {
29
29
  return run.ownerId !== void 0 && run.ownerId !== ownerId && run.heartbeatAt !== void 0 && now - run.heartbeatAt < 3e4;
30
30
  }
31
31
  const pendingWrites = /* @__PURE__ */ new WeakMap();
32
+ /**
33
+ * JSON budget for the per-step entries on a run's audit record. Audit metadata
34
+ * is capped at 4 KB as a whole and an oversized record loses all of it, so
35
+ * later steps are dropped (and counted) instead; totals always cover every step.
36
+ */
37
+ const FACTORY_RUN_USAGE_STEPS_JSON_BUDGET = 2400;
38
+ const USAGE_FIELDS = [
39
+ "promptTokens",
40
+ "completionTokens",
41
+ "totalTokens",
42
+ "reasoningTokens",
43
+ "cachedInputTokens",
44
+ "cacheCreationInputTokens",
45
+ "cacheCreationInputTokens5m",
46
+ "cacheCreationInputTokens1h"
47
+ ];
48
+ function usageCounts(usage) {
49
+ const counts = {};
50
+ for (const field of USAGE_FIELDS) {
51
+ const value = usage[field];
52
+ if (typeof value === "number") counts[field] = value;
53
+ }
54
+ return counts;
55
+ }
56
+ function runUsage(steps) {
57
+ if (steps.length === 0) return { status: "unreported" };
58
+ const totals = {};
59
+ for (const step of steps) for (const field of USAGE_FIELDS) if (step[field] !== void 0) totals[field] = (totals[field] ?? 0) + step[field];
60
+ const kept = [];
61
+ let length = 2;
62
+ for (const step of steps) {
63
+ length += JSON.stringify(step).length + 1;
64
+ if (length > 2400) break;
65
+ kept.push(step);
66
+ }
67
+ return {
68
+ status: "recorded",
69
+ stepCount: steps.length,
70
+ totals,
71
+ steps: kept,
72
+ ...steps.length > kept.length ? { stepsTruncated: steps.length - kept.length } : {}
73
+ };
74
+ }
75
+ const sessionUsage = /* @__PURE__ */ new WeakMap();
76
+ function usageFor(session) {
77
+ let usage = sessionUsage.get(session);
78
+ if (!usage) {
79
+ usage = {
80
+ current: [],
81
+ held: /* @__PURE__ */ new Map()
82
+ };
83
+ sessionUsage.set(session, usage);
84
+ }
85
+ return usage;
86
+ }
32
87
  function serializeRunAudit(session, write) {
33
88
  const pending = (pendingWrites.get(session) ?? Promise.resolve()).then(write).catch((error) => console.warn("[Factory run audit] Unable to record run lifecycle.", error));
34
89
  pendingWrites.set(session, pending);
@@ -55,27 +110,47 @@ function runAuditInput(run) {
55
110
  metadata
56
111
  };
57
112
  }
58
- async function recordRunEnds(session, audit, reason, kickoffId) {
113
+ async function recordRunEnds(session, audit, reason, kickoffId, endedTurn) {
59
114
  if (!reason || reason === "suspended") return;
115
+ const usage = usageFor(session);
60
116
  const runs = await listSessionOpenRuns(session);
61
117
  if (runs.length === 0) return;
118
+ const ending = runs.filter((run) => !kickoffId || run.kickoffId === kickoffId);
119
+ const turn = endedTurn ?? usage.lastTurn;
120
+ const steps = turn && !turn.claimed ? turn.steps : [];
121
+ if (turn && ending.length > 0) turn.claimed = true;
122
+ const turnUsage = runUsage(steps);
62
123
  const remaining = [];
63
124
  for (const run of runs) {
64
- if (kickoffId && run.kickoffId !== kickoffId) {
125
+ if (!ending.includes(run)) {
65
126
  remaining.push(run);
66
127
  continue;
67
128
  }
129
+ let runUsageRecord = usage.held.get(run.kickoffId);
130
+ if (!runUsageRecord) {
131
+ const shared = ending.filter((other) => other !== run).map((other) => other.kickoffId);
132
+ runUsageRecord = turnUsage.status === "recorded" && shared.length > 0 ? {
133
+ ...turnUsage,
134
+ sharedWithKickoffIds: shared
135
+ } : turnUsage;
136
+ usage.held.set(run.kickoffId, runUsageRecord);
137
+ }
68
138
  const input = runAuditInput(run);
69
- if (!await audit.record({
139
+ if (await audit.record({
70
140
  ...input,
71
141
  actorId: `agent:${run.threadId}`,
72
142
  actorType: "agent",
73
143
  action: "factory.run.ended",
74
144
  metadata: {
75
145
  ...input.metadata,
76
- reason
146
+ reason,
147
+ usage: runUsageRecord
77
148
  }
78
- })) remaining.push(run);
149
+ })) usage.held.delete(run.kickoffId);
150
+ else {
151
+ console.warn(`[Factory run audit] Token usage for run ${run.kickoffId} (${run.role}) was not persisted; keeping it for the next attempt.`, runUsageRecord);
152
+ remaining.push(run);
153
+ }
79
154
  }
80
155
  await session.thread.setSetting({
81
156
  key: FACTORY_OPEN_RUNS_SETTING,
@@ -113,12 +188,23 @@ function heartbeatSessionOpenRun(session, kickoffId, at = Date.now()) {
113
188
  });
114
189
  }
115
190
  function observeSessionRunEnd(session, { audit }) {
191
+ const usage = usageFor(session);
116
192
  return session.subscribe((event) => {
193
+ if (event.type === "usage_update") {
194
+ usage.current.push(usageCounts(event.usage));
195
+ return;
196
+ }
117
197
  if (event.type !== "agent_end" || !event.reason || event.reason === "suspended") return;
118
- serializeRunAudit(session, () => recordRunEnds(session, audit, event.reason));
198
+ const turn = {
199
+ steps: usage.current,
200
+ claimed: false
201
+ };
202
+ usage.current = [];
203
+ usage.lastTurn = turn;
204
+ serializeRunAudit(session, () => recordRunEnds(session, audit, event.reason, void 0, turn));
119
205
  });
120
206
  }
121
207
  //#endregion
122
- export { FACTORY_OPEN_RUNS_SETTING, FACTORY_OPEN_RUN_STALE_MS, heartbeatSessionOpenRun, isOpenRunLiveElsewhere, listSessionOpenRuns, observeSessionRunEnd, recordSessionRunStart, waitForSessionRunAudit };
208
+ export { FACTORY_OPEN_RUNS_SETTING, FACTORY_OPEN_RUN_STALE_MS, FACTORY_RUN_USAGE_STEPS_JSON_BUDGET, heartbeatSessionOpenRun, isOpenRunLiveElsewhere, listSessionOpenRuns, observeSessionRunEnd, recordSessionRunStart, waitForSessionRunAudit };
123
209
 
124
210
  //# sourceMappingURL=run-audit.js.map