@mastra/factory 0.0.0-esbuild-bundle-worker-20260807173433

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 (329) hide show
  1. package/CHANGELOG.md +1008 -0
  2. package/LICENSE.md +30 -0
  3. package/README.md +27 -0
  4. package/dist/auth.d.ts +183 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +507 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/capabilities/connection.d.ts +9 -0
  9. package/dist/capabilities/connection.d.ts.map +1 -0
  10. package/dist/capabilities/connection.js +1 -0
  11. package/dist/capabilities/intake.d.ts +141 -0
  12. package/dist/capabilities/intake.d.ts.map +1 -0
  13. package/dist/capabilities/intake.js +1 -0
  14. package/dist/capabilities/version-control.d.ts +247 -0
  15. package/dist/capabilities/version-control.d.ts.map +1 -0
  16. package/dist/capabilities/version-control.js +1 -0
  17. package/dist/factory.d.ts +159 -0
  18. package/dist/factory.d.ts.map +1 -0
  19. package/dist/factory.js +491 -0
  20. package/dist/factory.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +7 -0
  24. package/dist/integrations/base.d.ts +254 -0
  25. package/dist/integrations/base.d.ts.map +1 -0
  26. package/dist/integrations/base.js +1 -0
  27. package/dist/integrations/github/config.d.ts +60 -0
  28. package/dist/integrations/github/config.d.ts.map +1 -0
  29. package/dist/integrations/github/config.js +42 -0
  30. package/dist/integrations/github/config.js.map +1 -0
  31. package/dist/integrations/github/integration.d.ts +220 -0
  32. package/dist/integrations/github/integration.d.ts.map +1 -0
  33. package/dist/integrations/github/integration.js +1162 -0
  34. package/dist/integrations/github/integration.js.map +1 -0
  35. package/dist/integrations/github/issue-triage.d.ts +27 -0
  36. package/dist/integrations/github/issue-triage.d.ts.map +1 -0
  37. package/dist/integrations/github/issue-triage.js +82 -0
  38. package/dist/integrations/github/issue-triage.js.map +1 -0
  39. package/dist/integrations/github/pat.d.ts +38 -0
  40. package/dist/integrations/github/pat.d.ts.map +1 -0
  41. package/dist/integrations/github/pat.js +59 -0
  42. package/dist/integrations/github/pat.js.map +1 -0
  43. package/dist/integrations/github/provenance.d.ts +27 -0
  44. package/dist/integrations/github/provenance.d.ts.map +1 -0
  45. package/dist/integrations/github/provenance.js +75 -0
  46. package/dist/integrations/github/provenance.js.map +1 -0
  47. package/dist/integrations/github/reconcile-worker.d.ts +41 -0
  48. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -0
  49. package/dist/integrations/github/reconcile-worker.js +125 -0
  50. package/dist/integrations/github/reconcile-worker.js.map +1 -0
  51. package/dist/integrations/github/routes.d.ts +69 -0
  52. package/dist/integrations/github/routes.d.ts.map +1 -0
  53. package/dist/integrations/github/routes.js +1335 -0
  54. package/dist/integrations/github/routes.js.map +1 -0
  55. package/dist/integrations/github/rules.d.ts +105 -0
  56. package/dist/integrations/github/rules.d.ts.map +1 -0
  57. package/dist/integrations/github/rules.js +551 -0
  58. package/dist/integrations/github/rules.js.map +1 -0
  59. package/dist/integrations/github/sandbox-release.d.ts +53 -0
  60. package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
  61. package/dist/integrations/github/sandbox-release.js +115 -0
  62. package/dist/integrations/github/sandbox-release.js.map +1 -0
  63. package/dist/integrations/github/sandbox.d.ts +281 -0
  64. package/dist/integrations/github/sandbox.d.ts.map +1 -0
  65. package/dist/integrations/github/sandbox.js +547 -0
  66. package/dist/integrations/github/sandbox.js.map +1 -0
  67. package/dist/integrations/github/session-subscriptions.d.ts +26 -0
  68. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -0
  69. package/dist/integrations/github/session-subscriptions.js +192 -0
  70. package/dist/integrations/github/session-subscriptions.js.map +1 -0
  71. package/dist/integrations/github/subscriptions.d.ts +54 -0
  72. package/dist/integrations/github/subscriptions.d.ts.map +1 -0
  73. package/dist/integrations/github/subscriptions.js +62 -0
  74. package/dist/integrations/github/subscriptions.js.map +1 -0
  75. package/dist/integrations/github/token-refresh.d.ts +12 -0
  76. package/dist/integrations/github/token-refresh.d.ts.map +1 -0
  77. package/dist/integrations/github/token-refresh.js +24 -0
  78. package/dist/integrations/github/token-refresh.js.map +1 -0
  79. package/dist/integrations/github/webhook.d.ts +107 -0
  80. package/dist/integrations/github/webhook.d.ts.map +1 -0
  81. package/dist/integrations/github/webhook.js +371 -0
  82. package/dist/integrations/github/webhook.js.map +1 -0
  83. package/dist/integrations/linear/agent-tools.d.ts +34 -0
  84. package/dist/integrations/linear/agent-tools.d.ts.map +1 -0
  85. package/dist/integrations/linear/agent-tools.js +85 -0
  86. package/dist/integrations/linear/agent-tools.js.map +1 -0
  87. package/dist/integrations/linear/integration.d.ts +235 -0
  88. package/dist/integrations/linear/integration.d.ts.map +1 -0
  89. package/dist/integrations/linear/integration.js +676 -0
  90. package/dist/integrations/linear/integration.js.map +1 -0
  91. package/dist/integrations/linear/routes.d.ts +61 -0
  92. package/dist/integrations/linear/routes.d.ts.map +1 -0
  93. package/dist/integrations/linear/routes.js +240 -0
  94. package/dist/integrations/linear/routes.js.map +1 -0
  95. package/dist/integrations/linear/rules.d.ts +44 -0
  96. package/dist/integrations/linear/rules.d.ts.map +1 -0
  97. package/dist/integrations/linear/rules.js +133 -0
  98. package/dist/integrations/linear/rules.js.map +1 -0
  99. package/dist/integrations/linear/storage.d.ts +65 -0
  100. package/dist/integrations/linear/storage.d.ts.map +1 -0
  101. package/dist/integrations/linear/storage.js +1 -0
  102. package/dist/integrations/platform/api-client.d.ts +26 -0
  103. package/dist/integrations/platform/api-client.d.ts.map +1 -0
  104. package/dist/integrations/platform/api-client.js +156 -0
  105. package/dist/integrations/platform/api-client.js.map +1 -0
  106. package/dist/integrations/platform/github/event-worker.d.ts +44 -0
  107. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
  108. package/dist/integrations/platform/github/event-worker.js +349 -0
  109. package/dist/integrations/platform/github/event-worker.js.map +1 -0
  110. package/dist/integrations/platform/github/integration.d.ts +64 -0
  111. package/dist/integrations/platform/github/integration.d.ts.map +1 -0
  112. package/dist/integrations/platform/github/integration.js +1006 -0
  113. package/dist/integrations/platform/github/integration.js.map +1 -0
  114. package/dist/integrations/platform/linear/integration.d.ts +46 -0
  115. package/dist/integrations/platform/linear/integration.d.ts.map +1 -0
  116. package/dist/integrations/platform/linear/integration.js +483 -0
  117. package/dist/integrations/platform/linear/integration.js.map +1 -0
  118. package/dist/integrations/slack/connect-route.d.ts +46 -0
  119. package/dist/integrations/slack/connect-route.d.ts.map +1 -0
  120. package/dist/integrations/slack/connect-route.js +186 -0
  121. package/dist/integrations/slack/connect-route.js.map +1 -0
  122. package/dist/integrations/slack/integration.d.ts +62 -0
  123. package/dist/integrations/slack/integration.d.ts.map +1 -0
  124. package/dist/integrations/slack/integration.js +68 -0
  125. package/dist/integrations/slack/integration.js.map +1 -0
  126. package/dist/integrations/slack/slack.d.ts +200 -0
  127. package/dist/integrations/slack/slack.d.ts.map +1 -0
  128. package/dist/integrations/slack/slack.js +411 -0
  129. package/dist/integrations/slack/slack.js.map +1 -0
  130. package/dist/integrations/workos/integration.d.ts +39 -0
  131. package/dist/integrations/workos/integration.d.ts.map +1 -0
  132. package/dist/integrations/workos/integration.js +91 -0
  133. package/dist/integrations/workos/integration.js.map +1 -0
  134. package/dist/routes/config.d.ts +235 -0
  135. package/dist/routes/config.d.ts.map +1 -0
  136. package/dist/routes/config.js +972 -0
  137. package/dist/routes/config.js.map +1 -0
  138. package/dist/routes/custom-provider-source.d.ts +52 -0
  139. package/dist/routes/custom-provider-source.d.ts.map +1 -0
  140. package/dist/routes/custom-provider-source.js +124 -0
  141. package/dist/routes/custom-provider-source.js.map +1 -0
  142. package/dist/routes/fs.d.ts +151 -0
  143. package/dist/routes/fs.d.ts.map +1 -0
  144. package/dist/routes/fs.js +708 -0
  145. package/dist/routes/fs.js.map +1 -0
  146. package/dist/routes/intake.d.ts +23 -0
  147. package/dist/routes/intake.d.ts.map +1 -0
  148. package/dist/routes/intake.js +179 -0
  149. package/dist/routes/intake.js.map +1 -0
  150. package/dist/routes/oauth.d.ts +43 -0
  151. package/dist/routes/oauth.d.ts.map +1 -0
  152. package/dist/routes/oauth.js +463 -0
  153. package/dist/routes/oauth.js.map +1 -0
  154. package/dist/routes/projects.d.ts +19 -0
  155. package/dist/routes/projects.d.ts.map +1 -0
  156. package/dist/routes/projects.js +461 -0
  157. package/dist/routes/projects.js.map +1 -0
  158. package/dist/routes/provider-credentials.d.ts +81 -0
  159. package/dist/routes/provider-credentials.d.ts.map +1 -0
  160. package/dist/routes/provider-credentials.js +86 -0
  161. package/dist/routes/provider-credentials.js.map +1 -0
  162. package/dist/routes/route.d.ts +48 -0
  163. package/dist/routes/route.d.ts.map +1 -0
  164. package/dist/routes/route.js +15 -0
  165. package/dist/routes/route.js.map +1 -0
  166. package/dist/routes/skills.d.ts +30 -0
  167. package/dist/routes/skills.d.ts.map +1 -0
  168. package/dist/routes/skills.js +168 -0
  169. package/dist/routes/skills.js.map +1 -0
  170. package/dist/routes/surface.d.ts +107 -0
  171. package/dist/routes/surface.d.ts.map +1 -0
  172. package/dist/routes/surface.js +297 -0
  173. package/dist/routes/surface.js.map +1 -0
  174. package/dist/routes/tenant-credentials.d.ts +74 -0
  175. package/dist/routes/tenant-credentials.d.ts.map +1 -0
  176. package/dist/routes/tenant-credentials.js +156 -0
  177. package/dist/routes/tenant-credentials.js.map +1 -0
  178. package/dist/routes/work-items.d.ts +40 -0
  179. package/dist/routes/work-items.d.ts.map +1 -0
  180. package/dist/routes/work-items.js +695 -0
  181. package/dist/routes/work-items.js.map +1 -0
  182. package/dist/rules/binding-context.d.ts +5 -0
  183. package/dist/rules/binding-context.d.ts.map +1 -0
  184. package/dist/rules/binding-context.js +28 -0
  185. package/dist/rules/binding-context.js.map +1 -0
  186. package/dist/rules/defaults.d.ts +9 -0
  187. package/dist/rules/defaults.d.ts.map +1 -0
  188. package/dist/rules/defaults.js +322 -0
  189. package/dist/rules/defaults.js.map +1 -0
  190. package/dist/rules/dispatcher.d.ts +43 -0
  191. package/dist/rules/dispatcher.d.ts.map +1 -0
  192. package/dist/rules/dispatcher.js +547 -0
  193. package/dist/rules/dispatcher.js.map +1 -0
  194. package/dist/rules/index.d.ts +7 -0
  195. package/dist/rules/index.d.ts.map +1 -0
  196. package/dist/rules/index.js +5 -0
  197. package/dist/rules/processor.d.ts +54 -0
  198. package/dist/rules/processor.d.ts.map +1 -0
  199. package/dist/rules/processor.js +378 -0
  200. package/dist/rules/processor.js.map +1 -0
  201. package/dist/rules/resolve.d.ts +17 -0
  202. package/dist/rules/resolve.d.ts.map +1 -0
  203. package/dist/rules/resolve.js +30 -0
  204. package/dist/rules/resolve.js.map +1 -0
  205. package/dist/rules/start-coordinator.d.ts +60 -0
  206. package/dist/rules/start-coordinator.d.ts.map +1 -0
  207. package/dist/rules/start-coordinator.js +175 -0
  208. package/dist/rules/start-coordinator.js.map +1 -0
  209. package/dist/rules/tools.d.ts +10 -0
  210. package/dist/rules/tools.d.ts.map +1 -0
  211. package/dist/rules/tools.js +59 -0
  212. package/dist/rules/tools.js.map +1 -0
  213. package/dist/rules/transition-service.d.ts +49 -0
  214. package/dist/rules/transition-service.d.ts.map +1 -0
  215. package/dist/rules/transition-service.js +232 -0
  216. package/dist/rules/transition-service.js.map +1 -0
  217. package/dist/rules/types.d.ts +261 -0
  218. package/dist/rules/types.d.ts.map +1 -0
  219. package/dist/rules/types.js +43 -0
  220. package/dist/rules/types.js.map +1 -0
  221. package/dist/rules/validation.d.ts +11 -0
  222. package/dist/rules/validation.d.ts.map +1 -0
  223. package/dist/rules/validation.js +308 -0
  224. package/dist/rules/validation.js.map +1 -0
  225. package/dist/sandbox/fleet.d.ts +222 -0
  226. package/dist/sandbox/fleet.d.ts.map +1 -0
  227. package/dist/sandbox/fleet.js +322 -0
  228. package/dist/sandbox/fleet.js.map +1 -0
  229. package/dist/sandbox/reattach.d.ts +3 -0
  230. package/dist/sandbox/reattach.d.ts.map +1 -0
  231. package/dist/sandbox/reattach.js +16 -0
  232. package/dist/sandbox/reattach.js.map +1 -0
  233. package/dist/server-error.d.ts +12 -0
  234. package/dist/server-error.d.ts.map +1 -0
  235. package/dist/server-error.js +16 -0
  236. package/dist/server-error.js.map +1 -0
  237. package/dist/session/factory-session.d.ts +116 -0
  238. package/dist/session/factory-session.d.ts.map +1 -0
  239. package/dist/session/factory-session.js +162 -0
  240. package/dist/session/factory-session.js.map +1 -0
  241. package/dist/skills/service.d.ts +48 -0
  242. package/dist/skills/service.d.ts.map +1 -0
  243. package/dist/skills/service.js +40 -0
  244. package/dist/skills/service.js.map +1 -0
  245. package/dist/spa-static.d.ts +33 -0
  246. package/dist/spa-static.d.ts.map +1 -0
  247. package/dist/spa-static.js +104 -0
  248. package/dist/spa-static.js.map +1 -0
  249. package/dist/state-signing.d.ts +59 -0
  250. package/dist/state-signing.d.ts.map +1 -0
  251. package/dist/state-signing.js +83 -0
  252. package/dist/state-signing.js.map +1 -0
  253. package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
  254. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
  255. package/dist/storage/domains/audit/agent-audit.js +66 -0
  256. package/dist/storage/domains/audit/agent-audit.js.map +1 -0
  257. package/dist/storage/domains/audit/base.d.ts +140 -0
  258. package/dist/storage/domains/audit/base.d.ts.map +1 -0
  259. package/dist/storage/domains/audit/base.js +184 -0
  260. package/dist/storage/domains/audit/base.js.map +1 -0
  261. package/dist/storage/domains/audit/domain.d.ts +76 -0
  262. package/dist/storage/domains/audit/domain.d.ts.map +1 -0
  263. package/dist/storage/domains/audit/domain.js +269 -0
  264. package/dist/storage/domains/audit/domain.js.map +1 -0
  265. package/dist/storage/domains/channel-identity/base.d.ts +96 -0
  266. package/dist/storage/domains/channel-identity/base.d.ts.map +1 -0
  267. package/dist/storage/domains/channel-identity/base.js +149 -0
  268. package/dist/storage/domains/channel-identity/base.js.map +1 -0
  269. package/dist/storage/domains/credentials/base.d.ts +134 -0
  270. package/dist/storage/domains/credentials/base.d.ts.map +1 -0
  271. package/dist/storage/domains/credentials/base.js +273 -0
  272. package/dist/storage/domains/credentials/base.js.map +1 -0
  273. package/dist/storage/domains/custom-providers/base.d.ts +57 -0
  274. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
  275. package/dist/storage/domains/custom-providers/base.js +145 -0
  276. package/dist/storage/domains/custom-providers/base.js.map +1 -0
  277. package/dist/storage/domains/intake/base.d.ts +38 -0
  278. package/dist/storage/domains/intake/base.d.ts.map +1 -0
  279. package/dist/storage/domains/intake/base.js +80 -0
  280. package/dist/storage/domains/intake/base.js.map +1 -0
  281. package/dist/storage/domains/integrations/base.d.ts +126 -0
  282. package/dist/storage/domains/integrations/base.d.ts.map +1 -0
  283. package/dist/storage/domains/integrations/base.js +317 -0
  284. package/dist/storage/domains/integrations/base.js.map +1 -0
  285. package/dist/storage/domains/memory-settings/base.d.ts +62 -0
  286. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
  287. package/dist/storage/domains/memory-settings/base.js +126 -0
  288. package/dist/storage/domains/memory-settings/base.js.map +1 -0
  289. package/dist/storage/domains/model-packs/base.d.ts +49 -0
  290. package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
  291. package/dist/storage/domains/model-packs/base.js +96 -0
  292. package/dist/storage/domains/model-packs/base.js.map +1 -0
  293. package/dist/storage/domains/projects/base.d.ts +58 -0
  294. package/dist/storage/domains/projects/base.d.ts.map +1 -0
  295. package/dist/storage/domains/projects/base.js +111 -0
  296. package/dist/storage/domains/projects/base.js.map +1 -0
  297. package/dist/storage/domains/queue-health/base.d.ts +52 -0
  298. package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
  299. package/dist/storage/domains/queue-health/base.js +122 -0
  300. package/dist/storage/domains/queue-health/base.js.map +1 -0
  301. package/dist/storage/domains/source-control/base.d.ts +360 -0
  302. package/dist/storage/domains/source-control/base.d.ts.map +1 -0
  303. package/dist/storage/domains/source-control/base.js +914 -0
  304. package/dist/storage/domains/source-control/base.js.map +1 -0
  305. package/dist/storage/domains/source-control/inmemory.d.ts +175 -0
  306. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
  307. package/dist/storage/domains/source-control/inmemory.js +380 -0
  308. package/dist/storage/domains/source-control/inmemory.js.map +1 -0
  309. package/dist/storage/domains/work-items/base.d.ts +412 -0
  310. package/dist/storage/domains/work-items/base.d.ts.map +1 -0
  311. package/dist/storage/domains/work-items/base.js +1320 -0
  312. package/dist/storage/domains/work-items/base.js.map +1 -0
  313. package/dist/storage/domains/work-items/metrics.d.ts +101 -0
  314. package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
  315. package/dist/storage/domains/work-items/metrics.js +221 -0
  316. package/dist/storage/domains/work-items/metrics.js.map +1 -0
  317. package/dist/timing.d.ts +15 -0
  318. package/dist/timing.d.ts.map +1 -0
  319. package/dist/timing.js +34 -0
  320. package/dist/timing.js.map +1 -0
  321. package/dist/workspace.d.ts +24 -0
  322. package/dist/workspace.d.ts.map +1 -0
  323. package/dist/workspace.js +287 -0
  324. package/dist/workspace.js.map +1 -0
  325. package/factory-skills/configure-factory-rules/SKILL.md +67 -0
  326. package/factory-skills/factory-plan/SKILL.md +61 -0
  327. package/factory-skills/factory-review/SKILL.md +157 -0
  328. package/factory-skills/factory-triage/SKILL.md +99 -0
  329. package/package.json +83 -0
@@ -0,0 +1,184 @@
1
+ import { FactoryStorageDomain } from "@mastra/core/storage";
2
+ //#region src/storage/domains/audit/base.ts
3
+ /**
4
+ * Factory audit events domain — the append-only "who did what, when" trail
5
+ * behind the software factory.
6
+ *
7
+ * One `audit_events` row records a single audited mutation (work-item change,
8
+ * stage move, run start, worktree create/delete, git action, intake config
9
+ * change). Rows are append-only: there is no update/delete API, and the table
10
+ * is the local source of truth even when the WorkOS Audit Logs mirror is
11
+ * unavailable.
12
+ *
13
+ * Tenancy is **org-first**, like `work_items`: events are scoped by `org_id`
14
+ * and (usually) `factory_project_id`; `actor_id` records who acted but never
15
+ * scopes reads.
16
+ *
17
+ * v1 action taxonomy (register these in the WorkOS dashboard under
18
+ * Audit Logs → Events for the export mirror to accept them):
19
+ * - factory.work_item.created
20
+ * - factory.work_item.updated
21
+ * - factory.work_item.stage_moved
22
+ * - factory.work_item.deleted
23
+ * - factory.run.started
24
+ * - factory.worktree.created
25
+ * - factory.worktree.deleted
26
+ * - factory.triage.started
27
+ * - factory.git.commit
28
+ * - factory.git.push
29
+ * - factory.git.pr_opened
30
+ * - factory.intake.config_updated
31
+ *
32
+ * v1.1 adds agent-level actions (also register these in WorkOS):
33
+ * - factory.agent.commit
34
+ * - factory.agent.push
35
+ * - factory.agent.pr_opened
36
+ *
37
+ * Agent events carry `actor_type = 'agent'` with `actor_id = 'agent:<threadId>'`
38
+ * and `metadata.startedBy = <userId>` chaining accountability back to the human
39
+ * whose message drove the run.
40
+ */
41
+ /** Metadata is a bounded summary — never full payloads, never secrets. */
42
+ const MAX_METADATA_JSON_LENGTH = 4096;
43
+ const DEFAULT_PAGE_SIZE = 50;
44
+ const MAX_PAGE_SIZE = 200;
45
+ /** Truncate oversized metadata rather than dropping the whole event. */
46
+ function boundAuditMetadata(metadata) {
47
+ if (!metadata) return {};
48
+ try {
49
+ if (JSON.stringify(metadata).length <= MAX_METADATA_JSON_LENGTH) return metadata;
50
+ } catch {
51
+ return { truncated: true };
52
+ }
53
+ return { truncated: true };
54
+ }
55
+ /** Normalize a requested page size to a finite integer in `[1, MAX_PAGE_SIZE]`. */
56
+ function clampAuditLimit(limit) {
57
+ return Math.min(Math.max(typeof limit === "number" && Number.isFinite(limit) ? Math.trunc(limit) : DEFAULT_PAGE_SIZE, 1), MAX_PAGE_SIZE);
58
+ }
59
+ /** Encode the `(occurredAt, id)` keyset cursor of a row. */
60
+ function encodeAuditCursor(row) {
61
+ return `${row.occurredAt.toISOString()}_${row.id}`;
62
+ }
63
+ /** Decode a cursor back into its `(occurredAt, id)` parts, or `undefined`. */
64
+ function decodeAuditCursor(cursor) {
65
+ const sep = cursor.lastIndexOf("_");
66
+ if (sep <= 0) return void 0;
67
+ const occurredAt = new Date(cursor.slice(0, sep));
68
+ const id = cursor.slice(sep + 1);
69
+ if (Number.isNaN(occurredAt.getTime()) || !id) return void 0;
70
+ return {
71
+ occurredAt,
72
+ id
73
+ };
74
+ }
75
+ const AUDIT_EVENTS_SCHEMA = {
76
+ name: "audit_events",
77
+ columns: {
78
+ id: { type: "uuid-pk" },
79
+ org_id: { type: "text" },
80
+ actor_id: { type: "text" },
81
+ actor_type: {
82
+ type: "text",
83
+ default: "human"
84
+ },
85
+ action: { type: "text" },
86
+ targets: { type: "json" },
87
+ metadata: { type: "json" },
88
+ factory_project_id: {
89
+ type: "text",
90
+ nullable: true
91
+ },
92
+ project_repository_id: {
93
+ type: "text",
94
+ nullable: true
95
+ },
96
+ context: { type: "json" },
97
+ occurred_at: { type: "timestamp" }
98
+ },
99
+ indexes: [{
100
+ name: "audit_events_org_occurred_idx",
101
+ columns: ["org_id", "occurred_at"]
102
+ }, {
103
+ name: "audit_events_org_project_occurred_idx",
104
+ columns: [
105
+ "org_id",
106
+ "factory_project_id",
107
+ "occurred_at"
108
+ ]
109
+ }]
110
+ };
111
+ function toRow(db) {
112
+ return {
113
+ id: db.id,
114
+ orgId: db.org_id,
115
+ actorId: db.actor_id,
116
+ actorType: db.actor_type,
117
+ action: db.action,
118
+ targets: db.targets,
119
+ metadata: db.metadata,
120
+ factoryProjectId: db.factory_project_id,
121
+ projectRepositoryId: db.project_repository_id,
122
+ context: db.context,
123
+ occurredAt: db.occurred_at
124
+ };
125
+ }
126
+ /**
127
+ * Audit event storage, written once against the generic `FactoryStorageOps`
128
+ * surface. `record()` normalizes inputs (defaults, metadata bounding);
129
+ * `list()` is keyset-paginated on `(occurred_at, id)` newest-first.
130
+ */
131
+ var AuditStorage = class extends FactoryStorageDomain {
132
+ constructor() {
133
+ super("audit");
134
+ }
135
+ async init() {
136
+ await this.ensureCollections([AUDIT_EVENTS_SCHEMA]);
137
+ }
138
+ async dangerouslyClearAll() {
139
+ await this.ops.deleteMany("audit_events", {});
140
+ }
141
+ get #db() {
142
+ return this.ops;
143
+ }
144
+ /** Append one audit event. Throws on failure — swallow-on-failure lives in the caller. */
145
+ async record(input) {
146
+ return toRow(await this.#db.insertOne("audit_events", {
147
+ org_id: input.orgId,
148
+ actor_id: input.actorId,
149
+ actor_type: input.actorType ?? "human",
150
+ action: input.action,
151
+ targets: input.targets,
152
+ metadata: boundAuditMetadata(input.metadata),
153
+ factory_project_id: input.factoryProjectId ?? null,
154
+ project_repository_id: input.projectRepositoryId ?? null,
155
+ context: input.context ?? {},
156
+ occurred_at: input.occurredAt ?? /* @__PURE__ */ new Date()
157
+ }));
158
+ }
159
+ /** List an org's audit events newest-first with keyset pagination. */
160
+ async list(input) {
161
+ const limit = clampAuditLimit(input.limit);
162
+ const where = { org_id: input.orgId };
163
+ if (input.factoryProjectId) where.factory_project_id = input.factoryProjectId;
164
+ if (input.actions && input.actions.length > 0) where.action = { in: input.actions };
165
+ if (input.actorId) where.actor_id = input.actorId;
166
+ const cursor = input.before ? decodeAuditCursor(input.before) : void 0;
167
+ const rows = await this.#db.findMany("audit_events", where, {
168
+ orderBy: [["occurred_at", "desc"], ["id", "desc"]],
169
+ limit: limit + 1,
170
+ ...cursor ? { cursor: { values: [cursor.occurredAt, cursor.id] } } : {}
171
+ });
172
+ const events = rows.slice(0, limit).map(toRow);
173
+ const hasMore = rows.length > limit;
174
+ const last = events[events.length - 1];
175
+ return {
176
+ events,
177
+ ...hasMore && last ? { nextCursor: encodeAuditCursor(last) } : {}
178
+ };
179
+ }
180
+ };
181
+ //#endregion
182
+ export { AUDIT_EVENTS_SCHEMA, AuditStorage, boundAuditMetadata, clampAuditLimit, decodeAuditCursor, encodeAuditCursor };
183
+
184
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/audit/base.ts"],"sourcesContent":["/**\n * Factory audit events domain — the append-only \"who did what, when\" trail\n * behind the software factory.\n *\n * One `audit_events` row records a single audited mutation (work-item change,\n * stage move, run start, worktree create/delete, git action, intake config\n * change). Rows are append-only: there is no update/delete API, and the table\n * is the local source of truth even when the WorkOS Audit Logs mirror is\n * unavailable.\n *\n * Tenancy is **org-first**, like `work_items`: events are scoped by `org_id`\n * and (usually) `factory_project_id`; `actor_id` records who acted but never\n * scopes reads.\n *\n * v1 action taxonomy (register these in the WorkOS dashboard under\n * Audit Logs → Events for the export mirror to accept them):\n * - factory.work_item.created\n * - factory.work_item.updated\n * - factory.work_item.stage_moved\n * - factory.work_item.deleted\n * - factory.run.started\n * - factory.worktree.created\n * - factory.worktree.deleted\n * - factory.triage.started\n * - factory.git.commit\n * - factory.git.push\n * - factory.git.pr_opened\n * - factory.intake.config_updated\n *\n * v1.1 adds agent-level actions (also register these in WorkOS):\n * - factory.agent.commit\n * - factory.agent.push\n * - factory.agent.pr_opened\n *\n * Agent events carry `actor_type = 'agent'` with `actor_id = 'agent:<threadId>'`\n * and `metadata.startedBy = <userId>` chaining accountability back to the human\n * whose message drove the run.\n */\n\nimport { FactoryStorageDomain } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\n\n/** What an audit event acted on (WorkOS Audit Logs target shape). */\nexport interface AuditTarget {\n /** Target kind, e.g. 'work_item', 'worktree', 'issue', 'pull_request'. */\n type: string;\n /** Stable identifier of the target (row id, branch name, issue number...). */\n id: string;\n /** Human-readable label (work-item title, branch name...). */\n name?: string;\n}\n\n/** Who performed the audited action. */\nexport type AuditActorType = 'human' | 'agent';\n\n/** Request context captured alongside the event. */\nexport interface AuditContext {\n /** Client IP (first hop of `x-forwarded-for`) when available. */\n location?: string;\n /** Request `user-agent` header when available. */\n userAgent?: string;\n}\n\n/** One persisted audit event. */\nexport interface AuditEventRow {\n id: string;\n /** Owning WorkOS organization id — the trail is org-wide. */\n orgId: string;\n /** WorkOS user id of whoever performed the action, or `agent:<threadId>`. */\n actorId: string;\n /** Whether a human or an agent (inside a run) performed the action. */\n actorType: AuditActorType;\n /** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */\n action: string;\n /** What was acted on. */\n targets: AuditTarget[];\n /** Bounded event summary — never full payloads, never secrets. */\n metadata: Record<string, unknown>;\n /** Factory project the event is scoped to; null for org-level events. */\n factoryProjectId: string | null;\n /** Project-repository link affected by a repository-specific action. */\n projectRepositoryId: string | null;\n /** Request context (`x-forwarded-for` / `user-agent`). */\n context: AuditContext;\n occurredAt: Date;\n}\n\nexport interface RecordAuditEventInput {\n orgId: string;\n actorId: string;\n /** Who performed the action; defaults to 'human'. */\n actorType?: AuditActorType;\n /** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */\n action: string;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n context?: AuditContext;\n occurredAt?: Date;\n}\n\n/** A fully-normalized event ready to persist (id assigned on insert). */\nexport type AuditEventInsert = Omit<AuditEventRow, 'id'>;\n\nexport interface ListAuditEventsInput {\n orgId: string;\n factoryProjectId?: string;\n /** Restrict to these actions (exact match). */\n actions?: string[];\n actorId?: string;\n /** Opaque cursor from a previous page (`nextCursor`). */\n before?: string;\n limit?: number;\n}\n\nexport interface AuditEventPage {\n events: AuditEventRow[];\n /** Pass back as `before` to fetch the next (older) page; absent at the end. */\n nextCursor?: string;\n}\n\n/** Metadata is a bounded summary — never full payloads, never secrets. */\nconst MAX_METADATA_JSON_LENGTH = 4096;\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 200;\n\n/** Truncate oversized metadata rather than dropping the whole event. */\nexport function boundAuditMetadata(metadata: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!metadata) return {};\n try {\n if (JSON.stringify(metadata).length <= MAX_METADATA_JSON_LENGTH) return metadata;\n } catch {\n return { truncated: true };\n }\n return { truncated: true };\n}\n\n/** Normalize a requested page size to a finite integer in `[1, MAX_PAGE_SIZE]`. */\nexport function clampAuditLimit(limit: number | undefined): number {\n const normalized = typeof limit === 'number' && Number.isFinite(limit) ? Math.trunc(limit) : DEFAULT_PAGE_SIZE;\n return Math.min(Math.max(normalized, 1), MAX_PAGE_SIZE);\n}\n\n/** Encode the `(occurredAt, id)` keyset cursor of a row. */\nexport function encodeAuditCursor(row: AuditEventRow): string {\n return `${row.occurredAt.toISOString()}_${row.id}`;\n}\n\n/** Decode a cursor back into its `(occurredAt, id)` parts, or `undefined`. */\nexport function decodeAuditCursor(cursor: string): { occurredAt: Date; id: string } | undefined {\n const sep = cursor.lastIndexOf('_');\n if (sep <= 0) return undefined;\n const occurredAt = new Date(cursor.slice(0, sep));\n const id = cursor.slice(sep + 1);\n if (Number.isNaN(occurredAt.getTime()) || !id) return undefined;\n return { occurredAt, id };\n}\n\nexport const AUDIT_EVENTS_SCHEMA: CollectionSchema = {\n name: 'audit_events',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n actor_id: { type: 'text' },\n actor_type: { type: 'text', default: 'human' },\n action: { type: 'text' },\n targets: { type: 'json' },\n metadata: { type: 'json' },\n factory_project_id: { type: 'text', nullable: true },\n project_repository_id: { type: 'text', nullable: true },\n context: { type: 'json' },\n occurred_at: { type: 'timestamp' },\n },\n indexes: [\n { name: 'audit_events_org_occurred_idx', columns: ['org_id', 'occurred_at'] },\n { name: 'audit_events_org_project_occurred_idx', columns: ['org_id', 'factory_project_id', 'occurred_at'] },\n ],\n};\n\n/** Column shape of one `audit_events` row as returned by ops. */\ninterface AuditEventDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n actor_id: string;\n actor_type: AuditActorType;\n action: string;\n targets: AuditTarget[];\n metadata: Record<string, unknown>;\n factory_project_id: string | null;\n project_repository_id: string | null;\n context: AuditContext;\n occurred_at: Date;\n}\n\nfunction toRow(db: AuditEventDbRow): AuditEventRow {\n return {\n id: db.id,\n orgId: db.org_id,\n actorId: db.actor_id,\n actorType: db.actor_type,\n action: db.action,\n targets: db.targets,\n metadata: db.metadata,\n factoryProjectId: db.factory_project_id,\n projectRepositoryId: db.project_repository_id,\n context: db.context,\n occurredAt: db.occurred_at,\n };\n}\n\n/**\n * Audit event storage, written once against the generic `FactoryStorageOps`\n * surface. `record()` normalizes inputs (defaults, metadata bounding);\n * `list()` is keyset-paginated on `(occurred_at, id)` newest-first.\n */\nexport class AuditStorage extends FactoryStorageDomain {\n constructor() {\n super('audit');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([AUDIT_EVENTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('audit_events', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Append one audit event. Throws on failure — swallow-on-failure lives in the caller. */\n async record(input: RecordAuditEventInput): Promise<AuditEventRow> {\n const inserted = await this.#db.insertOne<AuditEventDbRow>('audit_events', {\n org_id: input.orgId,\n actor_id: input.actorId,\n actor_type: input.actorType ?? 'human',\n action: input.action,\n targets: input.targets,\n metadata: boundAuditMetadata(input.metadata),\n factory_project_id: input.factoryProjectId ?? null,\n project_repository_id: input.projectRepositoryId ?? null,\n context: input.context ?? {},\n occurred_at: input.occurredAt ?? new Date(),\n });\n return toRow(inserted);\n }\n\n /** List an org's audit events newest-first with keyset pagination. */\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n const limit = clampAuditLimit(input.limit);\n\n const where: CollectionWhere = { org_id: input.orgId };\n if (input.factoryProjectId) where.factory_project_id = input.factoryProjectId;\n if (input.actions && input.actions.length > 0) where.action = { in: input.actions };\n if (input.actorId) where.actor_id = input.actorId;\n\n const cursor = input.before ? decodeAuditCursor(input.before) : undefined;\n\n const rows = await this.#db.findMany<AuditEventDbRow>('audit_events', where, {\n orderBy: [\n ['occurred_at', 'desc'],\n ['id', 'desc'],\n ],\n limit: limit + 1,\n ...(cursor ? { cursor: { values: [cursor.occurredAt, cursor.id] } } : {}),\n });\n\n const events = rows.slice(0, limit).map(toRow);\n const hasMore = rows.length > limit;\n const last = events[events.length - 1];\n return {\n events,\n ...(hasMore && last ? { nextCursor: encodeAuditCursor(last) } : {}),\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { RequestContext } from '@mastra/core/request-context';
2
+ import type { ApiRoute, IUserProvider } from '@mastra/core/server';
3
+ import type { Context } from 'hono';
4
+ import type { RouteAuth } from '../../../routes/route.js';
5
+ import type { FactoryProjectsStorage } from '../projects/base.js';
6
+ import type { AuditContext, AuditEventPage, AuditEventRow, AuditStorage, AuditTarget, ListAuditEventsInput, RecordAuditEventInput } from './base.js';
7
+ export interface EmitAuditInput {
8
+ action: string;
9
+ factoryProjectId?: string;
10
+ projectRepositoryId?: string;
11
+ targets: AuditTarget[];
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ export interface EmitAgentAuditInput {
15
+ action: string;
16
+ targets: AuditTarget[];
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface AuditEmitter {
20
+ emit(args: {
21
+ context: Context;
22
+ input: EmitAuditInput;
23
+ }): Promise<void>;
24
+ }
25
+ export interface AuditAgentEmitter {
26
+ emitAgent(args: {
27
+ requestContext: RequestContext;
28
+ input: EmitAgentAuditInput;
29
+ }): Promise<void>;
30
+ }
31
+ /** Best-effort destination for locally persisted audit events (e.g. an integration's audit log). */
32
+ export interface AuditSink {
33
+ id: string;
34
+ audit?(args: {
35
+ event: AuditEventRow;
36
+ }): Promise<void>;
37
+ }
38
+ export interface AuditActorProfile {
39
+ id: string;
40
+ name: string;
41
+ avatarUrl?: string;
42
+ }
43
+ export interface AuditDomainOptions {
44
+ auth: RouteAuth;
45
+ /** Audit storage domain handle. */
46
+ audit: AuditStorage;
47
+ /** Projects domain handle, used to scope the audit trail route. */
48
+ projects: FactoryProjectsStorage;
49
+ /** Resolve persisted human actor ids to display names and profile images. */
50
+ users?: Pick<IUserProvider, 'getUser' | 'getUsers'>;
51
+ /** Best-effort fan-out destinations notified after each recorded event. */
52
+ sinks?: AuditSink[];
53
+ /** Resolve the acting tenant for agent-emitted events from the request context. */
54
+ agentTenant?: (requestContext: RequestContext) => {
55
+ orgId?: string;
56
+ userId?: string;
57
+ } | undefined;
58
+ }
59
+ export declare function auditRequestContext(c: Context): AuditContext;
60
+ /** Factory-owned audit behavior backed by the audit storage domain. */
61
+ export declare class AuditDomain implements AuditEmitter, AuditAgentEmitter {
62
+ #private;
63
+ constructor({ auth, audit, projects, users, sinks, agentTenant }: AuditDomainOptions);
64
+ record(input: RecordAuditEventInput): Promise<AuditEventRow | null>;
65
+ list(input: ListAuditEventsInput): Promise<AuditEventPage>;
66
+ emit({ context, input }: {
67
+ context: Context;
68
+ input: EmitAuditInput;
69
+ }): Promise<void>;
70
+ emitAgent({ requestContext, input, }: {
71
+ requestContext: RequestContext;
72
+ input: EmitAgentAuditInput;
73
+ }): Promise<void>;
74
+ routes(): ApiRoute[];
75
+ }
76
+ //# sourceMappingURL=domain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/domain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,269 @@
1
+ import { registerApiRoute } from "@mastra/core/server";
2
+ //#region src/storage/domains/audit/domain.ts
3
+ /**
4
+ * Reserved metadata key on `AuditEventRow.metadata` that carries the acting
5
+ * human user's display name and avatar captured from the auth context at
6
+ * record time. Read back by `#resolveActorProfiles` so names/avatars work
7
+ * for every auth provider without requiring an `IUserProvider.getUser(id)`
8
+ * implementation — the Studio provider proxies through the shared API and
9
+ * cannot resolve arbitrary users by id.
10
+ */
11
+ const ACTOR_PROFILE_METADATA_KEY = "__actorProfile";
12
+ function readStoredActorProfile(metadata) {
13
+ if (!metadata) return void 0;
14
+ const raw = metadata[ACTOR_PROFILE_METADATA_KEY];
15
+ if (!raw || typeof raw !== "object") return void 0;
16
+ const profile = raw;
17
+ const name = typeof profile.name === "string" && profile.name.trim() ? profile.name.trim() : void 0;
18
+ const avatarUrl = typeof profile.avatarUrl === "string" && profile.avatarUrl.trim() ? profile.avatarUrl.trim() : void 0;
19
+ if (!name && !avatarUrl) return void 0;
20
+ return {
21
+ ...name ? { name } : {},
22
+ ...avatarUrl ? { avatarUrl } : {}
23
+ };
24
+ }
25
+ /** Read the auth gate's stashed user directly from the Hono context. */
26
+ function readFactoryAuthUserFromContext(context) {
27
+ return context.get("factoryAuthUser") ?? void 0;
28
+ }
29
+ function buildActorProfileMetadata(user) {
30
+ const name = user.name?.trim() || user.email?.trim();
31
+ const avatarUrl = user.avatarUrl?.trim();
32
+ if (!name && !avatarUrl) return void 0;
33
+ return {
34
+ ...name ? { name } : {},
35
+ ...avatarUrl ? { avatarUrl } : {}
36
+ };
37
+ }
38
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
39
+ const MAX_ACTION_FILTERS = 16;
40
+ const MAX_ACTOR_PROFILES = 100;
41
+ const AUTOMATION_ACTORS = /* @__PURE__ */ new Set([
42
+ "factory",
43
+ "system",
44
+ "automation",
45
+ "factory-rule-dispatcher",
46
+ "factory-tool-result-rule"
47
+ ]);
48
+ function loose(c) {
49
+ return c;
50
+ }
51
+ function parseActionsParam(raw) {
52
+ if (!raw) return void 0;
53
+ const actions = raw.split(",").map((action) => action.trim()).filter(Boolean).slice(0, MAX_ACTION_FILTERS);
54
+ return actions.length > 0 ? actions : void 0;
55
+ }
56
+ function isHumanActorId(actorId) {
57
+ if (!actorId) return false;
58
+ return !AUTOMATION_ACTORS.has(actorId) && !actorId.startsWith("agent:") && !actorId.startsWith("github:");
59
+ }
60
+ function parseActorIdsParam(raw) {
61
+ if (!raw) return [];
62
+ return [...new Set(raw.split(",").map((actorId) => actorId.trim()).filter(isHumanActorId))].slice(0, MAX_ACTOR_PROFILES);
63
+ }
64
+ function parseLimitParam(raw) {
65
+ if (!raw) return void 0;
66
+ const limit = Number.parseInt(raw, 10);
67
+ return Number.isFinite(limit) ? limit : void 0;
68
+ }
69
+ function auditRequestContext(c) {
70
+ const location = c.req.header("x-forwarded-for")?.split(",")[0]?.trim();
71
+ const userAgent = c.req.header("user-agent");
72
+ return {
73
+ ...location ? { location } : {},
74
+ ...userAgent ? { userAgent } : {}
75
+ };
76
+ }
77
+ /** Factory-owned audit behavior backed by the audit storage domain. */
78
+ var AuditDomain = class {
79
+ #auth;
80
+ #audit;
81
+ #projects;
82
+ #users;
83
+ #sinks;
84
+ #agentTenant;
85
+ constructor({ auth, audit, projects, users, sinks = [], agentTenant }) {
86
+ this.#auth = auth;
87
+ this.#audit = audit;
88
+ this.#projects = projects;
89
+ this.#users = users;
90
+ this.#sinks = sinks;
91
+ this.#agentTenant = agentTenant;
92
+ const ids = /* @__PURE__ */ new Set();
93
+ for (const sink of this.#sinks) {
94
+ if (!sink.id) throw new Error("Audit integration id must not be empty");
95
+ if (ids.has(sink.id)) throw new Error(`Duplicate audit integration id '${sink.id}'`);
96
+ ids.add(sink.id);
97
+ }
98
+ }
99
+ async record(input) {
100
+ try {
101
+ await this.#audit.ensureReady();
102
+ const row = await this.#audit.record(input);
103
+ for (const sink of this.#sinks) {
104
+ if (!sink.audit) continue;
105
+ Promise.resolve().then(() => sink.audit?.({ event: row })).catch((err) => {
106
+ console.warn("[Audit] Audit integration failed", {
107
+ integration: sink.id,
108
+ action: row.action,
109
+ error: err instanceof Error ? err.message : String(err)
110
+ });
111
+ });
112
+ }
113
+ return row;
114
+ } catch (err) {
115
+ console.warn("[Audit] Failed to record audit event", {
116
+ action: input.action,
117
+ error: err instanceof Error ? err.message : String(err)
118
+ });
119
+ return null;
120
+ }
121
+ }
122
+ async list(input) {
123
+ await this.#audit.ensureReady();
124
+ return this.#audit.list(input);
125
+ }
126
+ async emit({ context, input }) {
127
+ try {
128
+ const tenant = this.#auth.tenant(context);
129
+ if (!tenant?.orgId) return;
130
+ const user = readFactoryAuthUserFromContext(context);
131
+ const actorProfile = user ? buildActorProfileMetadata(user) : void 0;
132
+ const metadata = actorProfile ? {
133
+ ...input.metadata,
134
+ [ACTOR_PROFILE_METADATA_KEY]: actorProfile
135
+ } : input.metadata;
136
+ await this.record({
137
+ orgId: tenant.orgId,
138
+ actorId: tenant.userId,
139
+ action: input.action,
140
+ targets: input.targets,
141
+ metadata,
142
+ factoryProjectId: input.factoryProjectId,
143
+ projectRepositoryId: input.projectRepositoryId,
144
+ context: auditRequestContext(context)
145
+ });
146
+ } catch (err) {
147
+ console.warn("[Audit] Failed to emit audit event", {
148
+ action: input.action,
149
+ error: err instanceof Error ? err.message : String(err)
150
+ });
151
+ }
152
+ }
153
+ async emitAgent({ requestContext, input }) {
154
+ try {
155
+ const context = requestContext.get("controller");
156
+ const tenant = this.#agentTenant?.(requestContext);
157
+ const orgId = tenant?.orgId;
158
+ const userId = tenant?.userId;
159
+ const threadId = context?.threadId;
160
+ const state = context?.getState();
161
+ if (!orgId || !userId || !threadId || !state?.factoryProjectId) return;
162
+ const modeId = context.session.modeId?.trim();
163
+ const modelId = context.session.modelId?.trim();
164
+ await this.record({
165
+ orgId,
166
+ actorId: `agent:${threadId}`,
167
+ actorType: "agent",
168
+ action: input.action,
169
+ targets: input.targets,
170
+ metadata: {
171
+ ...input.metadata,
172
+ startedBy: userId,
173
+ ...modeId ? { agentName: `${modeId} agent` } : {},
174
+ ...modelId ? { modelId } : {}
175
+ },
176
+ factoryProjectId: state.factoryProjectId,
177
+ projectRepositoryId: state.projectRepositoryId,
178
+ context: {}
179
+ });
180
+ } catch (err) {
181
+ console.warn("[Audit] Failed to emit agent audit event", {
182
+ action: input.action,
183
+ error: err instanceof Error ? err.message : String(err)
184
+ });
185
+ }
186
+ }
187
+ async #resolveActorProfiles(events, requestedActorIds = []) {
188
+ const humanActorIds = [...new Set([...requestedActorIds, ...events.filter((event) => event.actorType === "human").map((event) => event.actorId)].filter(isHumanActorId))].slice(0, MAX_ACTOR_PROFILES);
189
+ if (humanActorIds.length === 0) return {};
190
+ const profiles = {};
191
+ for (const event of events) {
192
+ if (event.actorType !== "human") continue;
193
+ if (!isHumanActorId(event.actorId)) continue;
194
+ if (profiles[event.actorId]) continue;
195
+ const stored = readStoredActorProfile(event.metadata);
196
+ if (!stored?.name) continue;
197
+ profiles[event.actorId] = {
198
+ id: event.actorId,
199
+ name: stored.name,
200
+ ...stored.avatarUrl ? { avatarUrl: stored.avatarUrl } : {}
201
+ };
202
+ }
203
+ const unresolved = humanActorIds.filter((actorId) => !profiles[actorId]);
204
+ if (unresolved.length === 0 || !this.#users) return profiles;
205
+ try {
206
+ const users = this.#users.getUsers ? await this.#users.getUsers(unresolved) : await Promise.all(unresolved.map((actorId) => this.#users?.getUser(actorId) ?? null));
207
+ for (const [index, user] of users.entries()) {
208
+ if (!user) continue;
209
+ const name = user.name?.trim() || user.email?.trim() || user.id;
210
+ const actorId = unresolved[index] ?? user.id;
211
+ profiles[actorId] = {
212
+ id: user.id,
213
+ name,
214
+ ...user.avatarUrl ? { avatarUrl: user.avatarUrl } : {}
215
+ };
216
+ }
217
+ } catch (err) {
218
+ console.warn("[Audit] Failed to resolve audit actor profiles", { error: err instanceof Error ? err.message : String(err) });
219
+ }
220
+ return profiles;
221
+ }
222
+ routes() {
223
+ return [registerApiRoute("/web/factory/projects/:id/audit", {
224
+ method: "GET",
225
+ handler: async (cc) => {
226
+ const c = loose(cc);
227
+ const tenant = await this.#resolveTenant(c);
228
+ if ("response" in tenant) return tenant.response;
229
+ const projectId = c.req.param("id");
230
+ if (!projectId || !UUID_RE.test(projectId)) return c.json({ error: "Project not found" }, 404);
231
+ await this.#projects.ensureReady();
232
+ if (!await this.#projects.get({
233
+ orgId: tenant.orgId,
234
+ id: projectId
235
+ })) return c.json({ error: "Project not found" }, 404);
236
+ const page = await this.list({
237
+ orgId: tenant.orgId,
238
+ factoryProjectId: projectId,
239
+ actions: parseActionsParam(c.req.query("actions")),
240
+ actorId: c.req.query("actor") || void 0,
241
+ before: c.req.query("before") || void 0,
242
+ limit: parseLimitParam(c.req.query("limit"))
243
+ });
244
+ const actors = await this.#resolveActorProfiles(page.events, parseActorIdsParam(c.req.query("actorIds")));
245
+ return c.json({
246
+ ...page,
247
+ actors
248
+ });
249
+ }
250
+ })];
251
+ }
252
+ async #resolveTenant(c) {
253
+ await this.#auth.ensureUser(c);
254
+ const tenant = this.#auth.tenant(c);
255
+ if (!tenant) return { response: c.json({ error: "unauthorized" }, 401) };
256
+ if (!tenant.orgId) return { response: c.json({
257
+ error: "organization_required",
258
+ message: "The audit trail requires an organization."
259
+ }, 403) };
260
+ return {
261
+ orgId: tenant.orgId,
262
+ userId: tenant.userId
263
+ };
264
+ }
265
+ };
266
+ //#endregion
267
+ export { AuditDomain, auditRequestContext };
268
+
269
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","names":["#auth","#audit","#projects","#users","#sinks","#agentTenant","#resolveActorProfiles","#resolveTenant"],"sources":["../../../../src/storage/domains/audit/domain.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute, IUserProvider } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../projects/base.js';\nimport type {\n AuditContext,\n AuditEventPage,\n AuditEventRow,\n AuditStorage,\n AuditTarget,\n ListAuditEventsInput,\n RecordAuditEventInput,\n} from './base.js';\n\nexport interface EmitAuditInput {\n action: string;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface EmitAgentAuditInput {\n action: string;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface AuditEmitter {\n emit(args: { context: Context; input: EmitAuditInput }): Promise<void>;\n}\n\nexport interface AuditAgentEmitter {\n emitAgent(args: { requestContext: RequestContext; input: EmitAgentAuditInput }): Promise<void>;\n}\n\n/** Best-effort destination for locally persisted audit events (e.g. an integration's audit log). */\nexport interface AuditSink {\n id: string;\n audit?(args: { event: AuditEventRow }): Promise<void>;\n}\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n}\n\nexport interface AuditActorProfile {\n id: string;\n name: string;\n avatarUrl?: string;\n}\n\n/**\n * Reserved metadata key on `AuditEventRow.metadata` that carries the acting\n * human user's display name and avatar captured from the auth context at\n * record time. Read back by `#resolveActorProfiles` so names/avatars work\n * for every auth provider without requiring an `IUserProvider.getUser(id)`\n * implementation — the Studio provider proxies through the shared API and\n * cannot resolve arbitrary users by id.\n */\nconst ACTOR_PROFILE_METADATA_KEY = '__actorProfile';\n\ninterface StoredActorProfile {\n name?: string;\n avatarUrl?: string;\n}\n\nfunction readStoredActorProfile(metadata: Record<string, unknown> | undefined): StoredActorProfile | undefined {\n if (!metadata) return undefined;\n const raw = metadata[ACTOR_PROFILE_METADATA_KEY];\n if (!raw || typeof raw !== 'object') return undefined;\n const profile = raw as Record<string, unknown>;\n const name = typeof profile.name === 'string' && profile.name.trim() ? profile.name.trim() : undefined;\n const avatarUrl =\n typeof profile.avatarUrl === 'string' && profile.avatarUrl.trim() ? profile.avatarUrl.trim() : undefined;\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\n/** Read the auth gate's stashed user directly from the Hono context. */\nfunction readFactoryAuthUserFromContext(\n context: Context,\n): { name?: string; email?: string; avatarUrl?: string } | undefined {\n const user = context.get('factoryAuthUser') as { name?: string; email?: string; avatarUrl?: string } | undefined;\n return user ?? undefined;\n}\n\nfunction buildActorProfileMetadata(user: {\n name?: string;\n email?: string;\n avatarUrl?: string;\n}): StoredActorProfile | undefined {\n const name = user.name?.trim() || user.email?.trim();\n const avatarUrl = user.avatarUrl?.trim();\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\nexport interface AuditDomainOptions {\n auth: RouteAuth;\n /** Audit storage domain handle. */\n audit: AuditStorage;\n /** Projects domain handle, used to scope the audit trail route. */\n projects: FactoryProjectsStorage;\n /** Resolve persisted human actor ids to display names and profile images. */\n users?: Pick<IUserProvider, 'getUser' | 'getUsers'>;\n /** Best-effort fan-out destinations notified after each recorded event. */\n sinks?: AuditSink[];\n /** Resolve the acting tenant for agent-emitted events from the request context. */\n agentTenant?: (requestContext: RequestContext) => { orgId?: string; userId?: string } | undefined;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_ACTION_FILTERS = 16;\nconst MAX_ACTOR_PROFILES = 100;\nconst AUTOMATION_ACTORS = new Set([\n 'factory',\n 'system',\n 'automation',\n 'factory-rule-dispatcher',\n 'factory-tool-result-rule',\n]);\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction parseActionsParam(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined;\n const actions = raw\n .split(',')\n .map(action => action.trim())\n .filter(Boolean)\n .slice(0, MAX_ACTION_FILTERS);\n return actions.length > 0 ? actions : undefined;\n}\n\nfunction isHumanActorId(actorId: string | undefined): actorId is string {\n if (!actorId) return false;\n return !AUTOMATION_ACTORS.has(actorId) && !actorId.startsWith('agent:') && !actorId.startsWith('github:');\n}\n\nfunction parseActorIdsParam(raw: string | undefined): string[] {\n if (!raw) return [];\n return [\n ...new Set(\n raw\n .split(',')\n .map(actorId => actorId.trim())\n .filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n}\n\nfunction parseLimitParam(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const limit = Number.parseInt(raw, 10);\n return Number.isFinite(limit) ? limit : undefined;\n}\n\nexport function auditRequestContext(c: Context): AuditContext {\n const location = c.req.header('x-forwarded-for')?.split(',')[0]?.trim();\n const userAgent = c.req.header('user-agent');\n return {\n ...(location ? { location } : {}),\n ...(userAgent ? { userAgent } : {}),\n };\n}\n\n/** Factory-owned audit behavior backed by the audit storage domain. */\nexport class AuditDomain implements AuditEmitter, AuditAgentEmitter {\n readonly #auth: RouteAuth;\n readonly #audit: AuditStorage;\n readonly #projects: FactoryProjectsStorage;\n readonly #users: AuditDomainOptions['users'];\n readonly #sinks: AuditSink[];\n readonly #agentTenant: AuditDomainOptions['agentTenant'];\n\n constructor({ auth, audit, projects, users, sinks = [], agentTenant }: AuditDomainOptions) {\n this.#auth = auth;\n this.#audit = audit;\n this.#projects = projects;\n this.#users = users;\n this.#sinks = sinks;\n this.#agentTenant = agentTenant;\n\n const ids = new Set<string>();\n for (const sink of this.#sinks) {\n if (!sink.id) throw new Error('Audit integration id must not be empty');\n if (ids.has(sink.id)) throw new Error(`Duplicate audit integration id '${sink.id}'`);\n ids.add(sink.id);\n }\n }\n\n async record(input: RecordAuditEventInput): Promise<AuditEventRow | null> {\n try {\n await this.#audit.ensureReady();\n const row = await this.#audit.record(input);\n for (const sink of this.#sinks) {\n if (!sink.audit) continue;\n void Promise.resolve()\n .then(() => sink.audit?.({ event: row }))\n .catch(err => {\n console.warn('[Audit] Audit integration failed', {\n integration: sink.id,\n action: row.action,\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n return row;\n } catch (err) {\n console.warn('[Audit] Failed to record audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n return null;\n }\n }\n\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n await this.#audit.ensureReady();\n return this.#audit.list(input);\n }\n\n async emit({ context, input }: { context: Context; input: EmitAuditInput }): Promise<void> {\n try {\n const tenant = this.#auth.tenant(context);\n if (!tenant?.orgId) return;\n const user = readFactoryAuthUserFromContext(context);\n const actorProfile = user ? buildActorProfileMetadata(user) : undefined;\n const metadata = actorProfile\n ? { ...input.metadata, [ACTOR_PROFILE_METADATA_KEY]: actorProfile }\n : input.metadata;\n await this.record({\n orgId: tenant.orgId,\n actorId: tenant.userId,\n action: input.action,\n targets: input.targets,\n metadata,\n factoryProjectId: input.factoryProjectId,\n projectRepositoryId: input.projectRepositoryId,\n context: auditRequestContext(context),\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async emitAgent({\n requestContext,\n input,\n }: {\n requestContext: RequestContext;\n input: EmitAgentAuditInput;\n }): Promise<void> {\n try {\n const context = requestContext.get('controller') as\n | AgentControllerRequestContext<FactorySessionState>\n | undefined;\n const tenant = this.#agentTenant?.(requestContext);\n const orgId = tenant?.orgId;\n const userId = tenant?.userId;\n const threadId = context?.threadId;\n const state = context?.getState();\n if (!orgId || !userId || !threadId || !state?.factoryProjectId) return;\n\n const modeId = context.session.modeId?.trim();\n const modelId = context.session.modelId?.trim();\n await this.record({\n orgId,\n actorId: `agent:${threadId}`,\n actorType: 'agent',\n action: input.action,\n targets: input.targets,\n metadata: {\n ...input.metadata,\n startedBy: userId,\n ...(modeId ? { agentName: `${modeId} agent` } : {}),\n ...(modelId ? { modelId } : {}),\n },\n factoryProjectId: state.factoryProjectId,\n projectRepositoryId: state.projectRepositoryId,\n context: {},\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit agent audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async #resolveActorProfiles(\n events: AuditEventRow[],\n requestedActorIds: string[] = [],\n ): Promise<Record<string, AuditActorProfile>> {\n const humanActorIds = [\n ...new Set(\n [\n ...requestedActorIds,\n ...events.filter(event => event.actorType === 'human').map(event => event.actorId),\n ].filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n if (humanActorIds.length === 0) return {};\n\n // Prefer the profile stamped into event metadata at record time: it works\n // regardless of the auth provider's ability to resolve a user by id\n // (MastraAuthStudio's getUser() always returns null, WorkOS looks up by\n // id but every provider stamps here uniformly).\n const profiles: Record<string, AuditActorProfile> = {};\n for (const event of events) {\n if (event.actorType !== 'human') continue;\n if (!isHumanActorId(event.actorId)) continue;\n if (profiles[event.actorId]) continue;\n const stored = readStoredActorProfile(event.metadata);\n if (!stored?.name) continue;\n profiles[event.actorId] = {\n id: event.actorId,\n name: stored.name,\n ...(stored.avatarUrl ? { avatarUrl: stored.avatarUrl } : {}),\n };\n }\n\n const unresolved = humanActorIds.filter(actorId => !profiles[actorId]);\n if (unresolved.length === 0 || !this.#users) return profiles;\n\n try {\n const users = this.#users.getUsers\n ? await this.#users.getUsers(unresolved)\n : await Promise.all(unresolved.map(actorId => this.#users?.getUser(actorId) ?? null));\n for (const [index, user] of users.entries()) {\n if (!user) continue;\n const name = user.name?.trim() || user.email?.trim() || user.id;\n const actorId = unresolved[index] ?? user.id;\n profiles[actorId] = {\n id: user.id,\n name,\n ...(user.avatarUrl ? { avatarUrl: user.avatarUrl } : {}),\n };\n }\n } catch (err) {\n console.warn('[Audit] Failed to resolve audit actor profiles', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return profiles;\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects/:id/audit', {\n method: 'GET',\n handler: async cc => {\n const c = loose(cc);\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant.response;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return c.json({ error: 'Project not found' }, 404);\n await this.#projects.ensureReady();\n const project = await this.#projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) return c.json({ error: 'Project not found' }, 404);\n\n const page = await this.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n actions: parseActionsParam(c.req.query('actions')),\n actorId: c.req.query('actor') || undefined,\n before: c.req.query('before') || undefined,\n limit: parseLimitParam(c.req.query('limit')),\n });\n const actors = await this.#resolveActorProfiles(page.events, parseActorIdsParam(c.req.query('actorIds')));\n return c.json({ ...page, actors });\n },\n }),\n ];\n }\n\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.#auth.ensureUser(c);\n const tenant = this.#auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json({ error: 'organization_required', message: 'The audit trail requires an organization.' }, 403),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n}\n"],"mappings":"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"}