@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
@@ -1 +1 @@
1
- {"version":3,"file":"attention-providers.js","names":["#workItems","#spec","#boards","#scan","#receiptsFor","#linkedItems","#comments","#resolve"],"sources":["../../src/routes/attention-providers.ts"],"sourcesContent":["import type { BoardRegistry } from '../boards/index.js';\nimport { decisionRetryable, overtakenDecisionIds } from '../rules/decision-applicability.js';\n/**\n * Per-kind attention providers. Each kind owns its counts, its bounded page\n * scan, and its wire item shape; the route layer k-way merges provider pages\n * on `occurredAt desc` with one resumable cursor per kind.\n */\n\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n WorkItemCommentRow,\n WorkItemCommentsStorage,\n WorkItemMentionRow,\n} from '../storage/domains/comments/base.js';\nimport type {\n FactoryAttentionIdentity,\n FactoryAttentionKind,\n FactoryAttentionReceiptRecord,\n FactoryDeferredDecisionRecord,\n FactoryDispatchStatus,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport {\n factoryAttentionKey,\n factoryDecisionAttentionIdentity,\n factoryMentionAttentionIdentity,\n} from '../storage/domains/work-items/base.js';\n\nexport type FactoryAttentionView = 'open' | 'unread' | 'archived';\n\nexport interface AttentionScope {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n}\n\nexport interface AttentionStreamPosition {\n occurredAt: Date;\n id: string;\n}\n\nexport interface AttentionEntry {\n occurredAt: Date;\n /** Position resuming the provider's stream right after this entry. */\n resumeCursor: AttentionStreamPosition;\n item: Record<string, unknown>;\n}\n\nexport interface AttentionPageArgs {\n view: FactoryAttentionView;\n search?: string;\n before?: AttentionStreamPosition;\n limit: number;\n}\n\nexport interface AttentionPageResult {\n entries: AttentionEntry[];\n /** More rows behind the scan once every returned entry is consumed. */\n hasMore: boolean;\n /** Resume point for a scan-budget stop past the last returned entry. */\n continuation?: AttentionStreamPosition;\n}\n\nexport interface AttentionCounts {\n open: number;\n unread: number;\n}\n\nexport interface AttentionLatest {\n key: string;\n at: Date;\n unread: boolean;\n}\n\nexport interface AttentionProvider {\n kind: FactoryAttentionKind;\n counts(scope: AttentionScope): Promise<AttentionCounts>;\n latest(scope: AttentionScope): Promise<AttentionLatest | null>;\n page(scope: AttentionScope, args: AttentionPageArgs): Promise<AttentionPageResult>;\n markAllRead(\n scope: AttentionScope,\n args: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }>;\n}\n\nexport const SCAN_PAGE_SIZE = 50;\n// Receipt filtering is bounded per request; the response cursor resumes after the last scan.\nconst MAX_RECEIPT_SCAN_PAGES = 4;\n\ninterface ScanBatch<T> {\n rows: T[];\n /** Set only when the page budget stopped a scan that still had rows behind it. */\n continuation?: AttentionStreamPosition;\n}\n\n/**\n * One bounded keyset scan, shared by every provider read. Walks newest-first\n * from `before` until the stream runs dry or the page budget is spent; the\n * final batch of a budget stop carries the position to resume from.\n */\nexport async function* scanBatches<T>(\n fetchBatch: (before?: AttentionStreamPosition) => Promise<{ rows: T[]; hasMore: boolean }>,\n positionOf: (row: T) => AttentionStreamPosition,\n before?: AttentionStreamPosition,\n): AsyncGenerator<ScanBatch<T>> {\n let cursor = before;\n for (let scanned = 1; scanned <= MAX_RECEIPT_SCAN_PAGES; scanned += 1) {\n const { rows, hasMore } = await fetchBatch(cursor);\n const last = rows.at(-1);\n if (!last) return;\n const position = positionOf(last);\n yield { rows, ...(hasMore && scanned === MAX_RECEIPT_SCAN_PAGES ? { continuation: position } : {}) };\n if (!hasMore) return;\n cursor = position;\n }\n}\n\n/** Fills a page from the scan, stopping at `limit` entries or at the scan budget. */\nexport async function collectPage<T>(\n batches: AsyncGenerator<ScanBatch<T>>,\n limit: number,\n toEntries: (rows: T[]) => Promise<AttentionEntry[]>,\n): Promise<AttentionPageResult> {\n const entries: AttentionEntry[] = [];\n for await (const batch of batches) {\n for (const entry of await toEntries(batch.rows)) {\n if (entries.length === limit) return { entries, hasMore: true };\n entries.push(entry);\n }\n if (batch.continuation) return { entries, hasMore: true, continuation: batch.continuation };\n }\n return { entries, hasMore: false };\n}\n\n/** Marks every scanned batch read, reporting where a budget stop left off. */\nexport async function markScanRead<T>(\n batches: AsyncGenerator<ScanBatch<T>>,\n markBatch: (rows: T[]) => Promise<void>,\n): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n for await (const batch of batches) {\n await markBatch(batch.rows);\n if (batch.continuation) return { hasMore: true, continuation: batch.continuation };\n }\n return { hasMore: false };\n}\n\nexport function factoryDecisionType(decision: FactoryDeferredDecisionRecord): string {\n return typeof decision.decision.type === 'string' ? decision.decision.type.slice(0, 64) : 'unknown';\n}\n\nexport function matchesView(view: FactoryAttentionView, receipt: FactoryAttentionReceiptRecord | undefined): boolean {\n if (view === 'archived') return receipt?.state === 'archived';\n if (view === 'unread') return receipt === undefined;\n return receipt?.state !== 'archived';\n}\n\nfunction attentionTarget(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined) {\n if (!item) return { kind: 'rules' as const };\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : undefined;\n const session = role ? item.sessions[role] : undefined;\n if (session) {\n return {\n kind: 'thread' as const,\n sessionId: session.sessionId,\n threadId: session.threadId,\n };\n }\n return {\n kind: 'work-item' as const,\n workItemId: item.id,\n board: workItemBoard(item),\n };\n}\n\nexport function workItemBoard(item: WorkItemRow): 'review' | 'work' {\n const review = item.externalSource?.integrationId === 'github' && item.externalSource.type === 'pull-request';\n return review ? 'review' : 'work';\n}\n\n/** One deferred-decision status seen as attention: failures and parked runs differ only by this. */\nexport interface DecisionAttentionSpec {\n kind: FactoryAttentionKind;\n status: FactoryDispatchStatus;\n identity(decision: FactoryDeferredDecisionRecord): FactoryAttentionIdentity;\n occurredAt(decision: FactoryDeferredDecisionRecord): Date;\n title(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;\n detail(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;\n extra?(decision: FactoryDeferredDecisionRecord, context: { overtaken: boolean }): Record<string, unknown>;\n matches(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined, search: string): boolean;\n}\n\nexport class DecisionAttentionProvider implements AttentionProvider {\n readonly kind: FactoryAttentionKind;\n readonly #workItems: WorkItemsStorage;\n readonly #spec: DecisionAttentionSpec;\n readonly #boards: BoardRegistry;\n\n constructor(\n { workItems, boards }: { workItems: WorkItemsStorage; boards: BoardRegistry },\n spec: DecisionAttentionSpec,\n ) {\n this.#workItems = workItems;\n this.#boards = boards;\n this.#spec = spec;\n this.kind = spec.kind;\n }\n\n async counts(scope: AttentionScope): Promise<AttentionCounts> {\n const [total, receiptCount, archivedCount] = await Promise.all([\n this.#workItems.countDeferredDecisionsByStatuses({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n statuses: [this.#spec.status],\n }),\n this.#workItems.countAttentionReceipts({ ...receiptScope(scope), kind: this.kind }),\n this.#workItems.countAttentionReceipts({ ...receiptScope(scope), kind: this.kind, state: 'archived' }),\n ]);\n return {\n open: Math.max(0, total - archivedCount),\n unread: Math.max(0, total - receiptCount),\n };\n }\n\n async latest(scope: AttentionScope): Promise<AttentionLatest | null> {\n const page = await this.#workItems.listDecisionPageByStatus({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n status: this.#spec.status,\n limit: 1,\n });\n const newest = page.decisions[0];\n if (!newest) return null;\n const identity = this.#spec.identity(newest);\n const receipts = await this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: [identity],\n });\n return {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n at: this.#spec.occurredAt(newest),\n unread: receipts.length === 0,\n };\n }\n\n #scan(scope: AttentionScope, before?: AttentionStreamPosition) {\n return scanBatches<FactoryDeferredDecisionRecord>(\n async cursor => {\n const page = await this.#workItems.listDecisionPageByStatus({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n status: this.#spec.status,\n before: cursor,\n limit: SCAN_PAGE_SIZE,\n });\n return { rows: page.decisions, hasMore: page.hasMore };\n },\n decision => ({ occurredAt: this.#spec.occurredAt(decision), id: decision.id }),\n before,\n );\n }\n\n async page(scope: AttentionScope, { view, search, before, limit }: AttentionPageArgs): Promise<AttentionPageResult> {\n return collectPage(this.#scan(scope, before), limit, async decisions => {\n const [receiptByKey, itemById, overtaken] = await Promise.all([\n this.#receiptsFor(scope, decisions),\n this.#linkedItems(scope, decisions),\n overtakenDecisionIds(this.#workItems, this.#boards, scope, decisions),\n ]);\n const entries: AttentionEntry[] = [];\n for (const decision of decisions) {\n const identity = this.#spec.identity(decision);\n const receipt = receiptByKey.get(factoryAttentionKey(scope.factoryProjectId, identity));\n if (!matchesView(view, receipt)) continue;\n const item = decision.workItemId ? itemById.get(decision.workItemId) : undefined;\n if (search && !this.#spec.matches(decision, item, search)) continue;\n const occurredAt = this.#spec.occurredAt(decision);\n entries.push({\n occurredAt,\n resumeCursor: { occurredAt, id: decision.id },\n item: {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n kind: this.kind,\n decisionId: decision.id,\n occurrence: identity.occurrence,\n workItemId: decision.workItemId,\n title: this.#spec.title(decision, item),\n detail: this.#spec.detail(decision, item),\n decisionType: factoryDecisionType(decision),\n ...this.#spec.extra?.(decision, { overtaken: overtaken.has(decision.id) }),\n occurredAt: occurredAt.toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: attentionTarget(decision, item),\n },\n });\n }\n return entries;\n });\n }\n\n async #receiptsFor(\n scope: AttentionScope,\n decisions: FactoryDeferredDecisionRecord[],\n ): Promise<Map<string, FactoryAttentionReceiptRecord>> {\n const receipts = await this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: decisions.map(decision => this.#spec.identity(decision)),\n });\n return new Map(receipts.map(receipt => [factoryAttentionKey(scope.factoryProjectId, receipt), receipt]));\n }\n\n async #linkedItems(\n scope: AttentionScope,\n decisions: FactoryDeferredDecisionRecord[],\n ): Promise<Map<string, WorkItemRow>> {\n const items = await this.#workItems.listByIds({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n ids: decisions.flatMap(decision => (decision.workItemId ? [decision.workItemId] : [])),\n });\n return new Map(items.map(item => [item.id, item]));\n }\n\n async markAllRead(\n scope: AttentionScope,\n { before, now }: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n return markScanRead(this.#scan(scope, before), async decisions => {\n await this.#workItems.markAttentionReceiptsRead({\n ...receiptScope(scope),\n identities: decisions.map(decision => this.#spec.identity(decision)),\n now,\n });\n });\n }\n}\n\nexport const failedDecisionAttentionSpec: DecisionAttentionSpec = {\n kind: 'automation-failed',\n status: 'failed',\n identity: decision => factoryDecisionAttentionIdentity(decision.id, decision.failureOccurrence),\n occurredAt: decision => decision.completedAt ?? decision.updatedAt,\n title: (decision, item) => item?.title ?? factoryDispatchFailureMetadata(decision.failureCode).label,\n detail: decision => decision.lastError?.slice(0, 512) ?? factoryDispatchFailureMetadata(decision.failureCode).label,\n extra: (decision, { overtaken }) => ({\n failureCode: decision.failureCode,\n canRetry: decisionRetryable(decision, overtaken),\n }),\n matches: (decision, item, search) =>\n item?.title.toLowerCase().includes(search) === true ||\n decision.lastError?.toLowerCase().includes(search) === true ||\n factoryDecisionType(decision).toLowerCase().includes(search),\n};\n\ninterface ResolvedMention {\n mention: WorkItemMentionRow;\n comment: WorkItemCommentRow;\n item: WorkItemRow;\n identity: FactoryAttentionIdentity;\n receipt: FactoryAttentionReceiptRecord | undefined;\n}\n\nexport class MentionAttentionProvider implements AttentionProvider {\n readonly kind = 'mention' as const;\n readonly #workItems: WorkItemsStorage;\n readonly #comments: WorkItemCommentsStorage;\n\n constructor({ workItems, comments }: { workItems: WorkItemsStorage; comments: WorkItemCommentsStorage }) {\n this.#workItems = workItems;\n this.#comments = comments;\n }\n\n #scan(scope: AttentionScope, before?: AttentionStreamPosition) {\n return scanBatches<WorkItemMentionRow>(\n async cursor => {\n const rows = await this.#comments.listMentionsForUser({\n ...receiptScope(scope),\n ...(cursor ? { before: cursor } : {}),\n limit: SCAN_PAGE_SIZE + 1,\n });\n return { rows: rows.slice(0, SCAN_PAGE_SIZE), hasMore: rows.length > SCAN_PAGE_SIZE };\n },\n mention => ({ occurredAt: mention.occurredAt, id: mention.id }),\n before,\n );\n }\n\n /**\n * Mention rows joined to what the inbox needs to show one — dropping the\n * ones it cannot: a deleted comment, or a work item gone from the project.\n */\n async #resolve(scope: AttentionScope, mentions: WorkItemMentionRow[]): Promise<ResolvedMention[]> {\n const [receipts, comments, items] = await Promise.all([\n this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: mentions.map(mention => factoryMentionAttentionIdentity(mention.commentId)),\n }),\n this.#comments.listByIds({\n orgId: scope.orgId,\n ids: [...new Set(mentions.map(mention => mention.commentId))],\n }),\n this.#workItems.listByIds({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n ids: [...new Set(mentions.map(mention => mention.workItemId))],\n }),\n ]);\n const receiptByKey = new Map(\n receipts.map(receipt => [factoryAttentionKey(scope.factoryProjectId, receipt), receipt]),\n );\n const commentById = new Map(comments.map(comment => [comment.id, comment]));\n const itemById = new Map(items.map(item => [item.id, item]));\n\n const resolved: ResolvedMention[] = [];\n for (const mention of mentions) {\n const comment = commentById.get(mention.commentId);\n const item = itemById.get(mention.workItemId);\n if (!comment || comment.deletedAt || !item) continue;\n const identity = factoryMentionAttentionIdentity(mention.commentId);\n resolved.push({\n mention,\n comment,\n item,\n identity,\n receipt: receiptByKey.get(factoryAttentionKey(scope.factoryProjectId, identity)),\n });\n }\n return resolved;\n }\n\n /**\n * Derived from the same bounded scan as `page`, never from raw aggregates:\n * the badge must count exactly what the views can show, down to stopping at\n * the same scan budget. Aggregates would count orphan mention rows the list\n * can never display or clear.\n */\n async counts(scope: AttentionScope): Promise<AttentionCounts> {\n let open = 0;\n let unread = 0;\n for await (const batch of this.#scan(scope)) {\n for (const { receipt } of await this.#resolve(scope, batch.rows)) {\n if (receipt?.state !== 'archived') open += 1;\n if (receipt === undefined) unread += 1;\n }\n }\n return { open, unread };\n }\n\n async latest(scope: AttentionScope): Promise<AttentionLatest | null> {\n const mentions = await this.#comments.listMentionsForUser({ ...receiptScope(scope), limit: 10 });\n const newest = (await this.#resolve(scope, mentions))[0];\n if (!newest) return null;\n return {\n key: factoryAttentionKey(scope.factoryProjectId, newest.identity),\n at: newest.mention.occurredAt,\n unread: newest.receipt === undefined,\n };\n }\n\n async page(scope: AttentionScope, { view, search, before, limit }: AttentionPageArgs): Promise<AttentionPageResult> {\n return collectPage(this.#scan(scope, before), limit, async mentions => {\n const entries: AttentionEntry[] = [];\n for (const resolved of await this.#resolve(scope, mentions)) {\n if (!matchesView(view, resolved.receipt)) continue;\n if (search && !matchesMentionSearch(resolved, search)) continue;\n entries.push({\n occurredAt: resolved.mention.occurredAt,\n resumeCursor: { occurredAt: resolved.mention.occurredAt, id: resolved.mention.id },\n item: toMentionItem(scope, resolved),\n });\n }\n return entries;\n });\n }\n\n async markAllRead(\n scope: AttentionScope,\n { before, now }: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n return markScanRead(this.#scan(scope, before), async mentions => {\n await this.#workItems.markAttentionReceiptsRead({\n ...receiptScope(scope),\n identities: mentions.map(mention => factoryMentionAttentionIdentity(mention.commentId)),\n now,\n });\n });\n }\n}\n\nfunction matchesMentionSearch({ item, comment }: ResolvedMention, search: string): boolean {\n return (\n item.title.toLowerCase().includes(search) ||\n comment.body.toLowerCase().includes(search) ||\n comment.author.displayName?.toLowerCase().includes(search) === true\n );\n}\n\nfunction toMentionItem(scope: AttentionScope, { mention, comment, item, identity, receipt }: ResolvedMention) {\n const authorName = comment.author.displayName;\n return {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n kind: 'mention' as const,\n commentId: mention.commentId,\n occurrence: 0,\n workItemId: mention.workItemId,\n title: item.title,\n detail: comment.body.slice(0, 512),\n authorId: comment.author.id,\n ...(authorName ? { authorName } : {}),\n occurredAt: mention.occurredAt.toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: {\n kind: 'work-item' as const,\n workItemId: mention.workItemId,\n board: workItemBoard(item),\n commentId: mention.commentId,\n },\n };\n}\n\nexport function receiptScope(scope: AttentionScope): { orgId: string; factoryProjectId: string; userId: string } {\n return { orgId: scope.orgId, factoryProjectId: scope.factoryProjectId, userId: scope.userId };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"attention-providers.js","names":["#workItems","#spec","#boards","#scan","#receiptsFor","#linkedItems","#comments","#resolve"],"sources":["../../src/routes/attention-providers.ts"],"sourcesContent":["import { boardForWorkItem } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/index.js';\nimport { decisionRetryable, overtakenDecisionIds } from '../rules/decision-applicability.js';\n/**\n * Per-kind attention providers. Each kind owns its counts, its bounded page\n * scan, and its wire item shape; the route layer k-way merges provider pages\n * on `occurredAt desc` with one resumable cursor per kind.\n */\n\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n WorkItemCommentRow,\n WorkItemCommentsStorage,\n WorkItemMentionRow,\n} from '../storage/domains/comments/base.js';\nimport type {\n FactoryAttentionIdentity,\n FactoryAttentionKind,\n FactoryAttentionReceiptRecord,\n FactoryDeferredDecisionRecord,\n FactoryDispatchStatus,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport {\n factoryAttentionKey,\n factoryDecisionAttentionIdentity,\n factoryMentionAttentionIdentity,\n} from '../storage/domains/work-items/base.js';\n\nexport type FactoryAttentionView = 'open' | 'unread' | 'archived';\n\nexport interface AttentionScope {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n}\n\nexport interface AttentionStreamPosition {\n occurredAt: Date;\n id: string;\n}\n\nexport interface AttentionEntry {\n occurredAt: Date;\n /** Position resuming the provider's stream right after this entry. */\n resumeCursor: AttentionStreamPosition;\n item: Record<string, unknown>;\n}\n\nexport interface AttentionPageArgs {\n view: FactoryAttentionView;\n search?: string;\n before?: AttentionStreamPosition;\n limit: number;\n}\n\nexport interface AttentionPageResult {\n entries: AttentionEntry[];\n /** More rows behind the scan once every returned entry is consumed. */\n hasMore: boolean;\n /** Resume point for a scan-budget stop past the last returned entry. */\n continuation?: AttentionStreamPosition;\n}\n\nexport interface AttentionCounts {\n open: number;\n unread: number;\n}\n\nexport interface AttentionLatest {\n key: string;\n at: Date;\n unread: boolean;\n}\n\nexport interface AttentionProvider {\n kind: FactoryAttentionKind;\n counts(scope: AttentionScope): Promise<AttentionCounts>;\n latest(scope: AttentionScope): Promise<AttentionLatest | null>;\n page(scope: AttentionScope, args: AttentionPageArgs): Promise<AttentionPageResult>;\n markAllRead(\n scope: AttentionScope,\n args: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }>;\n}\n\nexport const SCAN_PAGE_SIZE = 50;\n// Receipt filtering is bounded per request; the response cursor resumes after the last scan.\nconst MAX_RECEIPT_SCAN_PAGES = 4;\n\ninterface ScanBatch<T> {\n rows: T[];\n /** Set only when the page budget stopped a scan that still had rows behind it. */\n continuation?: AttentionStreamPosition;\n}\n\n/**\n * One bounded keyset scan, shared by every provider read. Walks newest-first\n * from `before` until the stream runs dry or the page budget is spent; the\n * final batch of a budget stop carries the position to resume from.\n */\nexport async function* scanBatches<T>(\n fetchBatch: (before?: AttentionStreamPosition) => Promise<{ rows: T[]; hasMore: boolean }>,\n positionOf: (row: T) => AttentionStreamPosition,\n before?: AttentionStreamPosition,\n): AsyncGenerator<ScanBatch<T>> {\n let cursor = before;\n for (let scanned = 1; scanned <= MAX_RECEIPT_SCAN_PAGES; scanned += 1) {\n const { rows, hasMore } = await fetchBatch(cursor);\n const last = rows.at(-1);\n if (!last) return;\n const position = positionOf(last);\n yield { rows, ...(hasMore && scanned === MAX_RECEIPT_SCAN_PAGES ? { continuation: position } : {}) };\n if (!hasMore) return;\n cursor = position;\n }\n}\n\n/** Fills a page from the scan, stopping at `limit` entries or at the scan budget. */\nexport async function collectPage<T>(\n batches: AsyncGenerator<ScanBatch<T>>,\n limit: number,\n toEntries: (rows: T[]) => Promise<AttentionEntry[]>,\n): Promise<AttentionPageResult> {\n const entries: AttentionEntry[] = [];\n for await (const batch of batches) {\n for (const entry of await toEntries(batch.rows)) {\n if (entries.length === limit) return { entries, hasMore: true };\n entries.push(entry);\n }\n if (batch.continuation) return { entries, hasMore: true, continuation: batch.continuation };\n }\n return { entries, hasMore: false };\n}\n\n/** Marks every scanned batch read, reporting where a budget stop left off. */\nexport async function markScanRead<T>(\n batches: AsyncGenerator<ScanBatch<T>>,\n markBatch: (rows: T[]) => Promise<void>,\n): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n for await (const batch of batches) {\n await markBatch(batch.rows);\n if (batch.continuation) return { hasMore: true, continuation: batch.continuation };\n }\n return { hasMore: false };\n}\n\nexport function factoryDecisionType(decision: FactoryDeferredDecisionRecord): string {\n return typeof decision.decision.type === 'string' ? decision.decision.type.slice(0, 64) : 'unknown';\n}\n\nexport function matchesView(view: FactoryAttentionView, receipt: FactoryAttentionReceiptRecord | undefined): boolean {\n if (view === 'archived') return receipt?.state === 'archived';\n if (view === 'unread') return receipt === undefined;\n return receipt?.state !== 'archived';\n}\n\nfunction attentionTarget(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined) {\n if (!item) return { kind: 'rules' as const };\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : undefined;\n const session = role ? item.sessions[role] : undefined;\n if (session) {\n return {\n kind: 'thread' as const,\n sessionId: session.sessionId,\n threadId: session.threadId,\n };\n }\n return {\n kind: 'work-item' as const,\n workItemId: item.id,\n board: boardForWorkItem(item),\n };\n}\n\n/** One deferred-decision status seen as attention: failures and parked runs differ only by this. */\nexport interface DecisionAttentionSpec {\n kind: FactoryAttentionKind;\n status: FactoryDispatchStatus;\n identity(decision: FactoryDeferredDecisionRecord): FactoryAttentionIdentity;\n occurredAt(decision: FactoryDeferredDecisionRecord): Date;\n title(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;\n detail(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined): string;\n extra?(decision: FactoryDeferredDecisionRecord, context: { overtaken: boolean }): Record<string, unknown>;\n matches(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined, search: string): boolean;\n}\n\nexport class DecisionAttentionProvider implements AttentionProvider {\n readonly kind: FactoryAttentionKind;\n readonly #workItems: WorkItemsStorage;\n readonly #spec: DecisionAttentionSpec;\n readonly #boards: BoardRegistry;\n\n constructor(\n { workItems, boards }: { workItems: WorkItemsStorage; boards: BoardRegistry },\n spec: DecisionAttentionSpec,\n ) {\n this.#workItems = workItems;\n this.#boards = boards;\n this.#spec = spec;\n this.kind = spec.kind;\n }\n\n async counts(scope: AttentionScope): Promise<AttentionCounts> {\n const [total, receiptCount, archivedCount] = await Promise.all([\n this.#workItems.countDeferredDecisionsByStatuses({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n statuses: [this.#spec.status],\n }),\n this.#workItems.countAttentionReceipts({ ...receiptScope(scope), kind: this.kind }),\n this.#workItems.countAttentionReceipts({ ...receiptScope(scope), kind: this.kind, state: 'archived' }),\n ]);\n return {\n open: Math.max(0, total - archivedCount),\n unread: Math.max(0, total - receiptCount),\n };\n }\n\n async latest(scope: AttentionScope): Promise<AttentionLatest | null> {\n const page = await this.#workItems.listDecisionPageByStatus({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n status: this.#spec.status,\n limit: 1,\n });\n const newest = page.decisions[0];\n if (!newest) return null;\n const identity = this.#spec.identity(newest);\n const receipts = await this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: [identity],\n });\n return {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n at: this.#spec.occurredAt(newest),\n unread: receipts.length === 0,\n };\n }\n\n #scan(scope: AttentionScope, before?: AttentionStreamPosition) {\n return scanBatches<FactoryDeferredDecisionRecord>(\n async cursor => {\n const page = await this.#workItems.listDecisionPageByStatus({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n status: this.#spec.status,\n before: cursor,\n limit: SCAN_PAGE_SIZE,\n });\n return { rows: page.decisions, hasMore: page.hasMore };\n },\n decision => ({ occurredAt: this.#spec.occurredAt(decision), id: decision.id }),\n before,\n );\n }\n\n async page(scope: AttentionScope, { view, search, before, limit }: AttentionPageArgs): Promise<AttentionPageResult> {\n return collectPage(this.#scan(scope, before), limit, async decisions => {\n const [receiptByKey, itemById, overtaken] = await Promise.all([\n this.#receiptsFor(scope, decisions),\n this.#linkedItems(scope, decisions),\n overtakenDecisionIds(this.#workItems, this.#boards, scope, decisions),\n ]);\n const entries: AttentionEntry[] = [];\n for (const decision of decisions) {\n const identity = this.#spec.identity(decision);\n const receipt = receiptByKey.get(factoryAttentionKey(scope.factoryProjectId, identity));\n if (!matchesView(view, receipt)) continue;\n const item = decision.workItemId ? itemById.get(decision.workItemId) : undefined;\n if (search && !this.#spec.matches(decision, item, search)) continue;\n const occurredAt = this.#spec.occurredAt(decision);\n entries.push({\n occurredAt,\n resumeCursor: { occurredAt, id: decision.id },\n item: {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n kind: this.kind,\n decisionId: decision.id,\n occurrence: identity.occurrence,\n workItemId: decision.workItemId,\n title: this.#spec.title(decision, item),\n detail: this.#spec.detail(decision, item),\n decisionType: factoryDecisionType(decision),\n ...this.#spec.extra?.(decision, { overtaken: overtaken.has(decision.id) }),\n occurredAt: occurredAt.toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: attentionTarget(decision, item),\n },\n });\n }\n return entries;\n });\n }\n\n async #receiptsFor(\n scope: AttentionScope,\n decisions: FactoryDeferredDecisionRecord[],\n ): Promise<Map<string, FactoryAttentionReceiptRecord>> {\n const receipts = await this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: decisions.map(decision => this.#spec.identity(decision)),\n });\n return new Map(receipts.map(receipt => [factoryAttentionKey(scope.factoryProjectId, receipt), receipt]));\n }\n\n async #linkedItems(\n scope: AttentionScope,\n decisions: FactoryDeferredDecisionRecord[],\n ): Promise<Map<string, WorkItemRow>> {\n const items = await this.#workItems.listByIds({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n ids: decisions.flatMap(decision => (decision.workItemId ? [decision.workItemId] : [])),\n });\n return new Map(items.map(item => [item.id, item]));\n }\n\n async markAllRead(\n scope: AttentionScope,\n { before, now }: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n return markScanRead(this.#scan(scope, before), async decisions => {\n await this.#workItems.markAttentionReceiptsRead({\n ...receiptScope(scope),\n identities: decisions.map(decision => this.#spec.identity(decision)),\n now,\n });\n });\n }\n}\n\nexport const failedDecisionAttentionSpec: DecisionAttentionSpec = {\n kind: 'automation-failed',\n status: 'failed',\n identity: decision => factoryDecisionAttentionIdentity(decision.id, decision.failureOccurrence),\n occurredAt: decision => decision.completedAt ?? decision.updatedAt,\n title: (decision, item) => item?.title ?? factoryDispatchFailureMetadata(decision.failureCode).label,\n detail: decision => decision.lastError?.slice(0, 512) ?? factoryDispatchFailureMetadata(decision.failureCode).label,\n extra: (decision, { overtaken }) => ({\n failureCode: decision.failureCode,\n canRetry: decisionRetryable(decision, overtaken),\n }),\n matches: (decision, item, search) =>\n item?.title.toLowerCase().includes(search) === true ||\n decision.lastError?.toLowerCase().includes(search) === true ||\n factoryDecisionType(decision).toLowerCase().includes(search),\n};\n\ninterface ResolvedMention {\n mention: WorkItemMentionRow;\n comment: WorkItemCommentRow;\n item: WorkItemRow;\n identity: FactoryAttentionIdentity;\n receipt: FactoryAttentionReceiptRecord | undefined;\n}\n\nexport class MentionAttentionProvider implements AttentionProvider {\n readonly kind = 'mention' as const;\n readonly #workItems: WorkItemsStorage;\n readonly #comments: WorkItemCommentsStorage;\n\n constructor({ workItems, comments }: { workItems: WorkItemsStorage; comments: WorkItemCommentsStorage }) {\n this.#workItems = workItems;\n this.#comments = comments;\n }\n\n #scan(scope: AttentionScope, before?: AttentionStreamPosition) {\n return scanBatches<WorkItemMentionRow>(\n async cursor => {\n const rows = await this.#comments.listMentionsForUser({\n ...receiptScope(scope),\n ...(cursor ? { before: cursor } : {}),\n limit: SCAN_PAGE_SIZE + 1,\n });\n return { rows: rows.slice(0, SCAN_PAGE_SIZE), hasMore: rows.length > SCAN_PAGE_SIZE };\n },\n mention => ({ occurredAt: mention.occurredAt, id: mention.id }),\n before,\n );\n }\n\n /**\n * Mention rows joined to what the inbox needs to show one — dropping the\n * ones it cannot: a deleted comment, or a work item gone from the project.\n */\n async #resolve(scope: AttentionScope, mentions: WorkItemMentionRow[]): Promise<ResolvedMention[]> {\n const [receipts, comments, items] = await Promise.all([\n this.#workItems.listAttentionReceipts({\n ...receiptScope(scope),\n identities: mentions.map(mention => factoryMentionAttentionIdentity(mention.commentId)),\n }),\n this.#comments.listByIds({\n orgId: scope.orgId,\n ids: [...new Set(mentions.map(mention => mention.commentId))],\n }),\n this.#workItems.listByIds({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n ids: [...new Set(mentions.map(mention => mention.workItemId))],\n }),\n ]);\n const receiptByKey = new Map(\n receipts.map(receipt => [factoryAttentionKey(scope.factoryProjectId, receipt), receipt]),\n );\n const commentById = new Map(comments.map(comment => [comment.id, comment]));\n const itemById = new Map(items.map(item => [item.id, item]));\n\n const resolved: ResolvedMention[] = [];\n for (const mention of mentions) {\n const comment = commentById.get(mention.commentId);\n const item = itemById.get(mention.workItemId);\n if (!comment || comment.deletedAt || !item) continue;\n const identity = factoryMentionAttentionIdentity(mention.commentId);\n resolved.push({\n mention,\n comment,\n item,\n identity,\n receipt: receiptByKey.get(factoryAttentionKey(scope.factoryProjectId, identity)),\n });\n }\n return resolved;\n }\n\n /**\n * Derived from the same bounded scan as `page`, never from raw aggregates:\n * the badge must count exactly what the views can show, down to stopping at\n * the same scan budget. Aggregates would count orphan mention rows the list\n * can never display or clear.\n */\n async counts(scope: AttentionScope): Promise<AttentionCounts> {\n let open = 0;\n let unread = 0;\n for await (const batch of this.#scan(scope)) {\n for (const { receipt } of await this.#resolve(scope, batch.rows)) {\n if (receipt?.state !== 'archived') open += 1;\n if (receipt === undefined) unread += 1;\n }\n }\n return { open, unread };\n }\n\n async latest(scope: AttentionScope): Promise<AttentionLatest | null> {\n const mentions = await this.#comments.listMentionsForUser({ ...receiptScope(scope), limit: 10 });\n const newest = (await this.#resolve(scope, mentions))[0];\n if (!newest) return null;\n return {\n key: factoryAttentionKey(scope.factoryProjectId, newest.identity),\n at: newest.mention.occurredAt,\n unread: newest.receipt === undefined,\n };\n }\n\n async page(scope: AttentionScope, { view, search, before, limit }: AttentionPageArgs): Promise<AttentionPageResult> {\n return collectPage(this.#scan(scope, before), limit, async mentions => {\n const entries: AttentionEntry[] = [];\n for (const resolved of await this.#resolve(scope, mentions)) {\n if (!matchesView(view, resolved.receipt)) continue;\n if (search && !matchesMentionSearch(resolved, search)) continue;\n entries.push({\n occurredAt: resolved.mention.occurredAt,\n resumeCursor: { occurredAt: resolved.mention.occurredAt, id: resolved.mention.id },\n item: toMentionItem(scope, resolved),\n });\n }\n return entries;\n });\n }\n\n async markAllRead(\n scope: AttentionScope,\n { before, now }: { before?: AttentionStreamPosition; now: Date },\n ): Promise<{ hasMore: boolean; continuation?: AttentionStreamPosition }> {\n return markScanRead(this.#scan(scope, before), async mentions => {\n await this.#workItems.markAttentionReceiptsRead({\n ...receiptScope(scope),\n identities: mentions.map(mention => factoryMentionAttentionIdentity(mention.commentId)),\n now,\n });\n });\n }\n}\n\nfunction matchesMentionSearch({ item, comment }: ResolvedMention, search: string): boolean {\n return (\n item.title.toLowerCase().includes(search) ||\n comment.body.toLowerCase().includes(search) ||\n comment.author.displayName?.toLowerCase().includes(search) === true\n );\n}\n\nfunction toMentionItem(scope: AttentionScope, { mention, comment, item, identity, receipt }: ResolvedMention) {\n const authorName = comment.author.displayName;\n return {\n key: factoryAttentionKey(scope.factoryProjectId, identity),\n kind: 'mention' as const,\n commentId: mention.commentId,\n occurrence: 0,\n workItemId: mention.workItemId,\n title: item.title,\n detail: comment.body.slice(0, 512),\n authorId: comment.author.id,\n ...(authorName ? { authorName } : {}),\n occurredAt: mention.occurredAt.toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: {\n kind: 'work-item' as const,\n workItemId: mention.workItemId,\n board: boardForWorkItem(item),\n commentId: mention.commentId,\n },\n };\n}\n\nexport function receiptScope(scope: AttentionScope): { orgId: string; factoryProjectId: string; userId: string } {\n return { orgId: scope.orgId, factoryProjectId: scope.factoryProjectId, userId: scope.userId };\n}\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import type { AuthStorage } from '@mastra/code-sdk/auth/storage';
2
2
  import type { ProviderAccess } from '@mastra/code-sdk/onboarding/packs';
3
+ import type { ModelReasoningOption } from '@mastra/core/llm';
3
4
  import type { ApiRoute } from '@mastra/core/server';
4
5
  import type { CredentialRecord, LoginSessionKind, ModelCredentialsStorage } from '../storage/domains/credentials/base.js';
5
- import type { CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';
6
+ import type { CustomProviderRecord, CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';
6
7
  import type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';
7
8
  import type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';
8
9
  import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
@@ -60,13 +61,17 @@ export interface OMSession {
60
61
  /** `get()` returns '' when no model is selected, so callers fall back on falsy. */
61
62
  model: {
62
63
  get: () => string;
64
+ switch?: (modelId: string) => Promise<void>;
65
+ };
66
+ thread?: {
67
+ getId: () => string | null;
63
68
  };
64
69
  }
65
70
  /** Minimal controller surface this module needs (model catalog + modes + sessions). */
66
- interface ModelCatalog {
71
+ export interface ModelCatalog {
67
72
  listAvailableModels: () => Promise<Array<{
68
73
  id?: string;
69
- modelName?: string;
74
+ modelName: string;
70
75
  provider: string;
71
76
  hasApiKey: boolean;
72
77
  apiKeyEnvVar?: string;
@@ -75,7 +80,7 @@ interface ModelCatalog {
75
80
  id: string;
76
81
  defaultModelId?: string;
77
82
  }>;
78
- getSessionByResource?: (resourceId: string, scope?: string) => Promise<OMSession | undefined>;
83
+ getSession?: (sessionId: string) => Promise<OMSession | undefined>;
79
84
  }
80
85
  /**
81
86
  * Build a deduplicated, sorted list of providers from the model catalog,
@@ -113,6 +118,7 @@ export declare function isDeploymentModelProvider(provider: string): boolean;
113
118
  * ignored with a warning rather than failing boot.
114
119
  */
115
120
  export declare function resolveDeploymentModelProviders(providers: readonly string[] | undefined): ReadonlySet<string>;
121
+ export type ModelCredentialHolder = 'personal' | 'organization' | 'deployment';
116
122
  /**
117
123
  * Compute which providers the user can reach, mirroring the TUI's
118
124
  * `/models-pack` access derivation: OAuth/api-key from the credential store for
@@ -124,6 +130,39 @@ export declare function buildProviderAccess({ controller, authStorage, tenantCre
124
130
  tenantCredentials?: CredentialRecord[];
125
131
  deploymentProviders?: ReadonlySet<string>;
126
132
  }): Promise<ProviderAccess>;
133
+ /** A model the caller can pick or switch to. */
134
+ export interface ModelCatalogEntry {
135
+ id: string;
136
+ provider: string;
137
+ modelName: string;
138
+ hasApiKey: true;
139
+ reasoningOptions?: ModelReasoningOption[];
140
+ credentialLabel?: string;
141
+ credentialHolder?: ModelCredentialHolder;
142
+ }
143
+ /**
144
+ * Models usable with the given credentials: built-in models whose provider the
145
+ * credentials reach, then the org's custom provider models. The Settings model
146
+ * picker and `factory_switch_session_model` both use this so they cannot drift.
147
+ */
148
+ export declare function buildModelCatalog({ controller, authStorage, tenantCredentials, deploymentProviders, listCustomProviders, }: {
149
+ controller: ModelCatalog;
150
+ authStorage?: AuthStorage;
151
+ /** Tenant rows in deployed mode; absent in local mode, where `authStorage` and server keys govern. */
152
+ tenantCredentials?: CredentialRecord[];
153
+ deploymentProviders?: ReadonlySet<string>;
154
+ listCustomProviders?: () => Promise<CustomProviderRecord[]>;
155
+ }): Promise<ModelCatalogEntry[]>;
156
+ /**
157
+ * The tenant credentials a session's model policy allows: the Factory's (org
158
+ * rows) for Factory-scoped sessions, the session owner's for user-scoped ones.
159
+ */
160
+ export declare function listSessionScopedCredentials({ credentials, orgId, userId, modelScope, }: {
161
+ credentials: Pick<ModelCredentialsStorage, 'listCredentials'>;
162
+ orgId: string;
163
+ userId: string;
164
+ modelScope: 'factory' | 'user';
165
+ }): Promise<CredentialRecord[]>;
127
166
  /** Per-user default model context for the current request. */
128
167
  export interface ModelDefaultsContext {
129
168
  storage: ModelDefaultsStorage;
@@ -165,6 +204,7 @@ export interface ConfigRoutesDeps extends RouteDependencies {
165
204
  modelDefaults?: ModelDefaultsStorage;
166
205
  /** Source-control sessions available to config route integrations. */
167
206
  sourceControlSessions?: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;
207
+ sourceControls?: Array<Pick<SourceControlStorageHandle, 'sessions'>>;
168
208
  /** Thread membership used to resolve the same model policy as an invocation. */
169
209
  workItems?: WorkItemsStorage;
170
210
  /** Tenant memory-settings domain handle; absent in local (no-DB) mode. */
@@ -206,5 +246,4 @@ export interface ConfigRoutesDeps extends RouteDependencies {
206
246
  export declare class ConfigRoutes extends Route<ConfigRoutesDeps> {
207
247
  routes(): ApiRoute[];
208
248
  }
209
- export {};
210
249
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { WEB_OAUTH_FLOW_KINDS, getAuthProviderId, listTenantCredentialsForRequest, resolveCredentialContext, tenantOrgId } from "./provider-credentials.js";
1
+ import { WEB_OAUTH_PROVIDERS, credentialLabel, getAuthProviderId, listTenantCredentialsForRequest, resolveCredentialContext, tenantOrgId } from "./provider-credentials.js";
2
+ import { effectiveModelScope } from "../storage/domains/projects/base.js";
2
3
  import { resolveFactoryModelPolicy } from "../session/credential-policy.js";
3
4
  import "../session/memory-settings-hydration.js";
4
5
  import { factoryMemorySettingsUserId } from "../storage/domains/memory-settings/base.js";
@@ -57,7 +58,7 @@ async function listProviders({ controller, authStorage, tenantCredentials, deplo
57
58
  else if (authStorage?.hasStoredApiKey(model.provider)) source = "stored";
58
59
  else if (model.apiKeyEnvVar && process.env[model.apiKeyEnvVar]) source = "env";
59
60
  else if (model.hasApiKey) source = "env";
60
- const flowKind = WEB_OAUTH_FLOW_KINDS[model.provider];
61
+ const flowKind = WEB_OAUTH_PROVIDERS[model.provider]?.flow;
61
62
  seen.set(model.provider, {
62
63
  provider: model.provider,
63
64
  envVar: model.apiKeyEnvVar,
@@ -154,6 +155,19 @@ function resolveDeploymentModelProviders(providers) {
154
155
  }
155
156
  return resolved;
156
157
  }
158
+ function grantingTenantRecord(records, authProviderId) {
159
+ return records.find((r) => r.scope === "user" && r.provider === authProviderId) ?? records.find((r) => r.scope === "org" && r.provider === authProviderId);
160
+ }
161
+ function tenantModelCredential(records, provider) {
162
+ if (!records) return {};
163
+ if (isDeploymentModelProvider(provider)) return { credentialHolder: "deployment" };
164
+ const record = grantingTenantRecord(records, getAuthProviderId(provider));
165
+ if (!record) return {};
166
+ return {
167
+ credentialLabel: credentialLabel(provider, record.credential.type),
168
+ credentialHolder: record.scope === "user" ? "personal" : "organization"
169
+ };
170
+ }
157
171
  /**
158
172
  * Compute which providers the user can reach, mirroring the TUI's
159
173
  * `/models-pack` access derivation: OAuth/api-key from the credential store for
@@ -166,9 +180,7 @@ async function buildProviderAccess({ controller, authStorage, tenantCredentials,
166
180
  const authProviderId = getAuthProviderId(provider);
167
181
  if (tenantCredentials) {
168
182
  if (isDeploymentModelProvider(provider)) return deploymentProviders?.has(provider) && hasModelKey(provider) ? "apikey" : false;
169
- const userRec = tenantCredentials.find((r) => r.scope === "user" && r.provider === authProviderId);
170
- const orgRec = tenantCredentials.find((r) => r.scope === "org" && r.provider === authProviderId);
171
- const credential = userRec?.credential ?? orgRec?.credential;
183
+ const credential = grantingTenantRecord(tenantCredentials, authProviderId)?.credential;
172
184
  if (credential?.type === "oauth") return "oauth";
173
185
  if (credential?.type === "api_key" && credential.key.trim().length > 0) return "apikey";
174
186
  return false;
@@ -197,6 +209,52 @@ async function buildProviderAccess({ controller, authStorage, tenantCredentials,
197
209
  function canUseModelProvider(access, provider) {
198
210
  return Boolean(access[provider]);
199
211
  }
212
+ /**
213
+ * Models usable with the given credentials: built-in models whose provider the
214
+ * credentials reach, then the org's custom provider models. The Settings model
215
+ * picker and `factory_switch_session_model` both use this so they cannot drift.
216
+ */
217
+ async function buildModelCatalog({ controller, authStorage, tenantCredentials, deploymentProviders, listCustomProviders }) {
218
+ const [models, access] = await Promise.all([controller.listAvailableModels(), buildProviderAccess({
219
+ controller,
220
+ authStorage: tenantCredentials ? void 0 : authStorage,
221
+ tenantCredentials,
222
+ deploymentProviders
223
+ })]);
224
+ const catalog = models.flatMap(({ id, provider, modelName }) => typeof id === "string" && canUseModelProvider(access, provider) ? [{
225
+ id,
226
+ provider,
227
+ modelName,
228
+ hasApiKey: true,
229
+ reasoningOptions: getModelReasoningOptions(id),
230
+ ...tenantModelCredential(tenantCredentials, provider)
231
+ }] : []);
232
+ if (listCustomProviders) try {
233
+ const known = new Set(catalog.map((m) => m.id));
234
+ const customProviderCredential = tenantCredentials ? { credentialHolder: "organization" } : {};
235
+ for (const record of await listCustomProviders()) for (const model of record.models) {
236
+ const id = `${record.providerId}/${model}`;
237
+ if (known.has(id)) continue;
238
+ known.add(id);
239
+ catalog.push({
240
+ id,
241
+ provider: record.providerId,
242
+ modelName: model,
243
+ hasApiKey: true,
244
+ ...customProviderCredential
245
+ });
246
+ }
247
+ } catch {}
248
+ return catalog.sort((a, b) => a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider));
249
+ }
250
+ /**
251
+ * The tenant credentials a session's model policy allows: the Factory's (org
252
+ * rows) for Factory-scoped sessions, the session owner's for user-scoped ones.
253
+ */
254
+ async function listSessionScopedCredentials({ credentials, orgId, userId, modelScope }) {
255
+ const storageScope = modelScope === "factory" ? "org" : "user";
256
+ return (await credentials.listCredentials(orgId, userId)).filter((record) => record.scope === storageScope);
257
+ }
200
258
  /** Resolve the default-model context for a request, or a ready-to-return error response. */
201
259
  async function resolveModelDefaultsContext({ c, auth, modelDefaults }) {
202
260
  await auth.ensureUser(c);
@@ -586,13 +644,19 @@ var ConfigRoutes = class extends Route {
586
644
  let sessionPolicy;
587
645
  const resourceId = c.req.query("resourceId");
588
646
  const threadId = c.req.query("threadId");
589
- if (resourceId !== void 0 || threadId !== void 0) {
647
+ const sessionId = c.req.query("sessionId");
648
+ if (resourceId !== void 0 || threadId !== void 0 || sessionId !== void 0) {
590
649
  const address = z.object({
591
650
  resourceId: z.string().trim().min(1),
592
- threadId: z.string().trim().min(1)
593
- }).safeParse({
651
+ threadId: z.string().trim().min(1),
652
+ sessionId: z.string().trim().min(1).optional()
653
+ }).transform((data) => ({
654
+ ...data,
655
+ sessionId: data.sessionId ?? data.resourceId
656
+ })).safeParse({
594
657
  resourceId,
595
- threadId
658
+ threadId,
659
+ sessionId
596
660
  });
597
661
  if (!address.success) return c.json({ error: "invalid_session_context" }, 400);
598
662
  if (!tenant && auth.enabled()) return c.json({ error: "unauthorized" }, 401);
@@ -617,8 +681,12 @@ var ConfigRoutes = class extends Route {
617
681
  if (!policy) return c.json({ error: "unauthorized" }, 401);
618
682
  const orgId = policy.tenant.orgId ?? tenantOrgId(tenant);
619
683
  const modelScope = policy.tenant.credentialScope ?? "user";
620
- const storageScope = modelScope === "factory" ? "org" : "user";
621
- tenantCredentials = (await options.modelCredentials.listCredentials(orgId, policy.tenant.userId)).filter((record) => record.scope === storageScope);
684
+ tenantCredentials = await listSessionScopedCredentials({
685
+ credentials: options.modelCredentials,
686
+ orgId,
687
+ userId: policy.tenant.userId,
688
+ modelScope
689
+ });
622
690
  let defaultModelId = policy.factoryDefaultModelId ?? null;
623
691
  if (modelScope === "user") {
624
692
  if (!options.modelDefaults) return c.json({ error: "model_defaults_unavailable" }, 503);
@@ -648,44 +716,24 @@ var ConfigRoutes = class extends Route {
648
716
  const credentialScope = project.modelScope === "user" ? "user" : "org";
649
717
  tenantCredentials = allTenantCredentials.filter((record) => record.scope === credentialScope);
650
718
  }
651
- const [models, access] = await Promise.all([controller.listAvailableModels(), buildProviderAccess({
719
+ const customProviders = options.customProviders;
720
+ const models = await buildModelCatalog({
652
721
  controller,
653
- authStorage: tenantCredentials ? void 0 : authStorage,
722
+ authStorage,
654
723
  tenantCredentials,
655
- deploymentProviders
656
- })]);
657
- const catalog = models.flatMap(({ id, provider, modelName }) => typeof id === "string" && canUseModelProvider(access, provider) ? [{
658
- id,
659
- provider,
660
- modelName,
661
- hasApiKey: true,
662
- reasoningOptions: getModelReasoningOptions(id)
663
- }] : []);
664
- if (options.customProviders) try {
665
- const ctx = await resolveCustomProvidersContext({
666
- c: loose(c),
667
- auth,
668
- customProviders: options.customProviders
669
- });
670
- if (!("response" in ctx)) {
671
- const known = new Set(catalog.map((m) => m.id));
672
- for (const record of await ctx.storage.list({ orgId: ctx.orgId })) for (const model of record.models) {
673
- const id = `${record.providerId}/${model}`;
674
- if (known.has(id)) continue;
675
- known.add(id);
676
- catalog.push({
677
- id,
678
- provider: record.providerId,
679
- modelName: model,
680
- hasApiKey: true,
681
- reasoningOptions: void 0
682
- });
683
- }
684
- }
685
- } catch {}
724
+ deploymentProviders,
725
+ listCustomProviders: customProviders ? async () => {
726
+ const ctx = await resolveCustomProvidersContext({
727
+ c: loose(c),
728
+ auth,
729
+ customProviders
730
+ });
731
+ return "response" in ctx ? [] : ctx.storage.list({ orgId: ctx.orgId });
732
+ } : void 0
733
+ });
686
734
  return c.json({
687
735
  ...sessionPolicy,
688
- models: catalog.sort((a, b) => a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider))
736
+ models
689
737
  });
690
738
  } catch (error) {
691
739
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -713,6 +761,85 @@ var ConfigRoutes = class extends Route {
713
761
  }
714
762
  }
715
763
  }),
764
+ registerApiRoute("/web/config/default-model/apply", {
765
+ method: "POST",
766
+ requiresAuth: false,
767
+ handler: async (c) => {
768
+ const context = await resolveModelDefaultsContext({
769
+ c: loose(c),
770
+ auth,
771
+ modelDefaults: options.modelDefaults
772
+ });
773
+ if ("response" in context) return context.response;
774
+ if (!options.factoryProjects || !options.sourceControls || !options.workItems || !options.controller.getSession) return c.json({ error: "model_apply_unavailable" }, 503);
775
+ const record = await context.storage.get({
776
+ orgId: context.orgId,
777
+ userId: context.userId
778
+ });
779
+ if (!record?.modelId) return c.json({ error: "default_model_not_set" }, 400);
780
+ const applied = [];
781
+ const skipped = [];
782
+ const seen = /* @__PURE__ */ new Set();
783
+ try {
784
+ await options.factoryProjects.ensureReady();
785
+ const projects = await options.factoryProjects.list({ orgId: context.orgId });
786
+ for (const project of projects) {
787
+ const personal = effectiveModelScope(project.modelScope, "session") === "user";
788
+ const workerSessions = new Set((await options.workItems.listRunBindings(context.orgId, project.id)).map((binding) => binding.sessionId));
789
+ for (const sourceControl of options.sourceControls) {
790
+ const sessions = await sourceControl.sessions.listByProject({
791
+ orgId: context.orgId,
792
+ factoryProjectId: project.id,
793
+ viewerUserId: context.userId
794
+ });
795
+ for (const row of sessions) {
796
+ if (row.userId !== context.userId || row.originSessionId || workerSessions.has(row.sessionId) || seen.has(row.sessionId)) continue;
797
+ seen.add(row.sessionId);
798
+ if (!personal) {
799
+ skipped.push({
800
+ sessionId: row.sessionId,
801
+ reason: "factory-scope"
802
+ });
803
+ continue;
804
+ }
805
+ const session = await options.controller.getSession(row.sessionId);
806
+ if (!session?.thread || !session.model.switch) {
807
+ skipped.push({
808
+ sessionId: row.sessionId,
809
+ reason: "not-running"
810
+ });
811
+ continue;
812
+ }
813
+ if (!session.thread.getId()) {
814
+ skipped.push({
815
+ sessionId: row.sessionId,
816
+ reason: "thread-missing"
817
+ });
818
+ continue;
819
+ }
820
+ try {
821
+ await session.model.switch(record.modelId);
822
+ applied.push(row.sessionId);
823
+ } catch (error) {
824
+ console.warn("[factory] apply user default model failed for session", row.sessionId, error);
825
+ skipped.push({
826
+ sessionId: row.sessionId,
827
+ reason: "apply-failed"
828
+ });
829
+ }
830
+ }
831
+ }
832
+ }
833
+ } catch (error) {
834
+ return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
835
+ }
836
+ return c.json({
837
+ modelId: record.modelId,
838
+ applied,
839
+ skipped
840
+ });
841
+ }
842
+ }),
716
843
  registerApiRoute("/web/config/default-model", {
717
844
  method: "PUT",
718
845
  requiresAuth: false,
@@ -775,8 +902,7 @@ var ConfigRoutes = class extends Route {
775
902
  method: "GET",
776
903
  requiresAuth: false,
777
904
  handler: async (c) => {
778
- const resourceId = c.req.query("resourceId");
779
- const scope = c.req.query("scope") || void 0;
905
+ const sessionId = c.req.query("sessionId");
780
906
  const factoryProjectId = c.req.query("factoryId") || void 0;
781
907
  const context = await resolveMemorySettingsContext({
782
908
  c: loose(c),
@@ -793,7 +919,7 @@ var ConfigRoutes = class extends Route {
793
919
  });
794
920
  const fallback = await factoryOmFallback(factoryProjectId);
795
921
  const availableProviders = await resolveOMResponseContext(loose(c));
796
- const sessionModelId = (resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0)?.model.get();
922
+ const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
797
923
  return c.json({ config: readStoredOMConfig(record, sessionModelId || fallback, sessionModelId ? "live-session" : "configured-default", availableProviders) });
798
924
  } catch (error) {
799
925
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
@@ -812,8 +938,7 @@ var ConfigRoutes = class extends Route {
812
938
  } catch {
813
939
  return c.json({ error: "Invalid JSON body" }, 400);
814
940
  }
815
- const resourceId = typeof body.resourceId === "string" ? body.resourceId : "";
816
- const scope = typeof body.scope === "string" && body.scope ? body.scope : void 0;
941
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
817
942
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
818
943
  const model = typeof body.modelId === "string" ? body.modelId.trim() : "";
819
944
  if (!model) return c.json({ error: "Missing required field: modelId" }, 400);
@@ -833,7 +958,7 @@ var ConfigRoutes = class extends Route {
833
958
  orgId: context.orgId,
834
959
  userId: context.userId
835
960
  });
836
- const sessionModelId = (resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0)?.model.get();
961
+ const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
837
962
  const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
838
963
  return c.json({
839
964
  ok: true,
@@ -854,8 +979,7 @@ var ConfigRoutes = class extends Route {
854
979
  } catch {
855
980
  return c.json({ error: "Invalid JSON body" }, 400);
856
981
  }
857
- const resourceId = typeof body.resourceId === "string" ? body.resourceId : "";
858
- const scope = typeof body.scope === "string" && body.scope ? body.scope : void 0;
982
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
859
983
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
860
984
  const observation = typeof body.observationThreshold === "number" && body.observationThreshold > 0 ? Math.round(body.observationThreshold) : void 0;
861
985
  const reflection = typeof body.reflectionThreshold === "number" && body.reflectionThreshold > 0 ? Math.round(body.reflectionThreshold) : void 0;
@@ -878,7 +1002,7 @@ var ConfigRoutes = class extends Route {
878
1002
  orgId: context.orgId,
879
1003
  userId: context.userId
880
1004
  });
881
- const sessionModelId = (resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0)?.model.get();
1005
+ const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
882
1006
  const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
883
1007
  return c.json({
884
1008
  ok: true,
@@ -899,8 +1023,7 @@ var ConfigRoutes = class extends Route {
899
1023
  } catch {
900
1024
  return c.json({ error: "Invalid JSON body" }, 400);
901
1025
  }
902
- const resourceId = typeof body.resourceId === "string" ? body.resourceId : "";
903
- const scope = typeof body.scope === "string" && body.scope ? body.scope : void 0;
1026
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
904
1027
  const factoryProjectId = typeof body.factoryId === "string" && body.factoryId ? body.factoryId : void 0;
905
1028
  const raw = body.value;
906
1029
  const value = raw === "auto" || raw === true || raw === false ? raw : "auto";
@@ -920,7 +1043,7 @@ var ConfigRoutes = class extends Route {
920
1043
  orgId: context.orgId,
921
1044
  userId: context.userId
922
1045
  });
923
- const sessionModelId = (resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0)?.model.get();
1046
+ const sessionModelId = (sessionId ? await controller.getSession?.(sessionId) : void 0)?.model.get();
924
1047
  const config = readStoredOMConfig(record, sessionModelId || await factoryOmFallback(factoryProjectId), sessionModelId ? "live-session" : "configured-default", availableProviders);
925
1048
  return c.json({
926
1049
  ok: true,
@@ -935,6 +1058,6 @@ var ConfigRoutes = class extends Route {
935
1058
  }
936
1059
  };
937
1060
  //#endregion
938
- export { ConfigRoutes, DEPLOYMENT_MODEL_PROVIDERS, buildProviderAccess, isDeploymentModelProvider, listProviders, resolveDeploymentModelProviders };
1061
+ export { ConfigRoutes, DEPLOYMENT_MODEL_PROVIDERS, buildModelCatalog, buildProviderAccess, isDeploymentModelProvider, listProviders, listSessionScopedCredentials, resolveDeploymentModelProviders };
939
1062
 
940
1063
  //# sourceMappingURL=config.js.map