@mastra/factory 0.0.0 → 0.1.0-alpha.3

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 (287) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE.md +30 -0
  3. package/dist/auth.d.ts +181 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +370 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/capabilities/connection.d.ts +9 -0
  8. package/dist/capabilities/connection.d.ts.map +1 -0
  9. package/dist/capabilities/connection.js +1 -0
  10. package/dist/capabilities/connection.js.map +1 -0
  11. package/dist/capabilities/intake.d.ts +93 -0
  12. package/dist/capabilities/intake.d.ts.map +1 -0
  13. package/dist/capabilities/intake.js +1 -0
  14. package/dist/capabilities/intake.js.map +1 -0
  15. package/dist/capabilities/version-control.d.ts +239 -0
  16. package/dist/capabilities/version-control.d.ts.map +1 -0
  17. package/dist/capabilities/version-control.js +1 -0
  18. package/dist/capabilities/version-control.js.map +1 -0
  19. package/dist/factory.d.ts +145 -0
  20. package/dist/factory.d.ts.map +1 -0
  21. package/dist/factory.js +38245 -0
  22. package/dist/factory.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +38245 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/integrations/base.d.ts +197 -0
  28. package/dist/integrations/base.d.ts.map +1 -0
  29. package/dist/integrations/base.js +1 -0
  30. package/dist/integrations/base.js.map +1 -0
  31. package/dist/integrations/github/config.d.ts +60 -0
  32. package/dist/integrations/github/config.d.ts.map +1 -0
  33. package/dist/integrations/github/config.js +28 -0
  34. package/dist/integrations/github/config.js.map +1 -0
  35. package/dist/integrations/github/factory-session.d.ts +12 -0
  36. package/dist/integrations/github/factory-session.d.ts.map +1 -0
  37. package/dist/integrations/github/factory-session.js +41 -0
  38. package/dist/integrations/github/factory-session.js.map +1 -0
  39. package/dist/integrations/github/integration.d.ts +191 -0
  40. package/dist/integrations/github/integration.d.ts.map +1 -0
  41. package/dist/integrations/github/integration.js +3001 -0
  42. package/dist/integrations/github/integration.js.map +1 -0
  43. package/dist/integrations/github/issue-triage.d.ts +27 -0
  44. package/dist/integrations/github/issue-triage.d.ts.map +1 -0
  45. package/dist/integrations/github/issue-triage.js +82 -0
  46. package/dist/integrations/github/issue-triage.js.map +1 -0
  47. package/dist/integrations/github/project-lock.d.ts +78 -0
  48. package/dist/integrations/github/project-lock.d.ts.map +1 -0
  49. package/dist/integrations/github/project-lock.js +70 -0
  50. package/dist/integrations/github/project-lock.js.map +1 -0
  51. package/dist/integrations/github/provenance.d.ts +27 -0
  52. package/dist/integrations/github/provenance.d.ts.map +1 -0
  53. package/dist/integrations/github/provenance.js +104 -0
  54. package/dist/integrations/github/provenance.js.map +1 -0
  55. package/dist/integrations/github/routes.d.ts +72 -0
  56. package/dist/integrations/github/routes.d.ts.map +1 -0
  57. package/dist/integrations/github/routes.js +1834 -0
  58. package/dist/integrations/github/routes.js.map +1 -0
  59. package/dist/integrations/github/sandbox.d.ts +268 -0
  60. package/dist/integrations/github/sandbox.d.ts.map +1 -0
  61. package/dist/integrations/github/sandbox.js +381 -0
  62. package/dist/integrations/github/sandbox.js.map +1 -0
  63. package/dist/integrations/github/session-subscriptions.d.ts +23 -0
  64. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -0
  65. package/dist/integrations/github/session-subscriptions.js +190 -0
  66. package/dist/integrations/github/session-subscriptions.js.map +1 -0
  67. package/dist/integrations/github/subscriptions.d.ts +54 -0
  68. package/dist/integrations/github/subscriptions.d.ts.map +1 -0
  69. package/dist/integrations/github/subscriptions.js +70 -0
  70. package/dist/integrations/github/subscriptions.js.map +1 -0
  71. package/dist/integrations/github/webhook.d.ts +87 -0
  72. package/dist/integrations/github/webhook.d.ts.map +1 -0
  73. package/dist/integrations/github/webhook.js +381 -0
  74. package/dist/integrations/github/webhook.js.map +1 -0
  75. package/dist/integrations/linear/agent-tools.d.ts +34 -0
  76. package/dist/integrations/linear/agent-tools.d.ts.map +1 -0
  77. package/dist/integrations/linear/agent-tools.js +108 -0
  78. package/dist/integrations/linear/agent-tools.js.map +1 -0
  79. package/dist/integrations/linear/integration.d.ts +233 -0
  80. package/dist/integrations/linear/integration.d.ts.map +1 -0
  81. package/dist/integrations/linear/integration.js +962 -0
  82. package/dist/integrations/linear/integration.js.map +1 -0
  83. package/dist/integrations/linear/routes.d.ts +61 -0
  84. package/dist/integrations/linear/routes.d.ts.map +1 -0
  85. package/dist/integrations/linear/routes.js +242 -0
  86. package/dist/integrations/linear/routes.js.map +1 -0
  87. package/dist/integrations/linear/storage.d.ts +65 -0
  88. package/dist/integrations/linear/storage.d.ts.map +1 -0
  89. package/dist/integrations/linear/storage.js +1 -0
  90. package/dist/integrations/linear/storage.js.map +1 -0
  91. package/dist/integrations/platform/api-client.d.ts +24 -0
  92. package/dist/integrations/platform/api-client.d.ts.map +1 -0
  93. package/dist/integrations/platform/api-client.js +153 -0
  94. package/dist/integrations/platform/api-client.js.map +1 -0
  95. package/dist/integrations/platform/github/event-worker.d.ts +44 -0
  96. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
  97. package/dist/integrations/platform/github/event-worker.js +585 -0
  98. package/dist/integrations/platform/github/event-worker.js.map +1 -0
  99. package/dist/integrations/platform/github/integration.d.ts +36 -0
  100. package/dist/integrations/platform/github/integration.d.ts.map +1 -0
  101. package/dist/integrations/platform/github/integration.js +3253 -0
  102. package/dist/integrations/platform/github/integration.js.map +1 -0
  103. package/dist/integrations/platform/linear/integration.d.ts +46 -0
  104. package/dist/integrations/platform/linear/integration.d.ts.map +1 -0
  105. package/dist/integrations/platform/linear/integration.js +891 -0
  106. package/dist/integrations/platform/linear/integration.js.map +1 -0
  107. package/dist/integrations/workos/integration.d.ts +39 -0
  108. package/dist/integrations/workos/integration.d.ts.map +1 -0
  109. package/dist/integrations/workos/integration.js +96 -0
  110. package/dist/integrations/workos/integration.js.map +1 -0
  111. package/dist/routes/config.d.ts +220 -0
  112. package/dist/routes/config.d.ts.map +1 -0
  113. package/dist/routes/config.js +770 -0
  114. package/dist/routes/config.js.map +1 -0
  115. package/dist/routes/fs.d.ts +105 -0
  116. package/dist/routes/fs.d.ts.map +1 -0
  117. package/dist/routes/fs.js +294 -0
  118. package/dist/routes/fs.js.map +1 -0
  119. package/dist/routes/intake.d.ts +23 -0
  120. package/dist/routes/intake.d.ts.map +1 -0
  121. package/dist/routes/intake.js +179 -0
  122. package/dist/routes/intake.js.map +1 -0
  123. package/dist/routes/oauth.d.ts +43 -0
  124. package/dist/routes/oauth.d.ts.map +1 -0
  125. package/dist/routes/oauth.js +25058 -0
  126. package/dist/routes/oauth.js.map +1 -0
  127. package/dist/routes/projects.d.ts +19 -0
  128. package/dist/routes/projects.d.ts.map +1 -0
  129. package/dist/routes/projects.js +395 -0
  130. package/dist/routes/projects.js.map +1 -0
  131. package/dist/routes/provider-credentials.d.ts +81 -0
  132. package/dist/routes/provider-credentials.d.ts.map +1 -0
  133. package/dist/routes/provider-credentials.js +68 -0
  134. package/dist/routes/provider-credentials.js.map +1 -0
  135. package/dist/routes/route.d.ts +48 -0
  136. package/dist/routes/route.d.ts.map +1 -0
  137. package/dist/routes/route.js +11 -0
  138. package/dist/routes/route.js.map +1 -0
  139. package/dist/routes/skills.d.ts +30 -0
  140. package/dist/routes/skills.d.ts.map +1 -0
  141. package/dist/routes/skills.js +181 -0
  142. package/dist/routes/skills.js.map +1 -0
  143. package/dist/routes/surface.d.ts +83 -0
  144. package/dist/routes/surface.d.ts.map +1 -0
  145. package/dist/routes/surface.js +28519 -0
  146. package/dist/routes/surface.js.map +1 -0
  147. package/dist/routes/tenant-credentials.d.ts +74 -0
  148. package/dist/routes/tenant-credentials.d.ts.map +1 -0
  149. package/dist/routes/tenant-credentials.js +181 -0
  150. package/dist/routes/tenant-credentials.js.map +1 -0
  151. package/dist/routes/work-items.d.ts +40 -0
  152. package/dist/routes/work-items.d.ts.map +1 -0
  153. package/dist/routes/work-items.js +902 -0
  154. package/dist/routes/work-items.js.map +1 -0
  155. package/dist/rules/binding-context.d.ts +5 -0
  156. package/dist/rules/binding-context.d.ts.map +1 -0
  157. package/dist/rules/binding-context.js +40 -0
  158. package/dist/rules/binding-context.js.map +1 -0
  159. package/dist/rules/defaults.d.ts +9 -0
  160. package/dist/rules/defaults.d.ts.map +1 -0
  161. package/dist/rules/defaults.js +334 -0
  162. package/dist/rules/defaults.js.map +1 -0
  163. package/dist/rules/dispatcher.d.ts +40 -0
  164. package/dist/rules/dispatcher.d.ts.map +1 -0
  165. package/dist/rules/dispatcher.js +695 -0
  166. package/dist/rules/dispatcher.js.map +1 -0
  167. package/dist/rules/github-service.d.ts +29 -0
  168. package/dist/rules/github-service.d.ts.map +1 -0
  169. package/dist/rules/github-service.js +474 -0
  170. package/dist/rules/github-service.js.map +1 -0
  171. package/dist/rules/index.d.ts +7 -0
  172. package/dist/rules/index.d.ts.map +1 -0
  173. package/dist/rules/index.js +583 -0
  174. package/dist/rules/index.js.map +1 -0
  175. package/dist/rules/linear-service.d.ts +27 -0
  176. package/dist/rules/linear-service.d.ts.map +1 -0
  177. package/dist/rules/linear-service.js +343 -0
  178. package/dist/rules/linear-service.js.map +1 -0
  179. package/dist/rules/processor.d.ts +54 -0
  180. package/dist/rules/processor.d.ts.map +1 -0
  181. package/dist/rules/processor.js +629 -0
  182. package/dist/rules/processor.js.map +1 -0
  183. package/dist/rules/resolve.d.ts +17 -0
  184. package/dist/rules/resolve.d.ts.map +1 -0
  185. package/dist/rules/resolve.js +27 -0
  186. package/dist/rules/resolve.js.map +1 -0
  187. package/dist/rules/start-coordinator.d.ts +58 -0
  188. package/dist/rules/start-coordinator.d.ts.map +1 -0
  189. package/dist/rules/start-coordinator.js +136 -0
  190. package/dist/rules/start-coordinator.js.map +1 -0
  191. package/dist/rules/tools.d.ts +10 -0
  192. package/dist/rules/tools.d.ts.map +1 -0
  193. package/dist/rules/tools.js +93 -0
  194. package/dist/rules/tools.js.map +1 -0
  195. package/dist/rules/transition-service.d.ts +32 -0
  196. package/dist/rules/transition-service.d.ts.map +1 -0
  197. package/dist/rules/transition-service.js +474 -0
  198. package/dist/rules/transition-service.js.map +1 -0
  199. package/dist/rules/types.d.ts +225 -0
  200. package/dist/rules/types.d.ts.map +1 -0
  201. package/dist/rules/types.js +33 -0
  202. package/dist/rules/types.js.map +1 -0
  203. package/dist/rules/validation.d.ts +11 -0
  204. package/dist/rules/validation.d.ts.map +1 -0
  205. package/dist/rules/validation.js +310 -0
  206. package/dist/rules/validation.js.map +1 -0
  207. package/dist/sandbox/fleet.d.ts +208 -0
  208. package/dist/sandbox/fleet.d.ts.map +1 -0
  209. package/dist/sandbox/fleet.js +259 -0
  210. package/dist/sandbox/fleet.js.map +1 -0
  211. package/dist/sandbox/reattach.d.ts +3 -0
  212. package/dist/sandbox/reattach.d.ts.map +1 -0
  213. package/dist/sandbox/reattach.js +9 -0
  214. package/dist/sandbox/reattach.js.map +1 -0
  215. package/dist/server-error.d.ts +12 -0
  216. package/dist/server-error.d.ts.map +1 -0
  217. package/dist/server-error.js +20 -0
  218. package/dist/server-error.js.map +1 -0
  219. package/dist/skills/service.d.ts +48 -0
  220. package/dist/skills/service.d.ts.map +1 -0
  221. package/dist/skills/service.js +45 -0
  222. package/dist/skills/service.js.map +1 -0
  223. package/dist/spa-static.d.ts +34 -0
  224. package/dist/spa-static.d.ts.map +1 -0
  225. package/dist/spa-static.js +73 -0
  226. package/dist/spa-static.js.map +1 -0
  227. package/dist/state-signing.d.ts +48 -0
  228. package/dist/state-signing.d.ts.map +1 -0
  229. package/dist/state-signing.js +48 -0
  230. package/dist/state-signing.js.map +1 -0
  231. package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
  232. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
  233. package/dist/storage/domains/audit/agent-audit.js +65 -0
  234. package/dist/storage/domains/audit/agent-audit.js.map +1 -0
  235. package/dist/storage/domains/audit/base.d.ts +140 -0
  236. package/dist/storage/domains/audit/base.d.ts.map +1 -0
  237. package/dist/storage/domains/audit/base.js +127 -0
  238. package/dist/storage/domains/audit/base.js.map +1 -0
  239. package/dist/storage/domains/audit/domain.d.ts +69 -0
  240. package/dist/storage/domains/audit/domain.d.ts.map +1 -0
  241. package/dist/storage/domains/audit/domain.js +165 -0
  242. package/dist/storage/domains/audit/domain.js.map +1 -0
  243. package/dist/storage/domains/credentials/base.d.ts +134 -0
  244. package/dist/storage/domains/credentials/base.d.ts.map +1 -0
  245. package/dist/storage/domains/credentials/base.js +257 -0
  246. package/dist/storage/domains/credentials/base.js.map +1 -0
  247. package/dist/storage/domains/intake/base.d.ts +38 -0
  248. package/dist/storage/domains/intake/base.d.ts.map +1 -0
  249. package/dist/storage/domains/intake/base.js +62 -0
  250. package/dist/storage/domains/intake/base.js.map +1 -0
  251. package/dist/storage/domains/integrations/base.d.ts +126 -0
  252. package/dist/storage/domains/integrations/base.d.ts.map +1 -0
  253. package/dist/storage/domains/integrations/base.js +252 -0
  254. package/dist/storage/domains/integrations/base.js.map +1 -0
  255. package/dist/storage/domains/model-packs/base.d.ts +49 -0
  256. package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
  257. package/dist/storage/domains/model-packs/base.js +95 -0
  258. package/dist/storage/domains/model-packs/base.js.map +1 -0
  259. package/dist/storage/domains/projects/base.d.ts +55 -0
  260. package/dist/storage/domains/projects/base.d.ts.map +1 -0
  261. package/dist/storage/domains/projects/base.js +99 -0
  262. package/dist/storage/domains/projects/base.js.map +1 -0
  263. package/dist/storage/domains/queue-health/base.d.ts +52 -0
  264. package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
  265. package/dist/storage/domains/queue-health/base.js +87 -0
  266. package/dist/storage/domains/queue-health/base.js.map +1 -0
  267. package/dist/storage/domains/source-control/base.d.ts +288 -0
  268. package/dist/storage/domains/source-control/base.d.ts.map +1 -0
  269. package/dist/storage/domains/source-control/base.js +722 -0
  270. package/dist/storage/domains/source-control/base.js.map +1 -0
  271. package/dist/storage/domains/source-control/inmemory.d.ts +158 -0
  272. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
  273. package/dist/storage/domains/source-control/inmemory.js +348 -0
  274. package/dist/storage/domains/source-control/inmemory.js.map +1 -0
  275. package/dist/storage/domains/work-items/base.d.ts +412 -0
  276. package/dist/storage/domains/work-items/base.d.ts.map +1 -0
  277. package/dist/storage/domains/work-items/base.js +1174 -0
  278. package/dist/storage/domains/work-items/base.js.map +1 -0
  279. package/dist/storage/domains/work-items/metrics.d.ts +101 -0
  280. package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
  281. package/dist/storage/domains/work-items/metrics.js +203 -0
  282. package/dist/storage/domains/work-items/metrics.js.map +1 -0
  283. package/dist/workspace.d.ts +20 -0
  284. package/dist/workspace.d.ts.map +1 -0
  285. package/dist/workspace.js +364 -0
  286. package/dist/workspace.js.map +1 -0
  287. package/package.json +19 -19
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rules/processor.ts","../../src/auth.ts","../../src/rules/binding-context.ts","../../src/rules/resolve.ts","../../src/rules/types.ts","../../src/rules/validation.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport type { MastraDBMessage, MessageList } from '@mastra/core/agent/message-list';\nimport type {\n ComputeStateSignalArgs,\n ComputeStateSignalResult,\n ProcessInputStepArgs,\n Processor,\n} from '@mastra/core/processors';\n\nimport type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { getFactorySessionCoordinates } from './binding-context.js';\nimport { resolveFactoryToolRule } from './resolve.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { FACTORY_RULE_STAGES } from './types.js';\nimport type {\n FactoryCommitDecision,\n FactoryRuleBoard,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRules,\n FactoryToolResultRuleContext,\n} from './types.js';\nimport { normalizeFactoryRuleJsonValue, validateFactoryRuleDecisions } from './validation.js';\n\nconst STATE_ID = 'factory-phase';\nconst RULE_TIMEOUT_MS = 5_000;\nconst TRANSCRIPT_PAGE_SIZE = 50;\nconst MAX_LINKED_ITEMS = 5;\nconst PHASE_LABELS: Record<(typeof FACTORY_RULE_STAGES)[number], string> = {\n intake: 'Intake',\n triage: 'Investigating',\n planning: 'Planning',\n execute: 'Building',\n review: 'Reviewing',\n done: 'Done',\n canceled: 'Canceled',\n};\n\ntype PersistedMessageReader = {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n filter?: { dateRange?: { start?: Date } };\n orderBy: { field: 'createdAt'; direction: 'ASC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n};\n\ntype CompletedToolResult = {\n assistantMessageId: string;\n messageCreatedAt: Date;\n toolCallId: string;\n toolName: string;\n input: FactoryRuleJsonValue;\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n};\n\ntype PhaseSnapshotValue = {\n bindingId?: string;\n itemId?: string;\n revision?: number;\n stage?: string;\n role?: string;\n board?: FactoryRuleBoard;\n ruleSetVersion?: string;\n status: 'active' | 'none';\n};\n\nfunction workItemSource(item: WorkItemRow) {\n if (!item.externalSource) return 'manual' as const;\n if (item.externalSource.integrationId === 'linear') return 'linear-issue' as const;\n return item.externalSource.type === 'pull-request' ? ('github-pr' as const) : ('github-issue' as const);\n}\n\nfunction workItemSourceKey(item: WorkItemRow): string | null {\n const source = item.externalSource;\n return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;\n}\n\nfunction boardForItem(item: WorkItemRow): FactoryRuleBoard {\n return item.externalSource?.type === 'pull-request' ? 'review' : 'work';\n}\n\nfunction boundedError(value: unknown): FactoryRuleJsonValue {\n const message = value instanceof Error ? value.message : typeof value === 'string' ? value : 'Tool execution failed.';\n return { message: message.slice(0, 2_000) };\n}\n\nfunction boundedResult(value: unknown): FactoryRuleJsonValue {\n try {\n return normalizeFactoryRuleJsonValue(value);\n } catch {\n return { message: 'Tool result was not serializable.' };\n }\n}\n\nfunction messageParts(message: MastraDBMessage): unknown[] {\n const content = message.content as { parts?: unknown[]; toolInvocations?: unknown[] } | unknown[] | undefined;\n if (Array.isArray(content)) return content;\n if (Array.isArray(content?.parts)) return content.parts;\n return Array.isArray(content?.toolInvocations) ? content.toolInvocations : [];\n}\n\nfunction completedStepToolCallIds(steps: unknown[]): Set<string> {\n const ids = new Set<string>();\n for (const rawStep of steps) {\n if (!rawStep || typeof rawStep !== 'object') continue;\n const toolResults = (rawStep as { toolResults?: unknown[] }).toolResults;\n if (!Array.isArray(toolResults)) continue;\n for (const rawResult of toolResults) {\n if (!rawResult || typeof rawResult !== 'object') continue;\n const toolCallId = (rawResult as { toolCallId?: unknown }).toolCallId;\n if (typeof toolCallId === 'string') ids.add(toolCallId);\n }\n }\n return ids;\n}\n\nfunction completedToolResults(message: MastraDBMessage): CompletedToolResult[] {\n if (message.role !== 'assistant') return [];\n const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);\n const completed: CompletedToolResult[] = [];\n for (const rawPart of messageParts(message)) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : part;\n const state = invocation.state;\n if (state !== 'result' && state !== 'error') continue;\n const toolCallId = invocation.toolCallId;\n const toolName = invocation.toolName ?? invocation.name;\n if (typeof toolCallId !== 'string' || typeof toolName !== 'string') continue;\n completed.push({\n assistantMessageId: message.id,\n messageCreatedAt: createdAt,\n toolCallId,\n toolName: toolName.slice(0, 256),\n input: boundedResult(invocation.args ?? {}),\n status: state === 'error' ? 'error' : 'success',\n value: state === 'error' ? boundedError(invocation.result ?? invocation.error) : boundedResult(invocation.result),\n });\n }\n return completed;\n}\n\nfunction currentCompletedToolMessage(\n messages: MastraDBMessage[],\n toolCallIds: ReadonlySet<string>,\n): MastraDBMessage | undefined {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!;\n if (completedToolResults(message).some(result => toolCallIds.has(result.toolCallId))) return message;\n }\n return undefined;\n}\n\nfunction phaseCacheKey(value: Omit<PhaseSnapshotValue, 'status'>, linked: WorkItemRow[]): string {\n return createHash('sha256')\n .update(\n JSON.stringify({\n ...value,\n linked: linked.map(item => [item.id, item.revision, item.stages[0]]),\n }),\n )\n .digest('hex');\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\nfunction phaseFromSignal(signal: { metadata?: Record<string, unknown> } | undefined): PhaseSnapshotValue | undefined {\n return (signal?.metadata?.value as { phase?: PhaseSnapshotValue } | undefined)?.phase;\n}\n\nfunction latestPhase(args: ComputeStateSignalArgs): PhaseSnapshotValue | undefined {\n for (const signal of [...args.activeStateSignals].reverse()) {\n const phase = phaseFromSignal(signal);\n if (phase) return phase;\n }\n return phaseFromSignal(args.lastSnapshot);\n}\n\nasync function withRuleTimeout<T>(operation: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport class FactoryPhaseStateProcessor implements Processor<'factory-phase'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n\n constructor(\n private readonly options: {\n rules: FactoryRules;\n storage: WorkItemsStorage;\n transitionService?: Pick<FactoryTransitionService, 'transition'>;\n messageReader?: PersistedMessageReader;\n recordPullRequestProvenance?: (input: {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n }) => Promise<void>;\n },\n ) {}\n\n async processInputStep(args: ProcessInputStepArgs): Promise<MessageList | undefined> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n if (!binding || binding.status !== 'active') return;\n const completedToolCallIds = completedStepToolCallIds(args.steps);\n const completedMessage = currentCompletedToolMessage(args.messages, completedToolCallIds);\n if (completedMessage) {\n await this.ingestMessages(binding, [completedMessage], completedToolCallIds);\n }\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n const prior = latestPhase(args);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n\n if (!binding) return;\n if (binding.status !== 'active') {\n if (!hasBase || prior?.status !== 'active') return;\n return {\n id: STATE_ID,\n cacheKey: `factory:none:${prior.bindingId ?? 'revoked'}`,\n mode: 'snapshot',\n tagName: 'factory-phase',\n contents: '\\n',\n value: { phase: { status: 'none' } },\n attributes: { status: 'none' },\n metadata: { value: { phase: { status: 'none' } } },\n };\n }\n\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!item || item.stages.length !== 1 || !FACTORY_RULE_STAGES.includes(item.stages[0] as never)) return;\n const allItems = await this.options.storage.list({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n });\n const linked = allItems\n .filter(candidate => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id)\n .slice(0, MAX_LINKED_ITEMS);\n const board = boardForItem(item);\n const stage = item.stages[0]!;\n const value: PhaseSnapshotValue = {\n status: 'active',\n bindingId: binding.id,\n itemId: item.id,\n revision: item.revision,\n stage,\n role: binding.role,\n board,\n ruleSetVersion: this.options.rules.version,\n };\n const cacheKey = phaseCacheKey(value, linked);\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n\n const linkedText = linked.length\n ? `\\nLinked items: ${linked.map(candidate => `${workItemSource(candidate)} ${candidate.title}`).join('; ')}`\n : '';\n const snapshotContents =\n `Factory ${board} phase: ${PHASE_LABELS[stage as keyof typeof PHASE_LABELS]} (${escapeText(stage)})\\n` +\n `Work item: ${escapeText(item.title)} (${item.id})\\n` +\n `Role: ${escapeText(binding.role)}\\nRevision: ${item.revision}\\nRules: ${escapeText(this.options.rules.version)}\\n` +\n `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;\n const isDelta = hasBase && prior?.status === 'active';\n return {\n id: STATE_ID,\n cacheKey,\n mode: isDelta ? 'delta' : 'snapshot',\n tagName: 'factory-phase',\n contents: isDelta ? `Factory phase update:\\n${snapshotContents}` : snapshotContents,\n value: { phase: value },\n ...(isDelta ? { delta: { phase: value } } : {}),\n attributes: { status: 'active', board, stage, role: binding.role, revision: item.revision },\n metadata: { value: { phase: value } },\n };\n }\n\n async reconcileAllBoundThreads(): Promise<void> {\n if (!this.options.messageReader) return;\n const bindings = await this.options.storage.listActiveRunBindings();\n for (const binding of bindings) await this.reconcileBinding(binding);\n }\n\n async reconcileBinding(binding: FactoryRunBindingRecord): Promise<void> {\n const reader = this.options.messageReader;\n if (!reader || binding.status !== 'active') return;\n const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);\n let page = 0;\n while (true) {\n const result = await reader.listMessages({\n threadId: binding.threadId,\n resourceId: binding.resourceId,\n page,\n perPage: TRANSCRIPT_PAGE_SIZE,\n ...(cursor ? { filter: { dateRange: { start: cursor.lastMessageCreatedAt } } } : {}),\n orderBy: { field: 'createdAt', direction: 'ASC' },\n });\n await this.ingestMessages(binding, result.messages);\n const last = result.messages.at(-1);\n if (last) {\n await this.options.storage.advanceToolResultCursor({\n bindingId: binding.id,\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n lastMessageId: last.id,\n lastMessageCreatedAt: last.createdAt instanceof Date ? last.createdAt : new Date(last.createdAt),\n updatedAt: new Date(),\n });\n }\n if (!result.hasMore) break;\n page += 1;\n }\n }\n\n private async ingestMessages(\n binding: FactoryRunBindingRecord,\n messages: MastraDBMessage[],\n toolCallIds?: ReadonlySet<string>,\n ): Promise<void> {\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!item || item.stages.length !== 1 || !FACTORY_RULE_STAGES.includes(item.stages[0] as never)) return;\n for (const message of messages) {\n for (const toolResult of completedToolResults(message)) {\n if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;\n try {\n await this.options.recordPullRequestProvenance?.({\n binding,\n item,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n toolName: toolResult.toolName,\n toolInput: toolResult.input,\n toolResult: toolResult.value,\n status: toolResult.status,\n });\n } catch {\n // Provenance is supporting evidence and must not block authoritative rule ingress.\n }\n await this.ingestToolResult(binding, item, toolResult);\n }\n }\n }\n\n private async ingestToolResult(\n binding: FactoryRunBindingRecord,\n item: WorkItemRow,\n toolResult: CompletedToolResult,\n ): Promise<void> {\n const rule = resolveFactoryToolRule(this.options.rules, toolResult.toolName);\n if (!rule) return;\n const ingressId = JSON.stringify([\n binding.id,\n binding.threadId,\n toolResult.assistantMessageId,\n toolResult.toolCallId,\n ]);\n const prior = await this.options.storage.getTransitionResultByIngress(\n binding.orgId,\n binding.factoryProjectId,\n ingressId,\n );\n if (prior) return;\n const board = boardForItem(item);\n const context: FactoryToolResultRuleContext = {\n tenant: { orgId: binding.orgId, projectId: binding.factoryProjectId },\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'toolResult', id: ingressId },\n cause: `Completed ${toolResult.toolName}`,\n causalChain: [],\n ruleSetVersion: this.options.rules.version,\n item: {\n id: item.id,\n source: workItemSource(item),\n sourceKey: workItemSourceKey(item),\n parentWorkItemId: item.parentWorkItemId,\n title: item.title,\n url: item.externalSource?.url ?? null,\n stages: item.stages,\n },\n board,\n itemRevision: item.revision,\n toolName: toolResult.toolName,\n threadId: binding.threadId,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n result: { status: toolResult.status, value: toolResult.value },\n };\n\n let decision: FactoryRuleDecision | void = undefined;\n let decisions: FactoryCommitDecision[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = await withRuleTimeout(Promise.resolve(rule(Object.freeze(context))));\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n decisions = validateFactoryRuleDecisions([decision]);\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory tool-result rule failed.',\n };\n }\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n ingress: { identity: ingressId, triggerType: 'tool.result' },\n ruleSetVersion: this.options.rules.version,\n expectedRevision: item.revision,\n actor: { ...context.actor },\n outcome,\n decisions: decisions.map(entry => ({ ...entry })),\n causalChain: [],\n now: new Date(),\n });\n if (committed.status !== 'committed' || !this.options.transitionService) return;\n for (const entry of decisions) {\n if (entry.type !== 'transition') continue;\n await this.options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n board: entry.board,\n stage: entry.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-tool-result-rule' },\n ingress: { type: 'rule', identity: `decision:${entry.idempotencyKey}` },\n cause: 'tool_result_rule',\n causalChain: [{ ingressId, decisionType: entry.type }],\n });\n }\n }\n}\n","import { MastraAuthWorkos } from '@mastra/auth-workos';\nimport {\n registerApiRoute,\n isAuthHttpHandler,\n isCredentialsProvider,\n isOrganizationsProvider,\n isSessionProvider,\n isSSOProvider,\n} from '@mastra/core/server';\nimport type { ApiRoute, IMastraAuthProvider, ISessionProvider } from '@mastra/core/server';\nimport type { Context, Hono } from 'hono';\n\nimport type { RouteAuth } from './routes/route.js';\n\n/**\n * Provider-neutral factory auth gating for the MastraCode web server.\n *\n * When an auth provider is active (a `MastraAuthProvider` instance passed to\n * `MastraFactory`'s `auth` slot, or — back-compat for suites/paths that never\n * boot the factory — implied by the WorkOS env vars), every route on the web\n * server is placed behind it: unauthenticated browser navigations are\n * redirected to the SPA's `/signin` page, API/XHR calls receive a 401, and a\n * small set of public routes stay reachable while signed out — the provider's\n * `/auth/*` routes plus `/auth/me`, the `/signin` page and its `/assets/*`\n * bundle. When no provider is active, `mountFactoryAuth` is a no-op and the server\n * behaves exactly as it does without auth.\n *\n * Provider specifics stay in the providers (`@mastra/auth-workos`,\n * `@mastra/auth-better-auth`, or any custom `IMastraAuthProvider`); this\n * module composes them capability-first via the core type guards:\n * - `authenticateToken` — session/bearer validation (all providers)\n * - `ISSOProvider` — hosted-login `/auth/login`, `/auth/callback`, `/auth/logout`\n * - `IAuthHttpHandler` — provider-owned `/auth/api/*` endpoints (better-auth)\n * - `IOrganizationsProvider` — personal-org bootstrap + admin checks\n * - `ICredentialsProvider.isSignUpEnabled` — SPA sign-up affordance\n * - `getClearSessionHeaders` — session cookie clearing on logout\n */\n\n/** Minimal shape of the signed-in user surfaced to the SPA (no tokens). */\nexport interface FactoryAuthUser {\n /** Stable WorkOS user id used to scope per-user data (GitHub installs etc.). */\n workosId?: string;\n /** Provider user id; WorkOS shapes may use `workosId` instead (see {@link workosId}). */\n id?: string;\n email?: string;\n name?: string;\n /**\n * Organization id. The org is the top-level tenant: it owns the GitHub\n * App installation and connected projects, while each user inside the org gets\n * isolated building instances. Absent for personal (no-org) accounts.\n */\n organizationId?: string;\n}\n\n/**\n * Tenant identity: the org is the top-level tenant, and each user inside it is\n * an isolated builder. Agent state, worktrees and sandboxes are scoped per\n * `(orgId, userId)`. Personal (no-org) users have `orgId === undefined`.\n */\nexport interface FactoryAuthTenant {\n /** Organization id, or `undefined` for personal (no-org) accounts. */\n orgId?: string;\n /** Stable provider user id. */\n userId: string;\n}\n\n/**\n * Validate that a `returnTo` value is a safe same-site path, to prevent\n * open-redirect attacks. Only absolute local paths (`/foo`) are allowed;\n * protocol-relative (`//evil.com`) and absolute URLs are rejected.\n */\nexport function sanitizeReturnTo(raw: string | undefined): string {\n if (!raw) return '/';\n if (!raw.startsWith('/')) return '/';\n // Reject protocol-relative URLs like \"//evil.com\" and \"/\\evil.com\".\n if (raw.startsWith('//') || raw.startsWith('/\\\\')) return '/';\n return raw;\n}\n\n/** Extract a bearer token from the Authorization header, if present. */\nexport function getBearerToken(authorization: string | undefined): string {\n if (!authorization) return '';\n const match = /^Bearer\\s+(.+)$/i.exec(authorization);\n return match?.[1] ?? '';\n}\n\n/**\n * Whether the SPA is served cross-origin from this API (platform deploy). When\n * `MASTRACODE_ALLOWED_ORIGINS` is set the browser talks to us cross-site, so\n * session cookies must be `SameSite=None; Secure` for the browser to send them.\n * Same-origin local dev leaves this unset and keeps the stricter `SameSite=Lax`.\n */\nexport function isCrossSiteAuth(): boolean {\n return Boolean(process.env.MASTRACODE_ALLOWED_ORIGINS?.trim());\n}\n\n/** Hono context variables set by the auth gate. */\nexport interface FactoryAuthVariables {\n factoryAuthUser: FactoryAuthUser;\n}\n\n/** Context key under which the gate stashes the authenticated user. */\nconst FACTORY_AUTH_USER_KEY = 'factoryAuthUser';\n\n/**\n * Read the authenticated user the gate stashed on the context, or\n * `undefined` when unauthenticated / auth disabled. Used by downstream routes\n * (e.g. GitHub) to scope rows per user.\n */\nexport function getFactoryAuthUser(c: Context): FactoryAuthUser | undefined {\n return c.get(FACTORY_AUTH_USER_KEY) as FactoryAuthUser | undefined;\n}\n\n/** Resolve the stable user id from an authenticated user shape. */\nexport function getFactoryAuthUserId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.workosId ?? user?.id;\n}\n\n/** Resolve the organization id from a user shape, if present. */\nexport function getFactoryAuthOrgId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.organizationId;\n}\n\n/**\n * Resolve the tenant identity `(orgId, userId)` from the authenticated user on\n * the context. Returns `undefined` when there is no signed-in user (auth\n * disabled or unauthenticated). `orgId` is `undefined` for personal accounts;\n * callers gate org-scoped GitHub features on its presence while agent state\n * falls back to a user-only tenant.\n */\nexport function factoryAuthTenant(c: Context): FactoryAuthTenant | undefined {\n const user = getFactoryAuthUser(c);\n const userId = getFactoryAuthUserId(user);\n if (!userId) return undefined;\n return { orgId: getFactoryAuthOrgId(user), userId };\n}\n\n/** True when both WorkOS credential env vars are present (legacy env gate). */\nfunction envWorkosConfigured(): boolean {\n return Boolean(process.env.WORKOS_API_KEY && process.env.WORKOS_CLIENT_ID);\n}\n\n/**\n * WorkOS provider implied by the `WORKOS_*` env vars — back-compat for test\n * suites exercised without booting the factory (route suites set `WORKOS_*`\n * directly and call {@link mountFactoryAuth} without an explicit provider).\n * `fetchMemberships: true` lets `authenticateToken` resolve `organizationId`\n * from a single membership when the JWT has no org claim — required so a\n * bootstrapped personal org resolves without re-auth.\n */\nfunction envFallbackAuthProvider(redirectUri: string | undefined): MastraAuthWorkos | undefined {\n if (!envWorkosConfigured()) return undefined;\n return new MastraAuthWorkos({\n redirectUri: redirectUri ?? process.env.WORKOS_REDIRECT_URI,\n fetchMemberships: true,\n });\n}\n\n/**\n * Map a provider `authenticateToken` result onto the neutral SPA user shape.\n *\n * Two result families exist today:\n * - flat provider users (WorkOS `WorkOSUser` et al.): `id`/`workosId`/`email`/\n * `name`/`organizationId` directly on the object;\n * - session-shaped results (better-auth `BetterAuthUser`): `{ session, user }`\n * with the active org on the session.\n */\nfunction toFactoryAuthUser(result: unknown): FactoryAuthUser | null {\n if (!result || typeof result !== 'object') return null;\n const record = result as Record<string, unknown>;\n\n // Session-shaped results: { session, user }.\n if (record.user && typeof record.user === 'object' && record.session && typeof record.session === 'object') {\n const user = record.user as { id?: unknown; email?: unknown; name?: unknown };\n const session = record.session as { activeOrganizationId?: unknown };\n if (typeof user.id !== 'string') return null;\n return {\n id: user.id,\n email: typeof user.email === 'string' ? user.email : undefined,\n name: typeof user.name === 'string' ? user.name : undefined,\n organizationId: typeof session.activeOrganizationId === 'string' ? session.activeOrganizationId : undefined,\n };\n }\n\n // Flat provider users.\n const flat = record as {\n id?: unknown;\n workosId?: unknown;\n email?: unknown;\n name?: unknown;\n organizationId?: unknown;\n };\n const id = typeof flat.id === 'string' ? flat.id : undefined;\n const workosId = typeof flat.workosId === 'string' ? flat.workosId : undefined;\n if (!id && !workosId) return null;\n return {\n id,\n workosId,\n email: typeof flat.email === 'string' ? flat.email : undefined,\n name: typeof flat.name === 'string' ? flat.name : undefined,\n organizationId: typeof flat.organizationId === 'string' ? flat.organizationId : undefined,\n };\n}\n\n/**\n * Resolve the authenticated user for a request via the provider. Never throws:\n * ordinary invalid/expired sessions resolve to `null`.\n */\nasync function authenticateRequest(\n provider: IMastraAuthProvider,\n token: string,\n raw: Request,\n): Promise<FactoryAuthUser | null> {\n try {\n const result = await provider.authenticateToken(token, raw);\n return toFactoryAuthUser(result);\n } catch {\n return null;\n }\n}\n\n/**\n * Bootstrap a personal org for no-org accounts so org-scoped features (GitHub\n * connect) work without leaving the app. Mutates the resolved user so the\n * current request sees the org immediately; subsequent requests resolve it via\n * the provider's own session/membership lookup (providers cache internally).\n * Best-effort: providers swallow their own bootstrap failures, and any\n * unexpected throw leaves the user no-org.\n */\nasync function ensureUserOrg(provider: IMastraAuthProvider, user: FactoryAuthUser): Promise<void> {\n if (getFactoryAuthOrgId(user)) return;\n if (!isOrganizationsProvider(provider)) return;\n const userId = getFactoryAuthUserId(user);\n if (!userId) return;\n try {\n const orgId = await provider.ensureOrganization(userId);\n if (orgId) user.organizationId = orgId;\n } catch {\n // Best-effort: the user stays no-org until a later request succeeds.\n }\n}\n\n/**\n * `Set-Cookie` values that clear the provider's session cookie(s), from the\n * provider's (possibly partial) `ISessionProvider.getClearSessionHeaders`.\n */\nfunction providerClearCookies(provider: IMastraAuthProvider): string[] {\n const getClearSessionHeaders = (provider as Partial<ISessionProvider>).getClearSessionHeaders;\n if (typeof getClearSessionHeaders !== 'function') return [];\n const headers = getClearSessionHeaders.call(provider) ?? {};\n const setCookie = headers['Set-Cookie'];\n if (!setCookie) return [];\n // A provider may join several clearing cookies into one header value.\n return setCookie.split(/,(?=\\s*[^;=,\\s]+=)/).map(cookie => cookie.trim());\n}\n\n/**\n * Fail-closed authorization for organization-level administrative mutations.\n * The caller must belong to the same active organization and the provider must\n * explicitly confirm an admin/owner role.\n */\nexport async function isOrganizationAdmin(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n organizationId: string,\n): Promise<boolean> {\n const user = await ensureFactoryAuthUser(provider, c);\n if (!user || user.organizationId !== organizationId || !provider || !isOrganizationsProvider(provider)) {\n return false;\n }\n const userId = getFactoryAuthUserId(user);\n if (!userId) return false;\n try {\n return await provider.isOrganizationAdmin(organizationId, userId);\n } catch {\n return false;\n }\n}\n\n/**\n * Build the factory's implementation of the `RouteAuth` seam over the\n * resolved provider (`undefined` = auth disabled). Constructed once per boot\n * by `MastraFactory.prepare()` and handed to factory route modules at\n * construction — they never import the factory auth module directly.\n */\nexport function createFactoryRouteAuth(provider: IMastraAuthProvider | undefined): RouteAuth {\n return {\n enabled: () => provider !== undefined,\n ensureUser: (c: Context) => ensureFactoryAuthUser(provider, c),\n tenant: (c: Context) => factoryAuthTenant(c),\n isOrganizationAdmin: (c: Context, organizationId: string) => isOrganizationAdmin(provider, c, organizationId),\n };\n}\n\n/** True when the given provider is WorkOS. Gates WorkOS-only capabilities. */\nexport function isWorkOSAuth(provider: IMastraAuthProvider | undefined): boolean {\n return provider instanceof MastraAuthWorkos;\n}\n\n/**\n * The raw WorkOS provider, for features that need the WorkOS client directly\n * (audit-log export, Admin Portal links). Callers must gate on\n * {@link isWorkOSAuth} first — throws when the provider is not WorkOS.\n */\nexport function getWorkOSProvider(provider: IMastraAuthProvider | undefined): MastraAuthWorkos {\n if (provider instanceof MastraAuthWorkos) return provider;\n throw new Error('WorkOS provider requested but the active factory auth provider is not WorkOS');\n}\n\n/**\n * Resolve the authenticated user for a request, stashing it on the context.\n *\n * The gate only authenticates non-`/auth/*` requests via the `Authorization`\n * header, so cookie-based browser navigations to public `/auth/*` routes (the\n * GitHub connect/callback flow) arrive without a gate-stashed user. This reads\n * the session cookie from the raw request the same way `/auth/me` does,\n * caches the result on the context, and returns it so downstream helpers like\n * {@link factoryAuthTenant} work uniformly on both gated and public routes.\n *\n * Returns `undefined` when there is no valid session (or auth is disabled).\n */\nexport async function ensureFactoryAuthUser(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n): Promise<FactoryAuthUser | undefined> {\n const existing = getFactoryAuthUser(c);\n if (existing) return existing;\n if (!provider) return undefined;\n\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n if (!user) return undefined;\n\n await ensureUserOrg(provider, user);\n\n c.set(FACTORY_AUTH_USER_KEY, user);\n return user;\n}\n\nexport interface MountFactoryAuthOptions {\n /**\n * Explicit auth provider to mount. When omitted, falls back to a WorkOS\n * provider implied by the `WORKOS_*` env vars (back-compat for suites that\n * never boot the factory).\n */\n provider?: IMastraAuthProvider;\n /**\n * Absolute URL the identity provider redirects back to after login (WorkOS\n * env-fallback path only). Defaults to the `WORKOS_REDIRECT_URI` env var.\n */\n redirectUri?: string;\n /** Browser-facing origin used to derive the SSO callback URL. */\n publicUrl?: string;\n}\n\n/**\n * Decide whether a request is a top-level browser navigation (which should be\n * redirected to `/signin`) versus an API/XHR call (which should get a 401 JSON\n * response the SPA can react to).\n */\nfunction isNavigationRequest(path: string, accept: string | undefined): boolean {\n if (path.startsWith('/api/')) return false;\n return (accept ?? '').includes('text/html');\n}\n\n/**\n * Handle the provider-neutral `/auth/me` route: validate the session with the\n * active provider and report the signed-in user (no tokens) to the SPA.\n * `/auth/me` is public (the gate skips `/auth/*`), so it validates the session\n * itself rather than reading a value the gate would have stashed.\n */\nasync function handleAuthMe(provider: IMastraAuthProvider, c: Context): Promise<Response> {\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n // Provider identity for the SPA: `/signin` renders the hosted-login button\n // for WorkOS and an email/password form for better-auth (with sign-up hidden\n // when the provider disables it).\n const signUpDisabled = isCredentialsProvider(provider) && provider.isSignUpEnabled?.() === false;\n const meta = { provider: provider.name, ...(signUpDisabled ? { signUpDisabled: true } : {}) };\n if (!user) {\n return c.json({ authenticated: false, user: null, ...meta });\n }\n // Resolve the org the same way gated requests do (providers cache, so this\n // is a lookup — not a create — after first bootstrap).\n await ensureUserOrg(provider, user);\n return c.json({\n authenticated: true,\n user: {\n userId: getFactoryAuthUserId(user),\n email: user.email,\n name: user.name,\n organizationId: user.organizationId,\n },\n ...meta,\n });\n}\n\n/** Encode a validated returnTo path into the OAuth `state` parameter. */\nfunction encodeState(returnTo: string): string {\n return Buffer.from(JSON.stringify({ returnTo }), 'utf8').toString('base64url');\n}\n\n/** Decode the OAuth `state` parameter back into a sanitized returnTo path. */\nfunction decodeState(state: string | undefined): string {\n if (!state) return '/';\n try {\n const parsed = JSON.parse(Buffer.from(state, 'base64url').toString('utf8')) as { returnTo?: string };\n return sanitizeReturnTo(parsed.returnTo);\n } catch {\n return '/';\n }\n}\n\n/** HTTP methods supported for public auth routes. */\ntype AuthRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'ALL';\n\n/** A public `/auth/*` route derived from the provider's capabilities. */\ninterface AuthRouteSpec {\n path: string;\n method: AuthRouteMethod;\n handler: (c: Context) => Response | Promise<Response>;\n}\n\n/**\n * Derive the public `/auth/*` routes from the provider's capabilities:\n *\n * - `IAuthHttpHandler` → `ALL /auth/api/*` proxy to the provider's own HTTP\n * surface (better-auth sign-in/up/out/session — what the SPA's\n * email/password form posts to).\n * - `ISSOProvider` → hosted-login `GET /auth/login` / `GET /auth/callback` /\n * `GET /auth/logout` (returnTo preserved through the OAuth `state` param).\n * - handler-shaped, non-SSO providers → `GET /auth/login` redirects to the\n * SPA's `/signin` form, `GET /auth/logout` revokes via the provider's\n * sign-out endpoint and clears the session cookie.\n */\nfunction providerAuthRoutes(provider: IMastraAuthProvider, publicUrl?: string): AuthRouteSpec[] {\n const routes: AuthRouteSpec[] = [];\n\n if (isAuthHttpHandler(provider)) {\n routes.push({\n path: '/auth/api/*',\n method: 'ALL',\n handler: c => provider.handleAuthRequest(c.req.raw),\n });\n }\n\n if (isSSOProvider(provider)) {\n routes.push(\n {\n path: '/auth/login',\n method: 'GET',\n handler: async c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n const state = encodeState(returnTo);\n // Build the callback URL from the browser-facing public origin so\n // the OAuth round-trip lands back on the SPA's origin (in dev the\n // SPA is on :5173 and Vite proxies /auth/* to the API on :4111 —\n // deriving from c.req.url would use :4111 and the post-callback\n // redirect to `/` would miss the SPA). Providers that ignore the\n // caller's URI in favor of their own config (e.g. MastraAuthWorkos\n // with an explicit `redirectUri` option) still take precedence.\n const redirectUri = publicUrl ? new URL('/auth/callback', publicUrl).toString() : '';\n const loginUrl = await provider.getLoginUrl(redirectUri, state);\n for (const cookie of (await provider.getLoginCookies?.(redirectUri, state)) ?? []) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect(loginUrl);\n },\n },\n {\n path: '/auth/callback',\n method: 'GET',\n handler: async c => {\n const code = c.req.query('code');\n const returnTo = decodeState(c.req.query('state'));\n if (!code) {\n return c.redirect('/auth/login');\n }\n try {\n const result = await provider.handleCallback(code, c.req.query('state') ?? '');\n if (result.cookies?.length) {\n // Provider populated cookies directly (e.g. WorkOS AuthKit builds\n // its own sealed session cookie inside handleCallback).\n for (const cookie of result.cookies) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } else if (isSessionProvider(provider) && result.tokens) {\n // Fallback for providers that expose ISessionProvider but leave\n // cookie construction to the server (e.g. MastraAuthStudio, which\n // returns just the sealed session as accessToken so\n // getSessionHeaders can scope the cookie to this deployment's\n // domain via MASTRA_COOKIE_DOMAIN / sharedApiUrl auto-detection).\n // Mirrors packages/server/src/server/handlers/auth.ts:492-503.\n const resultUser = result.user as { id: string; organizationId?: string };\n const session = await provider.createSession(resultUser.id, {\n accessToken: result.tokens.accessToken,\n refreshToken: result.tokens.refreshToken,\n expiresAt: result.tokens.expiresAt,\n organizationId: resultUser.organizationId,\n });\n for (const [key, value] of Object.entries(provider.getSessionHeaders(session))) {\n c.header(key, value, { append: true });\n }\n }\n return c.redirect(returnTo);\n } catch {\n // Code exchange failed (expired/replayed code, misconfig). Send the\n // user back to login rather than surfacing a raw error.\n return c.redirect('/auth/login');\n }\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n let logoutUrl: string | null = null;\n try {\n logoutUrl = (await provider.getLogoutUrl?.('/', c.req.raw)) ?? null;\n } catch {\n logoutUrl = null;\n }\n // Clear the session cookie regardless of whether the provider\n // returned a logout URL.\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect(logoutUrl ?? '/');\n },\n },\n );\n } else if (isAuthHttpHandler(provider)) {\n routes.push(\n {\n // Hosted-login equivalent: no hosted page, so send the browser to the\n // SPA's /signin form, preserving returnTo.\n path: '/auth/login',\n method: 'GET',\n handler: c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n // Revoke the session server-side through the provider's own sign-out\n // endpoint and forward its clearing cookies; fall back to our clear\n // cookies regardless.\n try {\n const origin = new URL(c.req.url).origin;\n const response = await provider.handleAuthRequest(\n new Request(`${origin}/auth/api/sign-out`, { method: 'POST', headers: c.req.raw.headers }),\n );\n for (const cookie of response.headers.getSetCookie()) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } catch {\n // No/invalid session: nothing to revoke.\n }\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect('/');\n },\n },\n );\n }\n\n return routes;\n}\n\n/**\n * Register the public `/auth/*` routes on a Hono app: the capability-derived\n * provider routes (login/callback/logout/provider APIs) plus the\n * provider-neutral `/auth/me`. Split out from `mountFactoryAuth` so both the local\n * Hono server and the platform Mastra entry can reuse the exact same handlers.\n */\nexport function registerAuthRoutes(\n app: Hono<any>,\n provider: IMastraAuthProvider,\n options: { publicUrl?: string } = {},\n): void {\n for (const route of providerAuthRoutes(provider, options.publicUrl)) {\n const methods = route.method === 'ALL' ? ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] : [route.method];\n app.on(methods, route.path, c => route.handler(c));\n }\n app.get('/auth/me', c => handleAuthMe(provider, c));\n}\n\n/**\n * Build the public `/auth/*` routes (provider routes + `/auth/me`) as Mastra\n * `server.apiRoutes`. Used by the platform Mastra entry (`src/mastra/index.ts`),\n * which can't register plain Hono routes on the deployer-generated app the way\n * the local server does via {@link registerAuthRoutes}.\n *\n * Handlers are identical to {@link registerAuthRoutes}. All are `requiresAuth: false`\n * (they must be reachable while unauthenticated), and the gate middleware skips\n * `/auth/*` so it never blocks them. `/auth/*` is not under `/api`, so it is a\n * valid custom-route path.\n */\nexport function buildAuthRoutes(provider: IMastraAuthProvider, options: { publicUrl?: string } = {}): ApiRoute[] {\n return [\n // `registerApiRoute` handlers see @mastra/core's bundled hono Context type,\n // which is structurally identical to (but nominally distinct from) the\n // local hono version the route handlers are typed against — cast across\n // the seam.\n ...providerAuthRoutes(provider, options.publicUrl).map(route =>\n registerApiRoute(route.path, {\n method: route.method,\n requiresAuth: false,\n handler: c => route.handler(c as unknown as Context),\n }),\n ),\n registerApiRoute('/auth/me', {\n method: 'GET',\n requiresAuth: false,\n handler: c => handleAuthMe(provider, c as unknown as Context),\n }),\n ];\n}\n\n/**\n * Build the auth gate as a plain Hono middleware handler `(c, next)`. Protects\n * everything that is not a public `/auth/*` route: authenticated requests stash\n * the user on the context and continue; unauthenticated navigations redirect to\n * login and XHR/API calls get a 401 JSON. Shared by the local Hono server\n * (`mountFactoryAuth`) and the platform Mastra entry (`server.middleware`).\n */\nexport function createFactoryAuthGate(provider: IMastraAuthProvider) {\n return async (c: Context, next: () => Promise<void>): Promise<Response | void> => {\n const path = c.req.path;\n if (path.startsWith('/auth/')) {\n return next();\n }\n if (c.req.method === 'POST' && path === '/web/github/webhook') {\n return next();\n }\n // The SPA sign-in page and the static bundle it needs must be reachable\n // while signed out; no user is stashed, so `/api/*` stays protected.\n if (path === '/signin' || path.startsWith('/assets/')) {\n return next();\n }\n\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n\n if (user) {\n // Bootstrap a personal org for no-org accounts so the org id resolves on\n // this request (see ensureFactoryAuthUser for the rationale).\n await ensureUserOrg(provider, user);\n c.set(FACTORY_AUTH_USER_KEY, user);\n c.get('requestContext')?.set('user', user);\n return next();\n }\n\n if (isNavigationRequest(path, c.req.header('Accept'))) {\n const url = new URL(c.req.url);\n const returnTo = sanitizeReturnTo(url.pathname + url.search);\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n }\n\n return c.json({ error: 'unauthorized' }, 401);\n };\n}\n\n/**\n * Mount factory auth gating onto the host app. No-op when auth is disabled\n * (no provider active).\n *\n * Must be called before the Mastra adapter routes, the `/web/*` routes, and\n * the static UI handlers so the gate covers every request. Composes the shared\n * `registerAuthRoutes` + `createFactoryAuthGate` factories so the local Hono server\n * and the platform Mastra entry stay behavior-identical.\n */\nexport function mountFactoryAuth(app: Hono<any>, options: MountFactoryAuthOptions = {}): boolean {\n const provider = options.provider ?? envFallbackAuthProvider(options.redirectUri);\n if (!provider) return false;\n\n registerAuthRoutes(app, provider, { publicUrl: options.publicUrl });\n app.use('*', createFactoryAuthGate(provider));\n return true;\n}\n","import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport type { FactoryAuthUser } from '../auth.js';\nimport { getFactoryAuthOrgId } from '../auth.js';\nimport type { FactoryRunBindingAddress, FactoryRunBindingSessionAddress } from '../storage/domains/work-items/base.js';\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n}\n\nexport function getFactorySessionCoordinates(\n requestContext: RequestContext | undefined,\n): FactoryRunBindingSessionAddress | null {\n if (!requestContext || typeof requestContext.get !== 'function') return null;\n const context = requestContext.get('controller') as AgentControllerRequestContext<FactorySessionState> | undefined;\n const factoryProjectId = context?.getState().factoryProjectId;\n if (!context?.threadId || !context.resourceId || !factoryProjectId) return null;\n return {\n factoryProjectId,\n threadId: context.threadId,\n resourceId: context.resourceId,\n sessionId: context.resourceId,\n };\n}\n\nexport function getFactorySessionAddress(requestContext: RequestContext | undefined): FactoryRunBindingAddress | null {\n const coordinates = getFactorySessionCoordinates(requestContext);\n if (!coordinates || !requestContext || typeof requestContext.get !== 'function') return null;\n const user = requestContext.get('user') as FactoryAuthUser | undefined;\n const orgId = getFactoryAuthOrgId(user);\n if (!orgId) return null;\n return { orgId, ...coordinates };\n}\n","import type {\n FactoryGithubEventName,\n FactoryGithubRuleLeaf,\n FactoryLinearEventName,\n FactoryLinearRuleLeaf,\n FactoryRuleBoard,\n FactoryRuleHandler,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryRules,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\n\nexport interface ResolvedFactoryStageRule {\n phase: 'exit' | 'enter';\n handler: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport function resolveFactoryStageRules(\n rules: FactoryRules,\n input: {\n board: FactoryRuleBoard;\n source: FactoryRuleSource;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n initialEntry?: boolean;\n },\n): ResolvedFactoryStageRule[] {\n if (input.fromStage === input.toStage && !input.initialEntry) return [];\n const boardRules = rules[input.board];\n const resolved: ResolvedFactoryStageRule[] = [];\n const onExit = input.initialEntry ? undefined : boardRules[input.fromStage]?.[input.source]?.onExit;\n if (onExit) resolved.push({ phase: 'exit', handler: onExit });\n const onEnter = boardRules[input.toStage]?.[input.source]?.onEnter;\n if (onEnter) resolved.push({ phase: 'enter', handler: onEnter });\n return resolved;\n}\n\nexport function resolveFactoryToolRule(rules: FactoryRules, toolName: string): FactoryToolRuleLeaf['onResult'] {\n return rules.tools[toolName]?.onResult;\n}\n\nexport function resolveFactoryGithubRule(\n rules: FactoryRules,\n event: FactoryGithubEventName,\n): FactoryGithubRuleLeaf['onEvent'] {\n return rules.github[event]?.onEvent;\n}\n\nexport function resolveFactoryLinearRule(\n rules: FactoryRules,\n event: FactoryLinearEventName,\n): FactoryLinearRuleLeaf['onEvent'] {\n return rules.linear[event]?.onEvent;\n}\n\nexport type ResolvedFactoryToolRule = FactoryRuleHandler<FactoryToolResultRuleContext>;\n","export type WorkItemSource = 'github-issue' | 'github-pr' | 'linear-issue' | 'manual';\n\nexport const FACTORY_RULE_STAGES = ['intake', 'triage', 'planning', 'execute', 'review', 'done', 'canceled'] as const;\nexport type FactoryRuleStage = (typeof FACTORY_RULE_STAGES)[number];\n\nexport const FACTORY_RULE_BOARDS = ['work', 'review'] as const;\nexport type FactoryRuleBoard = (typeof FACTORY_RULE_BOARDS)[number];\n\nexport const FACTORY_RULE_SOURCES = ['issue', 'pullRequest', 'linearIssue', 'manual'] as const;\nexport type FactoryRuleSource = (typeof FACTORY_RULE_SOURCES)[number];\n\nexport const FACTORY_GITHUB_EVENTS = [\n 'issueOpened',\n 'pullRequestOpened',\n 'pullRequestUpdated',\n 'pullRequestReviewRequested',\n 'pullRequestMerged',\n] as const;\nexport type FactoryGithubEventName = (typeof FACTORY_GITHUB_EVENTS)[number];\n\nexport const FACTORY_LINEAR_EVENTS = ['issueObserved'] as const;\nexport type FactoryLinearEventName = (typeof FACTORY_LINEAR_EVENTS)[number];\n\nexport type FactoryRuleJsonValue =\n null | boolean | number | string | FactoryRuleJsonValue[] | { [key: string]: FactoryRuleJsonValue };\n\nexport interface FactoryRuleItemContext {\n id: string;\n source: WorkItemSource;\n sourceKey: string | null;\n parentWorkItemId: string | null;\n title: string;\n url: string | null;\n stages: readonly string[];\n}\n\nexport type FactoryRuleActor =\n | { type: 'human'; id: string }\n | { type: 'agent'; bindingId: string; role: string }\n | { type: 'github'; login: string; trusted: boolean; factoryAuthored: boolean }\n | { type: 'system'; id: string };\n\nexport interface FactoryRuleIngressIdentity {\n type: 'human' | 'agent' | 'toolResult' | 'github' | 'linear' | 'rule';\n id: string;\n}\n\nexport interface FactoryRuleCausalEntry {\n ingressId: string;\n decisionType: FactoryCommitDecision['type'];\n}\n\nexport interface FactoryRuleContextBase {\n tenant: { orgId: string; projectId: string };\n actor: FactoryRuleActor;\n ingress: FactoryRuleIngressIdentity;\n cause: string;\n causalChain: readonly FactoryRuleCausalEntry[];\n ruleSetVersion: string;\n}\n\nexport interface FactoryBoundRuleContext extends FactoryRuleContextBase {\n item: FactoryRuleItemContext;\n board: FactoryRuleBoard;\n itemRevision: number;\n}\n\nexport interface FactoryStageRuleContext extends FactoryBoundRuleContext {\n source: FactoryRuleSource;\n stage: FactoryRuleStage;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n}\n\nexport interface FactoryToolResultRuleContext extends FactoryBoundRuleContext {\n toolName: string;\n threadId: string;\n assistantMessageId: string;\n toolCallId: string;\n result: {\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n };\n}\n\nexport interface FactoryGithubRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n event: FactoryGithubEventName;\n deliveryId: string;\n factory: { createdAt: string };\n repository: { id: number; fullName: string };\n issue?: { number: number; title: string; url: string; createdAt?: string };\n pullRequest?: {\n number: number;\n title: string;\n url: string;\n createdAt?: string;\n state: 'open' | 'closed';\n merged: boolean;\n headBranch: string;\n baseBranch: string;\n };\n}\n\nexport interface FactoryLinearRuleContext extends FactoryRuleContextBase {\n item?: FactoryRuleItemContext;\n board?: FactoryRuleBoard;\n itemRevision?: number;\n event: FactoryLinearEventName;\n issue: {\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n team: string | null;\n labels: readonly string[];\n createdAt: string;\n updatedAt: string;\n };\n}\n\nexport type FactoryRuleHandler<TContext> = (\n context: Readonly<TContext>,\n) => FactoryRuleDecision | void | Promise<FactoryRuleDecision | void>;\n\nexport interface FactoryBoardRuleLeaf {\n onEnter?: FactoryRuleHandler<FactoryStageRuleContext>;\n onExit?: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport interface FactoryToolRuleLeaf {\n onResult?: FactoryRuleHandler<FactoryToolResultRuleContext>;\n}\n\nexport interface FactoryGithubRuleLeaf {\n onEvent?: FactoryRuleHandler<FactoryGithubRuleContext>;\n}\n\nexport interface FactoryLinearRuleLeaf {\n onEvent?: FactoryRuleHandler<FactoryLinearRuleContext>;\n}\n\nexport type FactoryBoardRules = Partial<\n Record<FactoryRuleStage, Partial<Record<FactoryRuleSource, FactoryBoardRuleLeaf>>>\n>;\n\nexport interface FactoryRules {\n version: string;\n work: FactoryBoardRules;\n review: FactoryBoardRules;\n tools: Record<string, FactoryToolRuleLeaf>;\n github: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>>;\n linear: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>>;\n}\n\nexport interface FactoryRulesOverrides {\n work?: FactoryBoardRules;\n review?: FactoryBoardRules;\n tools?: Record<string, FactoryToolRuleLeaf>;\n github?: Partial<Record<FactoryGithubEventName, FactoryGithubRuleLeaf>>;\n linear?: Partial<Record<FactoryLinearEventName, FactoryLinearRuleLeaf>>;\n}\n\nexport type FactoryRuleRejectionCode =\n | 'forbidden'\n | 'invalid_transition'\n | 'missing_binding'\n | 'stale'\n | 'timeout'\n | 'rule_error'\n | 'causal_depth_exceeded'\n | 'repeated_transition';\n\nexport interface FactoryRuleRejectDecision {\n type: 'reject';\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\ninterface FactoryCommitDecisionBase {\n idempotencyKey: string;\n}\n\nexport interface FactoryTransitionDecision extends FactoryCommitDecisionBase {\n type: 'transition';\n board: FactoryRuleBoard;\n stage: FactoryRuleStage;\n}\n\nexport interface FactoryUpsertLinkedWorkItemDecision extends FactoryCommitDecisionBase {\n type: 'upsertLinkedWorkItem';\n board: FactoryRuleBoard;\n source: WorkItemSource;\n sourceKey: string;\n title: string;\n url: string | null;\n stage: FactoryRuleStage;\n metadata?: Record<string, FactoryRuleJsonValue>;\n}\n\nexport interface FactoryInvokeSkillDecision extends FactoryCommitDecisionBase {\n type: 'invokeSkill';\n role: string;\n skillName: string;\n arguments?: string;\n precedingMessage?: string;\n}\n\nexport interface FactorySendMessageDecision extends FactoryCommitDecisionBase {\n type: 'sendMessage';\n role: string;\n message: string;\n priority?: 'medium' | 'high' | 'urgent';\n idleBehavior?: 'persist' | 'wake';\n prepareBinding?: boolean;\n}\n\nexport interface FactoryNotifyDecision extends FactoryCommitDecisionBase {\n type: 'notify';\n title: string;\n body?: string;\n level?: 'info' | 'warning' | 'error';\n}\n\nexport type FactoryCommitDecision =\n | FactoryTransitionDecision\n | FactoryUpsertLinkedWorkItemDecision\n | FactoryInvokeSkillDecision\n | FactorySendMessageDecision\n | FactoryNotifyDecision;\n\nexport type FactoryRuleDecision = FactoryRuleRejectDecision | FactoryCommitDecision;\n\nexport interface FactoryTransitionResultAccepted {\n status: 'accepted';\n transitionId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n decisions: FactoryCommitDecision[];\n}\n\nexport interface FactoryTransitionResultRejected {\n status: 'rejected';\n transitionId: string;\n itemId: string;\n code: FactoryRuleRejectionCode;\n reason: string;\n}\n\nexport type FactoryTransitionResult = FactoryTransitionResultAccepted | FactoryTransitionResultRejected;\n\nexport function factoryRuleSourceForWorkItem(source: WorkItemSource): FactoryRuleSource {\n switch (source) {\n case 'github-issue':\n return 'issue';\n case 'github-pr':\n return 'pullRequest';\n case 'linear-issue':\n return 'linearIssue';\n case 'manual':\n return 'manual';\n }\n}\n","import {\n FACTORY_GITHUB_EVENTS,\n FACTORY_LINEAR_EVENTS,\n FACTORY_RULE_BOARDS,\n FACTORY_RULE_SOURCES,\n FACTORY_RULE_STAGES,\n} from './types.js';\nimport type {\n FactoryBoardRules,\n FactoryCommitDecision,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRules,\n FactoryRuleRejectionCode,\n WorkItemSource,\n} from './types.js';\n\nexport const MAX_FACTORY_RULE_CAUSAL_DEPTH = 8;\n\nconst MAX_VERSION_LENGTH = 128;\nconst MAX_IDEMPOTENCY_KEY_LENGTH = 256;\nconst MAX_REASON_LENGTH = 512;\nconst MAX_TITLE_LENGTH = 512;\nconst MAX_MESSAGE_LENGTH = 8_192;\nconst MAX_ARGUMENTS_LENGTH = 4_096;\nconst MAX_ROLE_LENGTH = 32;\nconst MAX_SKILL_NAME_LENGTH = 128;\nconst MAX_SOURCE_KEY_LENGTH = 256;\nconst MAX_URL_LENGTH = 2_048;\nconst MAX_METADATA_JSON_LENGTH = 16_384;\nconst MAX_JSON_DEPTH = 8;\nconst MAX_JSON_COLLECTION_SIZE = 100;\n\nconst IDENTIFIER_RE = /^[a-z0-9][a-z0-9_-]*$/i;\nconst SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst SENSITIVE_KEY_RE = /(?:authorization|cookie|credential|password|secret|token)/i;\nconst WORK_ITEM_SOURCES: readonly WorkItemSource[] = ['github-issue', 'github-pr', 'linear-issue', 'manual'];\nconst REJECTION_CODES: readonly FactoryRuleRejectionCode[] = [\n 'forbidden',\n 'invalid_transition',\n 'missing_binding',\n 'stale',\n 'timeout',\n 'rule_error',\n 'causal_depth_exceeded',\n 'repeated_transition',\n];\n\nexport class FactoryRuleValidationError extends Error {\n readonly code = 'invalid_factory_rule';\n\n constructor(message: string) {\n super(message);\n this.name = 'FactoryRuleValidationError';\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction assertExactKeys(value: Record<string, unknown>, keys: readonly string[], label: string): void {\n const allowed = new Set(keys);\n if (Object.keys(value).some(key => !allowed.has(key))) {\n throw new FactoryRuleValidationError(`${label} contains an unsupported field.`);\n }\n}\n\nfunction boundedString(value: unknown, label: string, max: number, pattern?: RegExp): string {\n if (typeof value !== 'string') throw new FactoryRuleValidationError(`${label} must be a string.`);\n const normalized = value.trim();\n if (normalized.length === 0 || normalized.length > max || (pattern && !pattern.test(normalized))) {\n throw new FactoryRuleValidationError(`${label} is invalid.`);\n }\n return normalized;\n}\n\nfunction optionalBoundedString(value: unknown, label: string, max: number): string | undefined {\n if (value === undefined) return undefined;\n return boundedString(value, label, max);\n}\n\nfunction enumValue<T extends string>(value: unknown, allowed: readonly T[], label: string): T {\n if (typeof value !== 'string' || !allowed.includes(value as T)) {\n throw new FactoryRuleValidationError(`${label} is invalid.`);\n }\n return value as T;\n}\n\nexport function normalizeFactoryRuleJsonValue(\n value: unknown,\n depth = 0,\n seen = new Set<object>(),\n): FactoryRuleJsonValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return value;\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) throw new FactoryRuleValidationError('Rule metadata must contain finite numbers.');\n return value;\n }\n if (depth >= MAX_JSON_DEPTH || (typeof value !== 'object' && !Array.isArray(value))) {\n throw new FactoryRuleValidationError('Rule metadata is not bounded JSON.');\n }\n if (seen.has(value as object)) throw new FactoryRuleValidationError('Rule metadata must not contain cycles.');\n seen.add(value as object);\n try {\n if (Array.isArray(value)) {\n if (value.length > MAX_JSON_COLLECTION_SIZE) {\n throw new FactoryRuleValidationError('Rule metadata contains too many entries.');\n }\n return value.map(entry => normalizeFactoryRuleJsonValue(entry, depth + 1, seen));\n }\n if (!isPlainObject(value)) throw new FactoryRuleValidationError('Rule metadata must use plain objects.');\n const entries = Object.entries(value);\n if (entries.length > MAX_JSON_COLLECTION_SIZE) {\n throw new FactoryRuleValidationError('Rule metadata contains too many fields.');\n }\n const sanitized: Record<string, FactoryRuleJsonValue> = {};\n for (const [key, entry] of entries) {\n const normalizedKey = boundedString(key, 'Rule metadata key', 128, IDENTIFIER_RE);\n sanitized[normalizedKey] = SENSITIVE_KEY_RE.test(normalizedKey)\n ? '[REDACTED]'\n : normalizeFactoryRuleJsonValue(entry, depth + 1, seen);\n }\n return sanitized;\n } finally {\n seen.delete(value as object);\n }\n}\n\nfunction sanitizeMetadata(value: unknown): Record<string, FactoryRuleJsonValue> | undefined {\n if (value === undefined) return undefined;\n const sanitized = normalizeFactoryRuleJsonValue(value);\n if (!isPlainObject(sanitized)) throw new FactoryRuleValidationError('Rule metadata must be an object.');\n if (JSON.stringify(sanitized).length > MAX_METADATA_JSON_LENGTH) {\n throw new FactoryRuleValidationError('Rule metadata is too large.');\n }\n return sanitized;\n}\n\nfunction validateBoardRules(rules: unknown, label: string): asserts rules is FactoryBoardRules {\n if (!isPlainObject(rules)) throw new FactoryRuleValidationError(`${label} must be an object.`);\n for (const [stage, sources] of Object.entries(rules)) {\n enumValue(stage, FACTORY_RULE_STAGES, `${label} stage`);\n if (!isPlainObject(sources)) throw new FactoryRuleValidationError(`${label}.${stage} must be an object.`);\n for (const [source, leaf] of Object.entries(sources)) {\n enumValue(source, FACTORY_RULE_SOURCES, `${label}.${stage} source`);\n if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`${label}.${stage}.${source} must be an object.`);\n assertExactKeys(leaf, ['onEnter', 'onExit'], `${label}.${stage}.${source}`);\n for (const handler of Object.values(leaf)) {\n if (handler !== undefined && typeof handler !== 'function') {\n throw new FactoryRuleValidationError(`${label}.${stage}.${source} handlers must be functions.`);\n }\n }\n }\n }\n}\n\nexport function assertFactoryRules(rules: unknown): asserts rules is FactoryRules {\n if (!isPlainObject(rules)) throw new FactoryRuleValidationError('Factory rules must be an object.');\n assertExactKeys(rules, ['version', 'work', 'review', 'tools', 'github', 'linear'], 'Factory rules');\n boundedString(rules.version, 'Factory rule version', MAX_VERSION_LENGTH);\n validateBoardRules(rules.work, 'Factory rules.work');\n validateBoardRules(rules.review, 'Factory rules.review');\n\n if (!isPlainObject(rules.tools)) throw new FactoryRuleValidationError('Factory rules.tools must be an object.');\n for (const [toolName, leaf] of Object.entries(rules.tools)) {\n boundedString(toolName, 'Factory tool name', 128, IDENTIFIER_RE);\n if (!isPlainObject(leaf))\n throw new FactoryRuleValidationError(`Factory rules.tools.${toolName} must be an object.`);\n assertExactKeys(leaf, ['onResult'], `Factory rules.tools.${toolName}`);\n if (leaf.onResult !== undefined && typeof leaf.onResult !== 'function') {\n throw new FactoryRuleValidationError(`Factory rules.tools.${toolName}.onResult must be a function.`);\n }\n }\n\n if (!isPlainObject(rules.github)) throw new FactoryRuleValidationError('Factory rules.github must be an object.');\n for (const [event, leaf] of Object.entries(rules.github)) {\n enumValue(event, FACTORY_GITHUB_EVENTS, 'Factory GitHub event');\n if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.github.${event} must be an object.`);\n assertExactKeys(leaf, ['onEvent'], `Factory rules.github.${event}`);\n if (leaf.onEvent !== undefined && typeof leaf.onEvent !== 'function') {\n throw new FactoryRuleValidationError(`Factory rules.github.${event}.onEvent must be a function.`);\n }\n }\n\n if (!isPlainObject(rules.linear)) throw new FactoryRuleValidationError('Factory rules.linear must be an object.');\n for (const [event, leaf] of Object.entries(rules.linear)) {\n enumValue(event, FACTORY_LINEAR_EVENTS, 'Factory Linear event');\n if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.linear.${event} must be an object.`);\n assertExactKeys(leaf, ['onEvent'], `Factory rules.linear.${event}`);\n if (leaf.onEvent !== undefined && typeof leaf.onEvent !== 'function') {\n throw new FactoryRuleValidationError(`Factory rules.linear.${event}.onEvent must be a function.`);\n }\n }\n}\n\nfunction commonCommitFields(value: Record<string, unknown>): { idempotencyKey: string } {\n return {\n idempotencyKey: boundedString(value.idempotencyKey, 'Factory decision idempotencyKey', MAX_IDEMPOTENCY_KEY_LENGTH),\n };\n}\n\nexport function validateFactoryRuleDecision(value: unknown, causalDepth = 0): FactoryRuleDecision {\n if (causalDepth > MAX_FACTORY_RULE_CAUSAL_DEPTH) {\n throw new FactoryRuleValidationError('Factory rule causal depth exceeded.');\n }\n if (!isPlainObject(value)) throw new FactoryRuleValidationError('Factory rule decision must be an object.');\n const type = value.type;\n if (typeof type !== 'string') throw new FactoryRuleValidationError('Factory rule decision type is required.');\n\n switch (type) {\n case 'reject': {\n assertExactKeys(value, ['type', 'code', 'reason'], 'Factory reject decision');\n return {\n type,\n code: enumValue(value.code, REJECTION_CODES, 'Factory rejection code'),\n reason: boundedString(value.reason, 'Factory rejection reason', MAX_REASON_LENGTH),\n };\n }\n case 'transition': {\n assertExactKeys(value, ['type', 'idempotencyKey', 'board', 'stage'], 'Factory transition decision');\n return {\n type,\n ...commonCommitFields(value),\n board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory transition board'),\n stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory transition stage'),\n };\n }\n case 'upsertLinkedWorkItem': {\n assertExactKeys(\n value,\n ['type', 'idempotencyKey', 'board', 'source', 'sourceKey', 'title', 'url', 'stage', 'metadata'],\n 'Factory linked work item decision',\n );\n const url = value.url;\n if (url !== null && (typeof url !== 'string' || url.length > MAX_URL_LENGTH || !/^https?:\\/\\//.test(url))) {\n throw new FactoryRuleValidationError('Factory linked work item URL is invalid.');\n }\n const metadata = sanitizeMetadata(value.metadata);\n return {\n type,\n ...commonCommitFields(value),\n board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory linked work item board'),\n source: enumValue(value.source, WORK_ITEM_SOURCES, 'Factory linked work item source'),\n sourceKey: boundedString(value.sourceKey, 'Factory linked work item sourceKey', MAX_SOURCE_KEY_LENGTH),\n title: boundedString(value.title, 'Factory linked work item title', MAX_TITLE_LENGTH),\n url,\n stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory linked work item stage'),\n ...(metadata ? { metadata } : {}),\n };\n }\n case 'invokeSkill': {\n assertExactKeys(\n value,\n ['type', 'idempotencyKey', 'role', 'skillName', 'arguments', 'precedingMessage'],\n 'Factory invoke skill decision',\n );\n const args = optionalBoundedString(value.arguments, 'Factory skill arguments', MAX_ARGUMENTS_LENGTH);\n const precedingMessage = optionalBoundedString(\n value.precedingMessage,\n 'Factory skill preceding message',\n MAX_MESSAGE_LENGTH,\n );\n return {\n type,\n ...commonCommitFields(value),\n role: boundedString(value.role, 'Factory skill role', MAX_ROLE_LENGTH, IDENTIFIER_RE),\n skillName: boundedString(value.skillName, 'Factory skill name', MAX_SKILL_NAME_LENGTH, SKILL_NAME_RE),\n ...(args ? { arguments: args } : {}),\n ...(precedingMessage ? { precedingMessage } : {}),\n };\n }\n case 'sendMessage': {\n assertExactKeys(\n value,\n ['type', 'idempotencyKey', 'role', 'message', 'priority', 'idleBehavior', 'prepareBinding'],\n 'Factory send message decision',\n );\n const priority =\n value.priority === undefined\n ? undefined\n : enumValue(value.priority, ['medium', 'high', 'urgent'] as const, 'Factory message priority');\n const idleBehavior =\n value.idleBehavior === undefined\n ? undefined\n : enumValue(value.idleBehavior, ['persist', 'wake'] as const, 'Factory message idle behavior');\n if (value.prepareBinding !== undefined && typeof value.prepareBinding !== 'boolean') {\n throw new FactoryRuleValidationError('Factory message prepareBinding must be a boolean.');\n }\n return {\n type,\n ...commonCommitFields(value),\n role: boundedString(value.role, 'Factory message role', MAX_ROLE_LENGTH, IDENTIFIER_RE),\n message: boundedString(value.message, 'Factory message', MAX_MESSAGE_LENGTH),\n ...(priority ? { priority } : {}),\n ...(idleBehavior ? { idleBehavior } : {}),\n ...(value.prepareBinding === true ? { prepareBinding: true } : {}),\n };\n }\n case 'notify': {\n assertExactKeys(value, ['type', 'idempotencyKey', 'title', 'body', 'level'], 'Factory notify decision');\n const body = optionalBoundedString(value.body, 'Factory notification body', MAX_MESSAGE_LENGTH);\n const level =\n value.level === undefined\n ? undefined\n : enumValue(value.level, ['info', 'warning', 'error'] as const, 'Factory notification level');\n return {\n type,\n ...commonCommitFields(value),\n title: boundedString(value.title, 'Factory notification title', MAX_TITLE_LENGTH),\n ...(body ? { body } : {}),\n ...(level ? { level } : {}),\n };\n }\n default:\n throw new FactoryRuleValidationError('Factory rule decision type is unsupported.');\n }\n}\n\nexport function validateFactoryRuleDecisions(values: readonly unknown[], causalDepth = 0): FactoryCommitDecision[] {\n if (values.length > MAX_JSON_COLLECTION_SIZE) {\n throw new FactoryRuleValidationError('Factory rule produced too many decisions.');\n }\n const decisions: FactoryCommitDecision[] = [];\n for (const value of values) {\n const decision = validateFactoryRuleDecision(value, causalDepth);\n if (decision.type === 'reject') {\n throw new FactoryRuleValidationError('A rejection cannot be persisted with commit decisions.');\n }\n decisions.push(decision);\n }\n const keys = decisions.map(decision => decision.idempotencyKey);\n if (new Set(keys).size !== keys.length) {\n throw new FactoryRuleValidationError('Factory decisions require unique idempotency keys.');\n }\n return decisions;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,17 @@
1
+ import type { FactoryGithubEventName, FactoryGithubRuleLeaf, FactoryLinearEventName, FactoryLinearRuleLeaf, FactoryRuleBoard, FactoryRuleHandler, FactoryRuleSource, FactoryRuleStage, FactoryRules, FactoryStageRuleContext, FactoryToolResultRuleContext, FactoryToolRuleLeaf } from './types.js';
2
+ export interface ResolvedFactoryStageRule {
3
+ phase: 'exit' | 'enter';
4
+ handler: FactoryRuleHandler<FactoryStageRuleContext>;
5
+ }
6
+ export declare function resolveFactoryStageRules(rules: FactoryRules, input: {
7
+ board: FactoryRuleBoard;
8
+ source: FactoryRuleSource;
9
+ fromStage: FactoryRuleStage;
10
+ toStage: FactoryRuleStage;
11
+ initialEntry?: boolean;
12
+ }): ResolvedFactoryStageRule[];
13
+ export declare function resolveFactoryToolRule(rules: FactoryRules, toolName: string): FactoryToolRuleLeaf['onResult'];
14
+ export declare function resolveFactoryGithubRule(rules: FactoryRules, event: FactoryGithubEventName): FactoryGithubRuleLeaf['onEvent'];
15
+ export declare function resolveFactoryLinearRule(rules: FactoryRules, event: FactoryLinearEventName): FactoryLinearRuleLeaf['onEvent'];
16
+ export type ResolvedFactoryToolRule = FactoryRuleHandler<FactoryToolResultRuleContext>;
17
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/rules/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,OAAO,EAAE,kBAAkB,CAAC,uBAAuB,CAAC,CAAC;CACtD;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE;IACL,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GACA,wBAAwB,EAAE,CAS5B;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAE7G;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,sBAAsB,GAC5B,qBAAqB,CAAC,SAAS,CAAC,CAElC;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,sBAAsB,GAC5B,qBAAqB,CAAC,SAAS,CAAC,CAElC;AAED,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,4BAA4B,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ // src/rules/resolve.ts
2
+ function resolveFactoryStageRules(rules, input) {
3
+ if (input.fromStage === input.toStage && !input.initialEntry) return [];
4
+ const boardRules = rules[input.board];
5
+ const resolved = [];
6
+ const onExit = input.initialEntry ? void 0 : boardRules[input.fromStage]?.[input.source]?.onExit;
7
+ if (onExit) resolved.push({ phase: "exit", handler: onExit });
8
+ const onEnter = boardRules[input.toStage]?.[input.source]?.onEnter;
9
+ if (onEnter) resolved.push({ phase: "enter", handler: onEnter });
10
+ return resolved;
11
+ }
12
+ function resolveFactoryToolRule(rules, toolName) {
13
+ return rules.tools[toolName]?.onResult;
14
+ }
15
+ function resolveFactoryGithubRule(rules, event) {
16
+ return rules.github[event]?.onEvent;
17
+ }
18
+ function resolveFactoryLinearRule(rules, event) {
19
+ return rules.linear[event]?.onEvent;
20
+ }
21
+ export {
22
+ resolveFactoryGithubRule,
23
+ resolveFactoryLinearRule,
24
+ resolveFactoryStageRules,
25
+ resolveFactoryToolRule
26
+ };
27
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rules/resolve.ts"],"sourcesContent":["import type {\n FactoryGithubEventName,\n FactoryGithubRuleLeaf,\n FactoryLinearEventName,\n FactoryLinearRuleLeaf,\n FactoryRuleBoard,\n FactoryRuleHandler,\n FactoryRuleSource,\n FactoryRuleStage,\n FactoryRules,\n FactoryStageRuleContext,\n FactoryToolResultRuleContext,\n FactoryToolRuleLeaf,\n} from './types.js';\n\nexport interface ResolvedFactoryStageRule {\n phase: 'exit' | 'enter';\n handler: FactoryRuleHandler<FactoryStageRuleContext>;\n}\n\nexport function resolveFactoryStageRules(\n rules: FactoryRules,\n input: {\n board: FactoryRuleBoard;\n source: FactoryRuleSource;\n fromStage: FactoryRuleStage;\n toStage: FactoryRuleStage;\n initialEntry?: boolean;\n },\n): ResolvedFactoryStageRule[] {\n if (input.fromStage === input.toStage && !input.initialEntry) return [];\n const boardRules = rules[input.board];\n const resolved: ResolvedFactoryStageRule[] = [];\n const onExit = input.initialEntry ? undefined : boardRules[input.fromStage]?.[input.source]?.onExit;\n if (onExit) resolved.push({ phase: 'exit', handler: onExit });\n const onEnter = boardRules[input.toStage]?.[input.source]?.onEnter;\n if (onEnter) resolved.push({ phase: 'enter', handler: onEnter });\n return resolved;\n}\n\nexport function resolveFactoryToolRule(rules: FactoryRules, toolName: string): FactoryToolRuleLeaf['onResult'] {\n return rules.tools[toolName]?.onResult;\n}\n\nexport function resolveFactoryGithubRule(\n rules: FactoryRules,\n event: FactoryGithubEventName,\n): FactoryGithubRuleLeaf['onEvent'] {\n return rules.github[event]?.onEvent;\n}\n\nexport function resolveFactoryLinearRule(\n rules: FactoryRules,\n event: FactoryLinearEventName,\n): FactoryLinearRuleLeaf['onEvent'] {\n return rules.linear[event]?.onEvent;\n}\n\nexport type ResolvedFactoryToolRule = FactoryRuleHandler<FactoryToolResultRuleContext>;\n"],"mappings":";AAoBO,SAAS,yBACd,OACA,OAO4B;AAC5B,MAAI,MAAM,cAAc,MAAM,WAAW,CAAC,MAAM,aAAc,QAAO,CAAC;AACtE,QAAM,aAAa,MAAM,MAAM,KAAK;AACpC,QAAM,WAAuC,CAAC;AAC9C,QAAM,SAAS,MAAM,eAAe,SAAY,WAAW,MAAM,SAAS,IAAI,MAAM,MAAM,GAAG;AAC7F,MAAI,OAAQ,UAAS,KAAK,EAAE,OAAO,QAAQ,SAAS,OAAO,CAAC;AAC5D,QAAM,UAAU,WAAW,MAAM,OAAO,IAAI,MAAM,MAAM,GAAG;AAC3D,MAAI,QAAS,UAAS,KAAK,EAAE,OAAO,SAAS,SAAS,QAAQ,CAAC;AAC/D,SAAO;AACT;AAEO,SAAS,uBAAuB,OAAqB,UAAmD;AAC7G,SAAO,MAAM,MAAM,QAAQ,GAAG;AAChC;AAEO,SAAS,yBACd,OACA,OACkC;AAClC,SAAO,MAAM,OAAO,KAAK,GAAG;AAC9B;AAEO,SAAS,yBACd,OACA,OACkC;AAClC,SAAO,MAAM,OAAO,KAAK,GAAG;AAC9B;","names":[]}
@@ -0,0 +1,58 @@
1
+ import type { MastraCodeState } from '@mastra/code-sdk/schema';
2
+ import type { AgentController } from '@mastra/core/agent-controller';
3
+ import { RequestContext } from '@mastra/core/request-context';
4
+ import type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';
5
+ import type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';
6
+ import type { FactoryTransitionService } from './transition-service.js';
7
+ import type { FactoryRuleStage, FactoryTransitionResult } from './types.js';
8
+ export interface FactoryStartRequest {
9
+ orgId: string;
10
+ userId: string;
11
+ factoryProjectId: string;
12
+ sessionId: string;
13
+ threadTitle: string;
14
+ threadTags?: Record<string, string>;
15
+ kickoffKey: string;
16
+ invocation?: {
17
+ type: 'prompt';
18
+ prompt: string;
19
+ } | {
20
+ type: 'skill';
21
+ skillName: string;
22
+ arguments: string;
23
+ };
24
+ destinationStage: FactoryRuleStage;
25
+ workItem: {
26
+ id?: string;
27
+ role: string;
28
+ input: CreateWorkItemInput;
29
+ };
30
+ requestContext?: RequestContext;
31
+ }
32
+ export declare class FactoryStartTransitionError extends Error {
33
+ readonly result: Extract<FactoryTransitionResult, {
34
+ status: 'rejected';
35
+ }>;
36
+ constructor(result: Extract<FactoryTransitionResult, {
37
+ status: 'rejected';
38
+ }>);
39
+ }
40
+ export interface FactoryStartPreparedResult {
41
+ workItemId: string;
42
+ bindingId: string;
43
+ threadId: string;
44
+ resourceId: string;
45
+ sessionId: string;
46
+ branch: string;
47
+ revision: number;
48
+ kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';
49
+ replayed: boolean;
50
+ }
51
+ type FactoryController = AgentController<MastraCodeState>;
52
+ export declare class FactoryStartCoordinator {
53
+ #private;
54
+ constructor(controller: FactoryController, storage: WorkItemsStorage, transitionService?: Pick<FactoryTransitionService, 'transition'>, sourceControl?: SourceControlStorageHandle);
55
+ prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult>;
56
+ }
57
+ export {};
58
+ //# sourceMappingURL=start-coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-coordinator.d.ts","sourceRoot":"","sources":["../../src/rules/start-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAwB,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAClH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACnG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE;QACR,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,mBAAmB,CAAC;KAC5B,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;gBAE9D,MAAM,EAAE,OAAO,CAAC,uBAAuB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC;CAK7E;AAED,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,iBAAiB,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;AAqD1D,qBAAa,uBAAuB;;gBAOhC,UAAU,EAAE,iBAAiB,EAC7B,OAAO,EAAE,gBAAgB,EACzB,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,EAChE,aAAa,CAAC,EAAE,0BAA0B;IAQtC,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,0BAA0B,CAAC;CAoEjF"}
@@ -0,0 +1,136 @@
1
+ // src/rules/start-coordinator.ts
2
+ import { RequestContext } from "@mastra/core/request-context";
3
+ import { formatSkillActivation } from "@mastra/core/workspace";
4
+ var FactoryStartTransitionError = class extends Error {
5
+ result;
6
+ constructor(result) {
7
+ super(result.reason);
8
+ this.name = "FactoryStartTransitionError";
9
+ this.result = result;
10
+ }
11
+ };
12
+ function escapeSkillBoundary(value) {
13
+ return value.replaceAll("</skill>", "&lt;/skill&gt;");
14
+ }
15
+ async function resolveKickoffMessage(session, invocation) {
16
+ if (!invocation) return null;
17
+ if (invocation.type === "prompt") return invocation.prompt;
18
+ const skills = session.getWorkspace().skills;
19
+ await skills?.maybeRefresh();
20
+ const skill = await skills?.get(invocation.skillName);
21
+ if (!skill || skill["user-invocable"] === false) {
22
+ throw new Error(`Skill not found: ${invocation.skillName}.`);
23
+ }
24
+ const args = invocation.arguments.trim();
25
+ const content = `${formatSkillActivation(skill)}${args ? `
26
+
27
+ ARGUMENTS: ${args}` : ""}`.trim();
28
+ return `<skill name="${skill.name}">
29
+ ${escapeSkillBoundary(content)}
30
+ </skill>`;
31
+ }
32
+ async function resolveSourceSession(storage, request) {
33
+ const session = await storage.sessions.getBySessionId(request.sessionId);
34
+ if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {
35
+ throw new Error("Factory session not found");
36
+ }
37
+ const projectRepository = await storage.projectRepositories.get({
38
+ orgId: request.orgId,
39
+ id: session.projectRepositoryId
40
+ });
41
+ if (!projectRepository) throw new Error("Factory session repository not found");
42
+ const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });
43
+ if (!connection || connection.factoryProjectId !== request.factoryProjectId) {
44
+ throw new Error("Factory session does not belong to this project");
45
+ }
46
+ return session;
47
+ }
48
+ async function configureThread(session, request) {
49
+ const threadId = session.thread.requireId();
50
+ await session.thread.rename({ title: request.threadTitle });
51
+ const settings = { ...request.threadTags ?? {}, factorySessionId: request.sessionId };
52
+ await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));
53
+ return threadId;
54
+ }
55
+ var FactoryStartCoordinator = class {
56
+ #controller;
57
+ #storage;
58
+ #transitionService;
59
+ #sourceControl;
60
+ constructor(controller, storage, transitionService, sourceControl) {
61
+ this.#controller = controller;
62
+ this.#storage = storage;
63
+ this.#transitionService = transitionService;
64
+ this.#sourceControl = sourceControl;
65
+ }
66
+ async prepare(request) {
67
+ const storage = this.#storage;
68
+ if (!this.#sourceControl) throw new Error("Factory source control storage is unavailable");
69
+ const sourceSession = await resolveSourceSession(this.#sourceControl, request);
70
+ const requestContext = request.requestContext ?? new RequestContext();
71
+ if (!request.requestContext) {
72
+ requestContext.set("user", { workosId: request.userId, organizationId: request.orgId });
73
+ }
74
+ const session = await this.#controller.createSession({
75
+ id: sourceSession.sessionId,
76
+ ownerId: request.userId,
77
+ resourceId: sourceSession.sessionId,
78
+ threadId: sourceSession.sessionId,
79
+ requestContext
80
+ });
81
+ const threadId = await configureThread(session, request);
82
+ const kickoffMessage = await resolveKickoffMessage(session, request.invocation);
83
+ const prepared = await storage.prepareRunStart({
84
+ orgId: request.orgId,
85
+ userId: request.userId,
86
+ factoryProjectId: request.factoryProjectId,
87
+ workItem: { id: request.workItem.id, input: request.workItem.input },
88
+ role: request.workItem.role,
89
+ session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },
90
+ resourceId: sourceSession.sessionId,
91
+ kickoffKey: request.kickoffKey,
92
+ kickoffMessage
93
+ });
94
+ await session.thread.setSetting({ key: "factoryWorkItemId", value: prepared.item.id });
95
+ let revision = prepared.item.revision;
96
+ if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {
97
+ if (!this.#transitionService) throw new Error("Factory transition service is unavailable.");
98
+ const transition = await this.#transitionService.transition({
99
+ orgId: request.orgId,
100
+ factoryProjectId: request.factoryProjectId,
101
+ workItemId: prepared.item.id,
102
+ board: prepared.item.externalSource?.type === "pull-request" ? "review" : "work",
103
+ stage: request.destinationStage,
104
+ expectedRevision: prepared.item.revision,
105
+ actor: { type: "human", id: request.userId },
106
+ ingress: { type: "human", identity: `start:${request.kickoffKey}:transition` },
107
+ cause: "run_start"
108
+ });
109
+ if (transition.status === "rejected") {
110
+ await storage.markPendingStart(prepared.binding.id, "failed", transition.reason);
111
+ throw new FactoryStartTransitionError(transition);
112
+ }
113
+ revision = transition.revision;
114
+ }
115
+ if (kickoffMessage === null) {
116
+ await storage.markPendingStart(prepared.binding.id, "sent");
117
+ prepared.pendingStart.status = "sent";
118
+ }
119
+ return {
120
+ workItemId: prepared.item.id,
121
+ bindingId: prepared.binding.id,
122
+ threadId,
123
+ resourceId: sourceSession.sessionId,
124
+ sessionId: sourceSession.sessionId,
125
+ branch: sourceSession.branch,
126
+ revision,
127
+ kickoffStatus: prepared.pendingStart.status,
128
+ replayed: prepared.replayed
129
+ };
130
+ }
131
+ };
132
+ export {
133
+ FactoryStartCoordinator,
134
+ FactoryStartTransitionError
135
+ };
136
+ //# sourceMappingURL=start-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rules/start-coordinator.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { formatSkillActivation } from '@mastra/core/workspace';\n\nimport type { SourceControlSession, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryRuleStage, FactoryTransitionResult } from './types.js';\n\nexport interface FactoryStartRequest {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n sessionId: string;\n threadTitle: string;\n threadTags?: Record<string, string>;\n kickoffKey: string;\n invocation?: { type: 'prompt'; prompt: string } | { type: 'skill'; skillName: string; arguments: string };\n destinationStage: FactoryRuleStage;\n workItem: {\n id?: string;\n role: string;\n input: CreateWorkItemInput;\n };\n requestContext?: RequestContext;\n}\n\nexport class FactoryStartTransitionError extends Error {\n readonly result: Extract<FactoryTransitionResult, { status: 'rejected' }>;\n\n constructor(result: Extract<FactoryTransitionResult, { status: 'rejected' }>) {\n super(result.reason);\n this.name = 'FactoryStartTransitionError';\n this.result = result;\n }\n}\n\nexport interface FactoryStartPreparedResult {\n workItemId: string;\n bindingId: string;\n threadId: string;\n resourceId: string;\n sessionId: string;\n branch: string;\n revision: number;\n kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';\n replayed: boolean;\n}\n\ntype FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nfunction escapeSkillBoundary(value: string): string {\n return value.replaceAll('</skill>', '&lt;/skill&gt;');\n}\n\nasync function resolveKickoffMessage(\n session: FactorySession,\n invocation: FactoryStartRequest['invocation'],\n): Promise<string | null> {\n if (!invocation) return null;\n if (invocation.type === 'prompt') return invocation.prompt;\n\n const skills = session.getWorkspace().skills;\n await skills?.maybeRefresh();\n const skill = await skills?.get(invocation.skillName);\n if (!skill || skill['user-invocable'] === false) {\n throw new Error(`Skill not found: ${invocation.skillName}.`);\n }\n const args = invocation.arguments.trim();\n const content = `${formatSkillActivation(skill)}${args ? `\\n\\nARGUMENTS: ${args}` : ''}`.trim();\n return `<skill name=\"${skill.name}\">\\n${escapeSkillBoundary(content)}\\n</skill>`;\n}\n\nasync function resolveSourceSession(\n storage: SourceControlStorageHandle,\n request: FactoryStartRequest,\n): Promise<SourceControlSession> {\n const session = await storage.sessions.getBySessionId(request.sessionId);\n if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n throw new Error('Factory session not found');\n }\n const projectRepository = await storage.projectRepositories.get({\n orgId: request.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) throw new Error('Factory session repository not found');\n const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });\n if (!connection || connection.factoryProjectId !== request.factoryProjectId) {\n throw new Error('Factory session does not belong to this project');\n }\n return session;\n}\n\nasync function configureThread(session: FactorySession, request: FactoryStartRequest): Promise<string> {\n const threadId = session.thread.requireId();\n await session.thread.rename({ title: request.threadTitle });\n const settings = { ...(request.threadTags ?? {}), factorySessionId: request.sessionId };\n await Promise.all(Object.entries(settings).map(([key, value]) => session.thread.setSetting({ key, value })));\n return threadId;\n}\n\nexport class FactoryStartCoordinator {\n readonly #controller: FactoryController;\n readonly #storage: WorkItemsStorage;\n readonly #transitionService?: Pick<FactoryTransitionService, 'transition'>;\n readonly #sourceControl?: SourceControlStorageHandle;\n\n constructor(\n controller: FactoryController,\n storage: WorkItemsStorage,\n transitionService?: Pick<FactoryTransitionService, 'transition'>,\n sourceControl?: SourceControlStorageHandle,\n ) {\n this.#controller = controller;\n this.#storage = storage;\n this.#transitionService = transitionService;\n this.#sourceControl = sourceControl;\n }\n\n async prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult> {\n const storage = this.#storage;\n if (!this.#sourceControl) throw new Error('Factory source control storage is unavailable');\n const sourceSession = await resolveSourceSession(this.#sourceControl, request);\n const requestContext = request.requestContext ?? new RequestContext();\n if (!request.requestContext) {\n requestContext.set('user', { workosId: request.userId, organizationId: request.orgId });\n }\n const session = await this.#controller.createSession({\n id: sourceSession.sessionId,\n ownerId: request.userId,\n resourceId: sourceSession.sessionId,\n threadId: sourceSession.sessionId,\n requestContext,\n });\n const threadId = await configureThread(session, request);\n const kickoffMessage = await resolveKickoffMessage(session, request.invocation);\n const prepared = await storage.prepareRunStart({\n orgId: request.orgId,\n userId: request.userId,\n factoryProjectId: request.factoryProjectId,\n workItem: { id: request.workItem.id, input: request.workItem.input },\n role: request.workItem.role,\n session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },\n resourceId: sourceSession.sessionId,\n kickoffKey: request.kickoffKey,\n kickoffMessage,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n if (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== request.destinationStage) {\n if (!this.#transitionService) throw new Error('Factory transition service is unavailable.');\n const transition = await this.#transitionService.transition({\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n workItemId: prepared.item.id,\n board: prepared.item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage: request.destinationStage,\n expectedRevision: prepared.item.revision,\n actor: { type: 'human', id: request.userId },\n ingress: { type: 'human', identity: `start:${request.kickoffKey}:transition` },\n cause: 'run_start',\n });\n if (transition.status === 'rejected') {\n await storage.markPendingStart(prepared.binding.id, 'failed', transition.reason);\n throw new FactoryStartTransitionError(transition);\n }\n revision = transition.revision;\n }\n\n if (kickoffMessage === null) {\n await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\n }\n\n return {\n workItemId: prepared.item.id,\n bindingId: prepared.binding.id,\n threadId,\n resourceId: sourceSession.sessionId,\n sessionId: sourceSession.sessionId,\n branch: sourceSession.branch,\n revision,\n kickoffStatus: prepared.pendingStart.status,\n replayed: prepared.replayed,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ import type { RequestContext } from '@mastra/core/request-context';
2
+ import type { IntegrationTools } from '../integrations/base.js';
3
+ import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
4
+ import type { FactoryTransitionService } from './transition-service.js';
5
+ export declare function createFactoryTransitionTools(options: {
6
+ requestContext: RequestContext;
7
+ storage: WorkItemsStorage;
8
+ transitionService: Pick<FactoryTransitionService, 'transition'>;
9
+ }): Promise<IntegrationTools>;
10
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/rules/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAgBxE,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC1D,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,iBAAiB,EAAE,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;CACjE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuC5B"}
@@ -0,0 +1,93 @@
1
+ // src/rules/tools.ts
2
+ import { createTool } from "@mastra/core/tools";
3
+ import { z } from "zod";
4
+
5
+ // src/auth.ts
6
+ import { MastraAuthWorkos } from "@mastra/auth-workos";
7
+ import {
8
+ registerApiRoute,
9
+ isAuthHttpHandler,
10
+ isCredentialsProvider,
11
+ isOrganizationsProvider,
12
+ isSessionProvider,
13
+ isSSOProvider
14
+ } from "@mastra/core/server";
15
+ function getFactoryAuthOrgId(user) {
16
+ return user?.organizationId;
17
+ }
18
+
19
+ // src/rules/binding-context.ts
20
+ function getFactorySessionCoordinates(requestContext) {
21
+ if (!requestContext || typeof requestContext.get !== "function") return null;
22
+ const context = requestContext.get("controller");
23
+ const factoryProjectId = context?.getState().factoryProjectId;
24
+ if (!context?.threadId || !context.resourceId || !factoryProjectId) return null;
25
+ return {
26
+ factoryProjectId,
27
+ threadId: context.threadId,
28
+ resourceId: context.resourceId,
29
+ sessionId: context.resourceId
30
+ };
31
+ }
32
+ function getFactorySessionAddress(requestContext) {
33
+ const coordinates = getFactorySessionCoordinates(requestContext);
34
+ if (!coordinates || !requestContext || typeof requestContext.get !== "function") return null;
35
+ const user = requestContext.get("user");
36
+ const orgId = getFactoryAuthOrgId(user);
37
+ if (!orgId) return null;
38
+ return { orgId, ...coordinates };
39
+ }
40
+
41
+ // src/rules/types.ts
42
+ var FACTORY_RULE_STAGES = ["intake", "triage", "planning", "execute", "review", "done", "canceled"];
43
+
44
+ // src/rules/tools.ts
45
+ var transitionInputSchema = z.object({
46
+ stage: z.enum(FACTORY_RULE_STAGES),
47
+ expectedRevision: z.number().int().positive(),
48
+ rationale: z.string().trim().min(1).max(1e3)
49
+ }).strict();
50
+ function boardForSource(type) {
51
+ return type === "pull-request" ? "review" : "work";
52
+ }
53
+ async function createFactoryTransitionTools(options) {
54
+ const address = getFactorySessionAddress(options.requestContext);
55
+ if (!address) return {};
56
+ const availableBinding = await options.storage.findActiveRunBinding(address);
57
+ if (!availableBinding) return {};
58
+ return {
59
+ factory_transition_work_item: createTool({
60
+ id: "factory_transition_work_item",
61
+ description: "Request a governed stage transition for the Factory work item exactly bound to this thread. Use the current revision from the factory-phase signal and explain why the transition is appropriate.",
62
+ inputSchema: transitionInputSchema,
63
+ execute: async ({ stage, expectedRevision, rationale }, execution) => {
64
+ const currentAddress = getFactorySessionAddress(execution.requestContext);
65
+ const toolCallId = execution.agent?.toolCallId;
66
+ if (!currentAddress || !toolCallId) {
67
+ throw new Error("Factory transitions require an authenticated bound agent tool call.");
68
+ }
69
+ const binding = await options.storage.findActiveRunBinding(currentAddress);
70
+ if (!binding || binding.id !== availableBinding.id) {
71
+ throw new Error("Factory agent binding is unavailable, revoked, or no longer matches this session.");
72
+ }
73
+ const item = await options.storage.get({ orgId: binding.orgId, id: binding.workItemId });
74
+ if (!item) throw new Error("Bound Factory work item not found.");
75
+ return options.transitionService.transition({
76
+ orgId: binding.orgId,
77
+ factoryProjectId: binding.factoryProjectId,
78
+ workItemId: binding.workItemId,
79
+ board: boardForSource(item.externalSource?.type),
80
+ stage,
81
+ expectedRevision,
82
+ actor: { type: "agent", bindingId: binding.id, role: binding.role },
83
+ ingress: { type: "agent", identity: `${binding.id}:${toolCallId}` },
84
+ cause: rationale
85
+ });
86
+ }
87
+ })
88
+ };
89
+ }
90
+ export {
91
+ createFactoryTransitionTools
92
+ };
93
+ //# sourceMappingURL=tools.js.map