@mastra/factory 0.26.0-alpha.2 → 0.26.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 (154) hide show
  1. package/README.md +74 -7
  2. package/dist/boards/define-board.d.ts +19 -0
  3. package/dist/boards/define-board.d.ts.map +1 -1
  4. package/dist/boards/define-board.js +41 -9
  5. package/dist/boards/define-board.js.map +1 -1
  6. package/dist/boards/project-boards.d.ts +78 -0
  7. package/dist/boards/project-boards.d.ts.map +1 -0
  8. package/dist/boards/project-boards.js +194 -0
  9. package/dist/boards/project-boards.js.map +1 -0
  10. package/dist/boards/review.js +2 -2
  11. package/dist/boards/review.js.map +1 -1
  12. package/dist/boards/stored-board.d.ts +101 -0
  13. package/dist/boards/stored-board.d.ts.map +1 -0
  14. package/dist/boards/stored-board.js +139 -0
  15. package/dist/boards/stored-board.js.map +1 -0
  16. package/dist/boards/work.d.ts.map +1 -1
  17. package/dist/boards/work.js +6 -3
  18. package/dist/boards/work.js.map +1 -1
  19. package/dist/capabilities/intake.d.ts +6 -0
  20. package/dist/capabilities/intake.d.ts.map +1 -1
  21. package/dist/factory.d.ts +15 -2
  22. package/dist/factory.d.ts.map +1 -1
  23. package/dist/factory.js +49 -10
  24. package/dist/factory.js.map +1 -1
  25. package/dist/integrations/jira/binding-scope.d.ts +30 -0
  26. package/dist/integrations/jira/binding-scope.d.ts.map +1 -0
  27. package/dist/integrations/jira/binding-scope.js +50 -0
  28. package/dist/integrations/jira/binding-scope.js.map +1 -0
  29. package/dist/integrations/jira/claim.d.ts +12 -0
  30. package/dist/integrations/jira/claim.d.ts.map +1 -1
  31. package/dist/integrations/jira/claim.js +24 -1
  32. package/dist/integrations/jira/claim.js.map +1 -1
  33. package/dist/integrations/jira/intake-jql.d.ts +32 -0
  34. package/dist/integrations/jira/intake-jql.d.ts.map +1 -0
  35. package/dist/integrations/jira/intake-jql.js +57 -0
  36. package/dist/integrations/jira/intake-jql.js.map +1 -0
  37. package/dist/integrations/jira/integration.d.ts +4 -3
  38. package/dist/integrations/jira/integration.d.ts.map +1 -1
  39. package/dist/integrations/jira/integration.js +14 -21
  40. package/dist/integrations/jira/integration.js.map +1 -1
  41. package/dist/integrations/jira/routes.d.ts +3 -0
  42. package/dist/integrations/jira/routes.d.ts.map +1 -1
  43. package/dist/integrations/jira/routes.js +16 -29
  44. package/dist/integrations/jira/routes.js.map +1 -1
  45. package/dist/integrations/jira/rules.d.ts.map +1 -1
  46. package/dist/integrations/jira/rules.js +8 -13
  47. package/dist/integrations/jira/rules.js.map +1 -1
  48. package/dist/integrations/platform/jira/integration.d.ts +7 -1
  49. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  50. package/dist/integrations/platform/jira/integration.js +26 -24
  51. package/dist/integrations/platform/jira/integration.js.map +1 -1
  52. package/dist/integrations/platform/jira/routes.d.ts +3 -0
  53. package/dist/integrations/platform/jira/routes.d.ts.map +1 -1
  54. package/dist/integrations/platform/jira/routes.js +17 -28
  55. package/dist/integrations/platform/jira/routes.js.map +1 -1
  56. package/dist/routes/attention-providers.d.ts +2 -1
  57. package/dist/routes/attention-providers.d.ts.map +1 -1
  58. package/dist/routes/attention-providers.js +3 -2
  59. package/dist/routes/attention-providers.js.map +1 -1
  60. package/dist/routes/attention.d.ts +2 -1
  61. package/dist/routes/attention.d.ts.map +1 -1
  62. package/dist/routes/attention.js.map +1 -1
  63. package/dist/routes/boards.d.ts +30 -0
  64. package/dist/routes/boards.d.ts.map +1 -0
  65. package/dist/routes/boards.js +272 -0
  66. package/dist/routes/boards.js.map +1 -0
  67. package/dist/routes/contracts.d.ts +249 -0
  68. package/dist/routes/contracts.d.ts.map +1 -1
  69. package/dist/routes/contracts.js +107 -35
  70. package/dist/routes/contracts.js.map +1 -1
  71. package/dist/routes/intake.d.ts +15 -10
  72. package/dist/routes/intake.d.ts.map +1 -1
  73. package/dist/routes/intake.js +109 -22
  74. package/dist/routes/intake.js.map +1 -1
  75. package/dist/routes/projects.d.ts +3 -0
  76. package/dist/routes/projects.d.ts.map +1 -1
  77. package/dist/routes/projects.js +10 -1
  78. package/dist/routes/projects.js.map +1 -1
  79. package/dist/routes/surface.d.ts +10 -3
  80. package/dist/routes/surface.d.ts.map +1 -1
  81. package/dist/routes/surface.js +12 -1
  82. package/dist/routes/surface.js.map +1 -1
  83. package/dist/routes/tenant-credentials.d.ts +9 -0
  84. package/dist/routes/tenant-credentials.d.ts.map +1 -1
  85. package/dist/routes/tenant-credentials.js +24 -2
  86. package/dist/routes/tenant-credentials.js.map +1 -1
  87. package/dist/routes/work-items.d.ts +3 -1
  88. package/dist/routes/work-items.d.ts.map +1 -1
  89. package/dist/routes/work-items.js +64 -42
  90. package/dist/routes/work-items.js.map +1 -1
  91. package/dist/rules/dispatcher.d.ts +7 -1
  92. package/dist/rules/dispatcher.d.ts.map +1 -1
  93. package/dist/rules/dispatcher.js +70 -34
  94. package/dist/rules/dispatcher.js.map +1 -1
  95. package/dist/rules/processor.d.ts +3 -1
  96. package/dist/rules/processor.d.ts.map +1 -1
  97. package/dist/rules/processor.js +11 -6
  98. package/dist/rules/processor.js.map +1 -1
  99. package/dist/rules/tools.d.ts +2 -1
  100. package/dist/rules/tools.d.ts.map +1 -1
  101. package/dist/rules/tools.js +2 -1
  102. package/dist/rules/tools.js.map +1 -1
  103. package/dist/rules/transition-service.d.ts +3 -1
  104. package/dist/rules/transition-service.d.ts.map +1 -1
  105. package/dist/rules/transition-service.js +12 -12
  106. package/dist/rules/transition-service.js.map +1 -1
  107. package/dist/storage/domains/audit/actions.d.ts +1 -0
  108. package/dist/storage/domains/audit/actions.d.ts.map +1 -1
  109. package/dist/storage/domains/audit/actions.js +6 -1
  110. package/dist/storage/domains/audit/actions.js.map +1 -1
  111. package/dist/storage/domains/boards/base.d.ts +110 -0
  112. package/dist/storage/domains/boards/base.d.ts.map +1 -0
  113. package/dist/storage/domains/boards/base.js +292 -0
  114. package/dist/storage/domains/boards/base.js.map +1 -0
  115. package/dist/storage/domains/boards/schemas.d.ts +11 -0
  116. package/dist/storage/domains/boards/schemas.d.ts.map +1 -0
  117. package/dist/storage/domains/boards/schemas.js +106 -0
  118. package/dist/storage/domains/boards/schemas.js.map +1 -0
  119. package/dist/storage/domains/credentials/base.d.ts +4 -1
  120. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  121. package/dist/storage/domains/credentials/base.js +5 -2
  122. package/dist/storage/domains/credentials/base.js.map +1 -1
  123. package/dist/storage/domains/intake/base.d.ts +86 -5
  124. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  125. package/dist/storage/domains/intake/base.js +298 -59
  126. package/dist/storage/domains/intake/base.js.map +1 -1
  127. package/dist/storage/domains/projects/base.d.ts +2 -1
  128. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  129. package/dist/storage/domains/projects/base.js +15 -10
  130. package/dist/storage/domains/projects/base.js.map +1 -1
  131. package/dist/storage/domains/work-items/base.d.ts +14 -0
  132. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  133. package/dist/storage/domains/work-items/base.js +39 -11
  134. package/dist/storage/domains/work-items/base.js.map +1 -1
  135. package/dist/tools/read-tools.d.ts +3 -1
  136. package/dist/tools/read-tools.d.ts.map +1 -1
  137. package/dist/tools/read-tools.js +3 -2
  138. package/dist/tools/read-tools.js.map +1 -1
  139. package/dist/tools/session-tools.d.ts +3 -1
  140. package/dist/tools/session-tools.d.ts.map +1 -1
  141. package/dist/tools/session-tools.js +2 -1
  142. package/dist/tools/session-tools.js.map +1 -1
  143. package/dist/tools/sessions-state-processor.d.ts +3 -1
  144. package/dist/tools/sessions-state-processor.d.ts.map +1 -1
  145. package/dist/tools/sessions-state-processor.js +2 -1
  146. package/dist/tools/sessions-state-processor.js.map +1 -1
  147. package/dist/tools/work-item-tools.d.ts +3 -1
  148. package/dist/tools/work-item-tools.d.ts.map +1 -1
  149. package/dist/tools/work-item-tools.js +3 -2
  150. package/dist/tools/work-item-tools.js.map +1 -1
  151. package/dist/workspace.js +1 -1
  152. package/factory-skills/factory-plan/SKILL.md +1 -1
  153. package/factory-skills/factory-triage/SKILL.md +18 -7
  154. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"projects.js","names":[],"sources":["../../src/routes/projects.ts"],"sourcesContent":["import { MastraError } from '@mastra/core/error';\nimport type { StandardSchemaWithJSON } from '@mastra/core/schema';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport { describeFactorySandbox, normalizeFactorySandboxSettings } from '@mastra/core/workspace';\nimport type { FactorySandbox, FactorySandboxDescription } from '@mastra/core/workspace';\nimport type { Context } from 'hono';\n\nimport type { EnvironmentBuildRunner } from '../environment/build-runner.js';\nimport { ensureSchedule, invalidCronMessage, readSchedule, scheduleIdFor } from '../environment/build-schedule.js';\nimport { redactCredentials } from '../environment/build.js';\nimport type { SessionRetirementCoordinator } from '../sandbox/session-retirement.js';\nimport type {\n FactoryProject,\n FactoryProjectsStorage,\n UpdateFactoryProjectInput,\n} from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport { ACTIVE_RUN_BINDING_STAGES } from '../storage/domains/work-items/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { FACTORY_ROUTE_CONTRACTS } from './contracts.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_NAME_LENGTH = 200;\nconst MAX_REPOSITORY_COMMAND_LENGTH = 2_000;\nconst MAX_BRANCH_LENGTH = 255;\nconst MAX_SANDBOX_PROVIDER_LENGTH = 100;\nconst MAX_SANDBOX_WORKDIR_LENGTH = 1_000;\nconst CONTROL_CHAR_RE = /[\\0-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\nfunction loose(context: unknown): Context {\n return context as Context;\n}\n\nasync function readJson(context: Context): Promise<unknown | undefined> {\n try {\n return await context.req.json();\n } catch {\n return undefined;\n }\n}\n\nfunction parseConnectionInput(value: unknown): { integrationId: string; installationId: string } | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.integrationId !== 'string' || !input.integrationId.trim()) return null;\n if (typeof input.installationId !== 'string' || !UUID_RE.test(input.installationId)) return null;\n return { integrationId: input.integrationId.trim(), installationId: input.installationId };\n}\n\nfunction parseOptionalString(\n value: unknown,\n { maxLength, nullable = false }: { maxLength: number; nullable?: boolean },\n): string | null | undefined | false {\n if (value === undefined) return undefined;\n if (value === null) return nullable ? null : false;\n if (typeof value !== 'string') return false;\n const normalized = value.trim();\n if (!normalized) return nullable ? null : false;\n if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;\n return normalized;\n}\n\nfunction parseRepositoryLinkInput(value: unknown): {\n repositoryId?: string;\n repository?: { externalId: string; slug: string };\n branch: string | null;\n sandboxProvider: string;\n sandboxWorkdir: string;\n setupCommand: string | null;\n teardownCommand: string | null;\n} | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const repositoryId =\n typeof input.repositoryId === 'string' && UUID_RE.test(input.repositoryId) ? input.repositoryId : undefined;\n const repositoryInput = input.repository;\n let repository: { externalId: string; slug: string } | undefined;\n if (repositoryInput && typeof repositoryInput === 'object') {\n const candidate = repositoryInput as Record<string, unknown>;\n const externalId = parseOptionalString(candidate.externalId, { maxLength: MAX_NAME_LENGTH });\n const slug = parseOptionalString(candidate.slug, { maxLength: MAX_NAME_LENGTH });\n if (typeof externalId !== 'string' || typeof slug !== 'string') return null;\n repository = { externalId, slug };\n }\n if (!repositoryId && !repository) return null;\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n typeof sandboxProvider !== 'string' ||\n typeof sandboxWorkdir !== 'string' ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n return {\n ...(repositoryId ? { repositoryId } : {}),\n ...(repository ? { repository } : {}),\n branch: branch ?? null,\n sandboxProvider,\n sandboxWorkdir,\n setupCommand: setupCommand ?? null,\n teardownCommand: teardownCommand ?? null,\n };\n}\n\nfunction parseRepositoryUpdateInput(value: unknown): UpdateProjectRepositoryInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateProjectRepositoryInput = {};\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n sandboxProvider === false ||\n sandboxProvider === null ||\n sandboxWorkdir === false ||\n sandboxWorkdir === null ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n if (branch !== undefined) patch.branch = branch;\n if (sandboxProvider !== undefined) patch.sandboxProvider = sandboxProvider;\n if (sandboxWorkdir !== undefined) patch.sandboxWorkdir = sandboxWorkdir;\n if (setupCommand !== undefined) patch.setupCommand = setupCommand;\n if (teardownCommand !== undefined) patch.teardownCommand = teardownCommand;\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\ninterface ModelApplySession {\n thread: {\n getById: (args: { threadId: string }) => Promise<{ metadata?: Record<string, unknown> | null } | null>;\n setSettingOn: (args: { threadId: string; key: string; value: unknown }) => Promise<unknown> | unknown;\n };\n}\n\ninterface ModelApplyController {\n getSession: (sessionId: string) => Promise<ModelApplySession | undefined>;\n}\n\nexport interface ProjectRoutesDeps extends RouteDependencies {\n /** Factory projects domain backing the CRUD surface. */\n projects: FactoryProjectsStorage;\n /** Source-control domain the connection/repository routes fan out over. */\n sourceControl: SourceControlStorage;\n /** Integration ids allowed as source-control connection targets. */\n versionControlIntegrationIds?: string[];\n /** Validate and persist a repository selected from a provider-backed listing. */\n resolveRepository?: (input: {\n integrationId: string;\n orgId: string;\n userId: string;\n installationId: string;\n externalId: string;\n slug: string;\n }) => Promise<SourceControlRepository | null>;\n /**\n * Fire-and-forget hook invoked after a repository is linked to a project —\n * kicks the initial base-checkpoint build. Must never throw.\n */\n onProjectRepositoryLinked?: (args: { orgId: string; projectRepository: ProjectRepository }) => void;\n /** Shared lifecycle for retiring sessions before their owning records are deleted. */\n sessionRetirement?: SessionRetirementCoordinator;\n /** Work-items domain used by session retirement and running-thread model updates. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences' | 'listRunBindings' | 'get'>;\n /** Controller used to reach the thread store behind each active binding. */\n controller?: ModelApplyController;\n /** The factory's normalized sandbox; describes and validates the environment settings. */\n sandbox?: FactorySandbox;\n /** Environment builds, present only when the sandbox has a `builds` capability. */\n environmentBuilds?: EnvironmentBuildRunner;\n}\n\n/** Provider output is shown to the user; strip any clone credential from it first. */\nfunction redactBuild<T extends { error?: string; logs?: string[] }>(build: T): T {\n return {\n ...build,\n ...(build.error !== undefined ? { error: redactCredentials(build.error) } : {}),\n ...(build.logs ? { logs: build.logs.map(redactCredentials) } : {}),\n };\n}\n\n/** What the environment reports when the factory has no sandbox configured. */\nconst NO_SANDBOX: FactorySandboxDescription = {\n provider: 'none',\n settingsSchema: { type: 'object', properties: {}, additionalProperties: false },\n capabilities: { template: false, builds: { available: false, history: false } },\n};\n\nexport class ProjectRoutes extends Route<ProjectRoutesDeps> {\n readonly #versionControlIntegrationIds: Set<string>;\n #sandboxDescription: FactorySandboxDescription | undefined;\n #settingsSchema: StandardSchemaWithJSON | undefined;\n\n constructor(deps: ProjectRoutesDeps) {\n super(deps);\n this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);\n }\n\n #describeSandbox(): FactorySandboxDescription {\n if (!this.deps.sandbox) return NO_SANDBOX;\n this.#sandboxDescription ??= describeFactorySandbox(this.deps.sandbox);\n return this.#sandboxDescription;\n }\n\n /**\n * Merge a settings patch onto the stored document (null removes a key) and\n * validate the result through the sandbox's schema. Returns the issues when\n * the merged document is rejected.\n *\n * A stored key the current schema does not declare, or whose stored value it\n * now rejects (a tightened range or choice list), is left over from another\n * provider or an older schema. When the merged document fails validation, the\n * merge retries without those keys so one stale value cannot lock every later\n * update; keys the patch itself sets are never pruned.\n */\n async #mergeSettings(\n stored: Record<string, unknown>,\n patch: Record<string, unknown | null>,\n ): Promise<\n { merged: Record<string, unknown> | null } | { issues: ReadonlyArray<{ message: string; path?: unknown }> }\n > {\n const merged: Record<string, unknown> = { ...stored };\n for (const [key, value] of Object.entries(patch)) {\n if (value === null) delete merged[key];\n else merged[key] = value;\n }\n this.#settingsSchema ??= normalizeFactorySandboxSettings(this.deps.sandbox!);\n const result = await this.#settingsSchema['~standard'].validate(merged);\n if (!result.issues) return { merged: Object.keys(merged).length > 0 ? merged : null };\n\n const declared = this.#describeSandbox().settingsSchema.properties ?? {};\n const rejected = new Set(\n result.issues.map(issue => {\n const segment = issue.path?.[0];\n return typeof segment === 'object' && segment !== null ? segment.key : segment;\n }),\n );\n const pruned = Object.fromEntries(\n Object.entries(merged).filter(([key]) => key in patch || (Object.hasOwn(declared, key) && !rejected.has(key))),\n );\n if (Object.keys(pruned).length === Object.keys(merged).length) return { issues: result.issues };\n const retried = await this.#settingsSchema['~standard'].validate(pruned);\n if (retried.issues) return { issues: result.issues };\n return { merged: Object.keys(pruned).length > 0 ? pruned : null };\n }\n\n async #projects(): Promise<FactoryProjectsStorage> {\n await this.deps.projects.ensureReady();\n return this.deps.projects;\n }\n\n async #sourceControl(): Promise<SourceControlStorage> {\n await this.deps.sourceControl.ensureReady();\n return this.deps.sourceControl;\n }\n\n async #handles(): Promise<SourceControlStorageHandle[]> {\n const storage = await this.#sourceControl();\n return [...this.#versionControlIntegrationIds].map(integrationId => storage.forIntegration(integrationId));\n }\n\n async #project(orgId: string, id: string) {\n return (await this.#projects()).get({ orgId, id });\n }\n\n async #findConnection({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const connection = await handle.connections.get({ orgId, id });\n if (connection?.factoryProjectId === projectId) return { handle, connection };\n }\n return null;\n }\n\n async #findProjectRepository({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const projectRepository = await handle.projectRepositories.get({ orgId, id });\n if (!projectRepository) continue;\n const connection = await handle.connections.get({ orgId, id: projectRepository.connectionId });\n if (connection?.factoryProjectId === projectId) return { handle, connection, projectRepository };\n }\n return null;\n }\n\n async #repositoryPayload(handle: SourceControlStorageHandle, orgId: string, projectRepository: ProjectRepository) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n return { ...projectRepository, repository };\n }\n\n /** Every link of the project across every handle, with the handle that owns it, in position order. */\n async #environmentLinks(orgId: string, projectId: string) {\n const links: Array<{ handle: SourceControlStorageHandle; projectRepository: ProjectRepository }> = [];\n for (const handle of await this.#handles()) {\n for (const projectRepository of await handle.projectRepositories.listByProject({\n orgId,\n factoryProjectId: projectId,\n })) {\n links.push({ handle, projectRepository });\n }\n }\n return links.sort(\n (a, b) =>\n a.projectRepository.position - b.projectRepository.position ||\n a.projectRepository.createdAt.getTime() - b.projectRepository.createdAt.getTime(),\n );\n }\n\n async #environmentPayload(orgId: string, project: FactoryProject) {\n const repositories = [];\n for (const { handle, projectRepository } of await this.#environmentLinks(orgId, project.id)) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n repositories.push({\n projectRepositoryId: projectRepository.id,\n connectionId: projectRepository.connectionId,\n repositoryId: projectRepository.repositoryId,\n slug: repository?.slug ?? null,\n defaultBranch: repository?.defaultBranch ?? null,\n position: projectRepository.position,\n inEnvironment: projectRepository.inEnvironment,\n setupCommand: projectRepository.setupCommand,\n teardownCommand: projectRepository.teardownCommand,\n lastBuildStatus: projectRepository.lastBuildStatus,\n lastBuildError: projectRepository.lastBuildError,\n lastBuiltAt: projectRepository.lastBuiltAt,\n });\n }\n return {\n environment: {\n sandbox: this.#describeSandbox(),\n settings: project.sandboxSettings ?? {},\n sandboxWorkingDirectory: project.sandboxWorkingDirectory,\n workspaceSetupCommand: project.workspaceSetupCommand,\n activeTemplateId: project.activeTemplateId,\n activeTemplateHeads: project.activeTemplateHeads,\n repositories,\n ...(await this.#buildPayload(project)),\n },\n };\n }\n\n /**\n * The build keys of the environment payload, present only when the\n * sandbox can build. Reads the project row and the schedule row; never\n * the provider (live status is the builds routes' job).\n */\n async #buildPayload(project: FactoryProject) {\n const builds = this.deps.environmentBuilds;\n if (!builds) return {};\n const schedules = builds.scheduleAvailable ? builds.schedules() : undefined;\n const schedule = schedules\n ? await readSchedule(schedules, project.id)\n : { enabled: false, cron: null, timezone: null };\n return {\n buildTriggers: {\n schedule: { ...schedule, scheduleAvailable: builds.scheduleAvailable },\n push: { enabled: project.buildOnPushEnabled, debounceMinutes: project.buildPushDebounceMinutes },\n },\n build: project.lastBuildId\n ? { buildId: project.lastBuildId, attemptedAt: project.lastBuildAttemptedAt?.toISOString() ?? null }\n : null,\n };\n }\n\n /** The builds capability behind the routes, or the 404 to answer with. */\n #builds(context: Context) {\n const builds = this.deps.sandbox?.builds;\n const runner = this.deps.environmentBuilds;\n if (!builds || !runner) return { response: context.json({ error: 'no_builds' }, 404) };\n return { builds, runner };\n }\n\n #buildRoutes(): ApiRoute[] {\n return [\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuild.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuild.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n const { heads: _heads, ...outcome } = await capability.runner.start(project.id, 'manual');\n return context.json(outcome);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuilds.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuilds.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n if (!capability.builds.list) return context.json({ error: 'no_history' }, 404);\n const read = await capability.runner.readContext(project.id);\n if (!read) return context.json({ error: 'no_environment' }, 404);\n const builds = await capability.builds.list(read.ctx, read.settings);\n return context.json({ builds: builds.map(redactBuild) });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuildGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuildGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n // A composite provider id (E2B's `<templateId>:<buildId>`) travels\n // encoded; Hono hands the param back decoded.\n const buildId = context.req.param('buildId') ?? '';\n if (!buildId) return context.json({ error: 'Build not found' }, 404);\n const read = await capability.runner.readContext(project.id);\n if (!read) return context.json({ error: 'no_environment' }, 404);\n // Providers answer for any id on the host's account, so the route\n // only forwards ids that belong to this project: its last build, or\n // one the provider lists under the project's template.\n if (buildId !== project.lastBuildId) {\n const listed = capability.builds.list ? await capability.builds.list(read.ctx, read.settings) : [];\n if (!listed.some(build => build.buildId === buildId)) {\n return context.json({ error: 'Build not found' }, 404);\n }\n }\n return context.json({ build: redactBuild(await capability.builds.get(read.ctx, read.settings, buildId)) });\n },\n }),\n ];\n }\n\n /** Best effort: a deleted project's cron schedule must not keep firing. */\n async #dropSchedule(projectId: string): Promise<void> {\n const runner = this.deps.environmentBuilds;\n if (!runner?.scheduleAvailable) return;\n try {\n await runner.schedules()!.delete(scheduleIdFor(projectId));\n } catch (error) {\n if (error instanceof MastraError && error.id === 'SCHEDULES_NOT_FOUND') return;\n console.warn('[factory] could not delete the environment build schedule of a deleted project:', error);\n }\n }\n\n async #retireProjectRepositorySessions(\n handle: SourceControlStorageHandle,\n orgId: string,\n projectRepositoryId: string,\n ): Promise<boolean> {\n const sessions = await handle.sessions.listByProjectRepository({ projectRepositoryId });\n if (sessions.length === 0) return true;\n if (!this.deps.sessionRetirement) return false;\n await this.deps.sessionRetirement.retireProjectRepositorySessions({\n sourceControl: handle,\n ...(this.deps.workItems ? { workItems: this.deps.workItems } : {}),\n orgId,\n projectRepositoryId,\n });\n return true;\n }\n\n async #resolveTenant(context: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(context);\n const tenant = this.deps.auth.tenant(context);\n if (!tenant) return { response: context.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: context.json(\n { error: 'organization_required', message: 'Factory projects require an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectList.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectList.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectCreate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectCreate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsed = FACTORY_ROUTE_CONTRACTS.projectCreate.bodySchema.safeParse(await readJson(context));\n if (!parsed.success) return context.json({ error: 'invalid_project' }, 400);\n const project = await (\n await this.#projects()\n ).create({ orgId: tenant.orgId, userId: tenant.userId, input: parsed.data });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectGet.pathSchema.safeParse({ id: context.req.param('id') });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\n const project = await this.#project(tenant.orgId, id);\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectUpdate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectUpdate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectUpdate.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const parsedBody = FACTORY_ROUTE_CONTRACTS.projectUpdate.bodySchema.safeParse(await readJson(context));\n if (!parsedBody.success) return context.json({ error: 'invalid_project' }, 400);\n // Scope decides whose credentials every session bills against, so only\n // admins may switch a shared Factory onto owners' personal keys. The\n // thinking default sets the reasoning spend of every run that has not\n // picked its own, so it is admin-only too.\n const adminField =\n parsedBody.data.modelScope !== undefined\n ? 'model scope'\n : parsedBody.data.defaultThinkingLevel !== undefined\n ? 'thinking default'\n : undefined;\n if (\n adminField &&\n this.deps.auth.enabled() &&\n !(await this.deps.auth.isOrganizationAdmin(context, tenant.orgId))\n ) {\n return context.json(\n {\n error: 'forbidden',\n message: `Organization administrator access is required to change the ${adminField}.`,\n },\n 403,\n );\n }\n const project = await (\n await this.#projects()\n ).update({ orgId: tenant.orgId, id: parsedPath.data.id, input: parsedBody.data });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n if (!(await this.deps.auth.isOrganizationAdmin(context, tenant.orgId))) {\n return context.json(\n {\n error: 'forbidden',\n message: 'Organization administrator access is required to update running sessions.',\n },\n 403,\n );\n }\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, parsedPath.data.id);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const modelId = project.defaultModelId;\n if (!modelId) {\n return context.json(\n { error: 'default_model_not_set', message: 'Set a default model on the project first.' },\n 400,\n );\n }\n const { workItems, controller } = this.deps;\n if (!workItems || !controller) return context.json({ error: 'model_apply_unavailable' }, 503);\n\n const bindings = (await workItems.listRunBindings(tenant.orgId, project.id)).filter(\n binding => binding.status === 'active',\n );\n const seen = new Set<string>();\n const applied: string[] = [];\n const skipped: Array<{\n threadId: string;\n reason:\n | 'not-running'\n | 'work-item-missing'\n | 'stage-inactive'\n | 'thread-missing'\n | 'mode-unknown'\n | 'apply-failed';\n }> = [];\n\n for (const binding of bindings) {\n const key = `${binding.sessionId}:${binding.threadId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n\n const item = await workItems.get({ orgId: tenant.orgId, id: binding.workItemId });\n if (!item || item.factoryProjectId !== project.id) {\n skipped.push({ threadId: binding.threadId, reason: 'work-item-missing' });\n continue;\n }\n if (!ACTIVE_RUN_BINDING_STAGES.has(item.stages[0] ?? '')) {\n skipped.push({ threadId: binding.threadId, reason: 'stage-inactive' });\n continue;\n }\n const session = await controller.getSession(binding.sessionId);\n if (!session) {\n skipped.push({ threadId: binding.threadId, reason: 'not-running' });\n continue;\n }\n const thread = await session.thread.getById({ threadId: binding.threadId });\n if (!thread) {\n skipped.push({ threadId: binding.threadId, reason: 'thread-missing' });\n continue;\n }\n try {\n // This route targets inactive bound threads, so persist the controller-owned key through the session gateway.\n await session.thread.setSettingOn({\n threadId: binding.threadId,\n key: 'currentModelId',\n value: modelId,\n });\n applied.push(binding.threadId);\n } catch (error) {\n console.warn('[factory] apply-default-model failed for thread', binding.threadId, error);\n skipped.push({ threadId: binding.threadId, reason: 'apply-failed' });\n }\n }\n\n return context.json({ modelId, applied, skipped });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectDelete.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectDelete.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectDelete.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\n if (!(await this.#project(tenant.orgId, id))) return context.json({ error: 'Project not found' }, 404);\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({ orgId: tenant.orgId, factoryProjectId: id })) {\n for (const projectRepository of await handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId: connection.id,\n })) {\n if (!(await this.#retireProjectRepositorySessions(handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await handle.connections.delete({ orgId: tenant.orgId, id: connection.id });\n }\n }\n await (await this.#projects()).delete({ orgId: tenant.orgId, id });\n await this.#dropSchedule(id);\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const connections = [];\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n })) {\n const installation = await handle.installations.get({\n orgId: tenant.orgId,\n id: connection.installationId,\n });\n // Skip orphaned connections whose installation was pruned (e.g.\n // the user uninstalled the GitHub App). Otherwise\n // `projectRepositories.list` throws `requireConnection` and the\n // whole endpoint 500s, which hangs the web UI on the page\n // loader for every project. New code cascade-deletes these on\n // installation removal, but this defensive skip lets already-\n // orphaned rows in existing databases self-heal on read.\n if (!installation) continue;\n const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });\n connections.push({\n ...connection,\n installation,\n repositories: await Promise.all(links.map(link => this.#repositoryPayload(handle, tenant.orgId, link))),\n });\n }\n }\n return context.json({ connections });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const input = parseConnectionInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_source_control_connection' }, 400);\n if (!this.#versionControlIntegrationIds.has(input.integrationId))\n return context.json({ error: 'Source-control integration not found' }, 404);\n const handle = (await this.#sourceControl()).forIntegration(input.integrationId);\n if (!(await handle.installations.get({ orgId: tenant.orgId, id: input.installationId })))\n return context.json({ error: 'Source-control installation not found' }, 404);\n const connection = await handle.connections.create({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n installationId: input.installationId,\n createdByUserId: tenant.userId,\n });\n return context.json({ connection }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n for (const projectRepository of await found.handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId,\n })) {\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await found.handle.connections.delete({ orgId: tenant.orgId, id: connectionId });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId/repositories', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n const input = parseRepositoryLinkInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const repository = input.repositoryId\n ? await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId })\n : input.repository && this.deps.resolveRepository\n ? await this.deps.resolveRepository({\n integrationId: found.connection.integrationId,\n orgId: tenant.orgId,\n userId: tenant.userId,\n installationId: found.connection.installationId,\n ...input.repository,\n })\n : null;\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const { repositoryId: _repositoryId, repository: _repository, ...linkInput } = input;\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n repositoryId: repository.id,\n ...linkInput,\n });\n try {\n this.deps.onProjectRepositoryLinked?.({ orgId: tenant.orgId, projectRepository });\n } catch (error) {\n console.warn('[factory] onProjectRepositoryLinked failed after a successful repository link:', error);\n }\n return context.json(\n { projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) },\n 201,\n );\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n return context.json(await this.#environmentPayload(tenant.orgId, project));\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const parsed = FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.bodySchema.safeParse(await readJson(context));\n if (!parsed.success) return context.json({ error: 'invalid_environment' }, 400);\n const {\n repositories: repositoryPatches,\n settings: settingsPatch,\n buildTriggers,\n ...projectInput\n } = parsed.data;\n const input: UpdateFactoryProjectInput = projectInput;\n const runner = this.deps.environmentBuilds;\n if (buildTriggers !== undefined) {\n if (!runner) return context.json({ error: 'no_builds' }, 400);\n if (buildTriggers.push?.enabled !== undefined) input.buildOnPushEnabled = buildTriggers.push.enabled;\n if (buildTriggers.push?.debounceMinutes !== undefined) {\n input.buildPushDebounceMinutes = buildTriggers.push.debounceMinutes;\n }\n if (buildTriggers.schedule) {\n if (!runner.scheduleAvailable) {\n if (buildTriggers.schedule.enabled) return context.json({ error: 'schedules_unavailable' }, 400);\n } else {\n const { cron, timezone } = buildTriggers.schedule;\n const stored = await readSchedule(runner.schedules()!, project.id);\n const nextCron = cron ?? stored.cron;\n // Disabling a schedule that never existed needs no cron: it is a no-op.\n if (!nextCron && buildTriggers.schedule.enabled)\n return context.json(\n {\n error: 'invalid_environment',\n issues: [{ message: 'cron is required', path: ['buildTriggers', 'schedule', 'cron'] }],\n },\n 400,\n );\n const nextTimezone = timezone ?? stored.timezone ?? undefined;\n const message = nextCron ? invalidCronMessage(nextCron, nextTimezone) : undefined;\n if (message) {\n return context.json(\n {\n error: 'invalid_environment',\n issues: [{ message, path: ['buildTriggers', 'schedule', 'cron'] }],\n },\n 400,\n );\n }\n }\n }\n }\n if (settingsPatch !== undefined && !this.deps.sandbox) return context.json({ error: 'no_sandbox' }, 400);\n\n let updated = project;\n if (Object.keys(input).length > 0 || settingsPatch !== undefined || repositoryPatches !== undefined) {\n let issues: ReadonlyArray<{ message: string; path?: unknown }> | undefined;\n let repositoryOutcome: 'updated' | 'not_found' | 'incomplete_reorder' | undefined;\n const sourceControl = await this.#sourceControl();\n // One transaction holds the project row, so the settings merge and the link\n // writes either all land or none do, and concurrent patches apply in turn.\n const result = await (\n await this.#projects()\n ).update({\n orgId: tenant.orgId,\n id: projectId,\n input: async (current, ops) => {\n let sandboxSettings: Record<string, unknown> | null | undefined;\n if (settingsPatch !== undefined) {\n const settings = await this.#mergeSettings(current.sandboxSettings ?? {}, settingsPatch);\n if ('issues' in settings) {\n issues = settings.issues;\n return null;\n }\n sandboxSettings = settings.merged;\n }\n if (repositoryPatches !== undefined) {\n repositoryOutcome = await sourceControl.updateEnvironmentRepositories(ops, {\n factoryProjectId: current.id,\n integrationIds: [...this.#versionControlIntegrationIds],\n patches: repositoryPatches,\n });\n if (repositoryOutcome !== 'updated') return null;\n }\n return { ...input, ...(sandboxSettings !== undefined ? { sandboxSettings } : {}) };\n },\n });\n if (repositoryOutcome === 'not_found') return context.json({ error: 'Project repository not found' }, 404);\n if (repositoryOutcome === 'incomplete_reorder') return context.json({ error: 'invalid_environment' }, 400);\n if (issues) {\n return context.json(\n {\n error: 'invalid_environment',\n issues: issues.map(issue => ({\n message: issue.message,\n path: Array.isArray(issue.path)\n ? issue.path.map(segment =>\n typeof segment === 'object' && segment !== null && 'key' in segment ? segment.key : segment,\n )\n : [],\n })),\n },\n 400,\n );\n }\n if (!result) return context.json({ error: 'Project not found' }, 404);\n updated = result;\n }\n if (buildTriggers?.schedule && runner?.scheduleAvailable) {\n const stored = await readSchedule(runner.schedules()!, project.id);\n const cron = buildTriggers.schedule.cron ?? stored.cron;\n // Without a cron the request disables a schedule that never existed: nothing to write.\n if (cron)\n await ensureSchedule(runner.schedules()!, project.id, {\n enabled: buildTriggers.schedule.enabled,\n cron,\n ...((buildTriggers.schedule.timezone ?? stored.timezone)\n ? { timezone: (buildTriggers.schedule.timezone ?? stored.timezone)! }\n : {}),\n });\n }\n // Saving never builds: the next Build now, schedule or push run\n // picks the changed template up.\n return context.json(await this.#environmentPayload(tenant.orgId, updated));\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n const input = parseRepositoryUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const projectRepository = await found.handle.projectRepositories.update({\n orgId: tenant.orgId,\n id: projectRepositoryId,\n input,\n });\n return context.json({\n projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository!),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepositoryId))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n await found.handle.projectRepositories.unlink({ orgId: tenant.orgId, id: projectRepositoryId });\n return context.body(null, 204);\n },\n }),\n ...this.#buildRoutes(),\n ];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"projects.js","names":[],"sources":["../../src/routes/projects.ts"],"sourcesContent":["import { MastraError } from '@mastra/core/error';\nimport type { StandardSchemaWithJSON } from '@mastra/core/schema';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactoryStorageOps } from '@mastra/core/storage';\nimport { describeFactorySandbox, normalizeFactorySandboxSettings } from '@mastra/core/workspace';\nimport type { FactorySandbox, FactorySandboxDescription } from '@mastra/core/workspace';\nimport type { Context } from 'hono';\n\nimport type { EnvironmentBuildRunner } from '../environment/build-runner.js';\nimport { ensureSchedule, invalidCronMessage, readSchedule, scheduleIdFor } from '../environment/build-schedule.js';\nimport { redactCredentials } from '../environment/build.js';\nimport type { SessionRetirementCoordinator } from '../sandbox/session-retirement.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type {\n FactoryProject,\n FactoryProjectsStorage,\n UpdateFactoryProjectInput,\n} from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport { ACTIVE_RUN_BINDING_STAGES } from '../storage/domains/work-items/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { FACTORY_ROUTE_CONTRACTS } from './contracts.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_NAME_LENGTH = 200;\nconst MAX_REPOSITORY_COMMAND_LENGTH = 2_000;\nconst MAX_BRANCH_LENGTH = 255;\nconst MAX_SANDBOX_PROVIDER_LENGTH = 100;\nconst MAX_SANDBOX_WORKDIR_LENGTH = 1_000;\nconst CONTROL_CHAR_RE = /[\\0-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\nfunction loose(context: unknown): Context {\n return context as Context;\n}\n\nasync function readJson(context: Context): Promise<unknown | undefined> {\n try {\n return await context.req.json();\n } catch {\n return undefined;\n }\n}\n\nfunction parseConnectionInput(value: unknown): { integrationId: string; installationId: string } | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.integrationId !== 'string' || !input.integrationId.trim()) return null;\n if (typeof input.installationId !== 'string' || !UUID_RE.test(input.installationId)) return null;\n return { integrationId: input.integrationId.trim(), installationId: input.installationId };\n}\n\nfunction parseOptionalString(\n value: unknown,\n { maxLength, nullable = false }: { maxLength: number; nullable?: boolean },\n): string | null | undefined | false {\n if (value === undefined) return undefined;\n if (value === null) return nullable ? null : false;\n if (typeof value !== 'string') return false;\n const normalized = value.trim();\n if (!normalized) return nullable ? null : false;\n if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;\n return normalized;\n}\n\nfunction parseRepositoryLinkInput(value: unknown): {\n repositoryId?: string;\n repository?: { externalId: string; slug: string };\n branch: string | null;\n sandboxProvider: string;\n sandboxWorkdir: string;\n setupCommand: string | null;\n teardownCommand: string | null;\n} | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const repositoryId =\n typeof input.repositoryId === 'string' && UUID_RE.test(input.repositoryId) ? input.repositoryId : undefined;\n const repositoryInput = input.repository;\n let repository: { externalId: string; slug: string } | undefined;\n if (repositoryInput && typeof repositoryInput === 'object') {\n const candidate = repositoryInput as Record<string, unknown>;\n const externalId = parseOptionalString(candidate.externalId, { maxLength: MAX_NAME_LENGTH });\n const slug = parseOptionalString(candidate.slug, { maxLength: MAX_NAME_LENGTH });\n if (typeof externalId !== 'string' || typeof slug !== 'string') return null;\n repository = { externalId, slug };\n }\n if (!repositoryId && !repository) return null;\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n typeof sandboxProvider !== 'string' ||\n typeof sandboxWorkdir !== 'string' ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n return {\n ...(repositoryId ? { repositoryId } : {}),\n ...(repository ? { repository } : {}),\n branch: branch ?? null,\n sandboxProvider,\n sandboxWorkdir,\n setupCommand: setupCommand ?? null,\n teardownCommand: teardownCommand ?? null,\n };\n}\n\nfunction parseRepositoryUpdateInput(value: unknown): UpdateProjectRepositoryInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateProjectRepositoryInput = {};\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n sandboxProvider === false ||\n sandboxProvider === null ||\n sandboxWorkdir === false ||\n sandboxWorkdir === null ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n if (branch !== undefined) patch.branch = branch;\n if (sandboxProvider !== undefined) patch.sandboxProvider = sandboxProvider;\n if (sandboxWorkdir !== undefined) patch.sandboxWorkdir = sandboxWorkdir;\n if (setupCommand !== undefined) patch.setupCommand = setupCommand;\n if (teardownCommand !== undefined) patch.teardownCommand = teardownCommand;\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\ninterface ModelApplySession {\n thread: {\n getById: (args: { threadId: string }) => Promise<{ metadata?: Record<string, unknown> | null } | null>;\n setSettingOn: (args: { threadId: string; key: string; value: unknown }) => Promise<unknown> | unknown;\n };\n}\n\ninterface ModelApplyController {\n getSession: (sessionId: string) => Promise<ModelApplySession | undefined>;\n}\n\nexport interface ProjectRoutesDeps extends RouteDependencies {\n /** Factory projects domain backing the CRUD surface. */\n projects: FactoryProjectsStorage;\n /** Source-control domain the connection/repository routes fan out over. */\n sourceControl: SourceControlStorage;\n /** Integration ids allowed as source-control connection targets. */\n versionControlIntegrationIds?: string[];\n /** Validate and persist a repository selected from a provider-backed listing. */\n resolveRepository?: (input: {\n integrationId: string;\n orgId: string;\n userId: string;\n installationId: string;\n externalId: string;\n slug: string;\n }) => Promise<SourceControlRepository | null>;\n /**\n * Fire-and-forget hook invoked after a repository is linked to a project —\n * kicks the initial base-checkpoint build. Must never throw.\n */\n onProjectRepositoryLinked?: (args: { orgId: string; projectRepository: ProjectRepository }) => void;\n /** Shared lifecycle for retiring sessions before their owning records are deleted. */\n sessionRetirement?: SessionRetirementCoordinator;\n /** Work-items domain used by session retirement and running-thread model updates. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences' | 'listRunBindings' | 'get'>;\n /** Controller used to reach the thread store behind each active binding. */\n controller?: ModelApplyController;\n /** The factory's normalized sandbox; describes and validates the environment settings. */\n sandbox?: FactorySandbox;\n /** Environment builds, present only when the sandbox has a `builds` capability. */\n environmentBuilds?: EnvironmentBuildRunner;\n /** Intake bindings, released when their Factory project is deleted. */\n intake?: Pick<IntakeStorage, 'ensureReady' | 'clearProjectBindings'>;\n}\n\n/** Provider output is shown to the user; strip any clone credential from it first. */\nfunction redactBuild<T extends { error?: string; logs?: string[] }>(build: T): T {\n return {\n ...build,\n ...(build.error !== undefined ? { error: redactCredentials(build.error) } : {}),\n ...(build.logs ? { logs: build.logs.map(redactCredentials) } : {}),\n };\n}\n\n/** What the environment reports when the factory has no sandbox configured. */\nconst NO_SANDBOX: FactorySandboxDescription = {\n provider: 'none',\n settingsSchema: { type: 'object', properties: {}, additionalProperties: false },\n capabilities: { template: false, builds: { available: false, history: false } },\n};\n\nexport class ProjectRoutes extends Route<ProjectRoutesDeps> {\n readonly #versionControlIntegrationIds: Set<string>;\n #sandboxDescription: FactorySandboxDescription | undefined;\n #settingsSchema: StandardSchemaWithJSON | undefined;\n\n constructor(deps: ProjectRoutesDeps) {\n super(deps);\n this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);\n }\n\n #describeSandbox(): FactorySandboxDescription {\n if (!this.deps.sandbox) return NO_SANDBOX;\n this.#sandboxDescription ??= describeFactorySandbox(this.deps.sandbox);\n return this.#sandboxDescription;\n }\n\n /**\n * Merge a settings patch onto the stored document (null removes a key) and\n * validate the result through the sandbox's schema. Returns the issues when\n * the merged document is rejected.\n *\n * A stored key the current schema does not declare, or whose stored value it\n * now rejects (a tightened range or choice list), is left over from another\n * provider or an older schema. When the merged document fails validation, the\n * merge retries without those keys so one stale value cannot lock every later\n * update; keys the patch itself sets are never pruned.\n */\n async #mergeSettings(\n stored: Record<string, unknown>,\n patch: Record<string, unknown | null>,\n ): Promise<\n { merged: Record<string, unknown> | null } | { issues: ReadonlyArray<{ message: string; path?: unknown }> }\n > {\n const merged: Record<string, unknown> = { ...stored };\n for (const [key, value] of Object.entries(patch)) {\n if (value === null) delete merged[key];\n else merged[key] = value;\n }\n this.#settingsSchema ??= normalizeFactorySandboxSettings(this.deps.sandbox!);\n const result = await this.#settingsSchema['~standard'].validate(merged);\n if (!result.issues) return { merged: Object.keys(merged).length > 0 ? merged : null };\n\n const declared = this.#describeSandbox().settingsSchema.properties ?? {};\n const rejected = new Set(\n result.issues.map(issue => {\n const segment = issue.path?.[0];\n return typeof segment === 'object' && segment !== null ? segment.key : segment;\n }),\n );\n const pruned = Object.fromEntries(\n Object.entries(merged).filter(([key]) => key in patch || (Object.hasOwn(declared, key) && !rejected.has(key))),\n );\n if (Object.keys(pruned).length === Object.keys(merged).length) return { issues: result.issues };\n const retried = await this.#settingsSchema['~standard'].validate(pruned);\n if (retried.issues) return { issues: result.issues };\n return { merged: Object.keys(pruned).length > 0 ? pruned : null };\n }\n\n async #projects(): Promise<FactoryProjectsStorage> {\n await this.deps.projects.ensureReady();\n return this.deps.projects;\n }\n\n async #sourceControl(): Promise<SourceControlStorage> {\n await this.deps.sourceControl.ensureReady();\n return this.deps.sourceControl;\n }\n\n async #intakeRelease(\n orgId: string,\n factoryProjectId: string,\n ): Promise<((ops: FactoryStorageOps) => Promise<void>) | undefined> {\n const { intake } = this.deps;\n if (!intake) return undefined;\n await intake.ensureReady();\n return ops => intake.clearProjectBindings({ orgId, factoryProjectId }, ops);\n }\n\n async #handles(): Promise<SourceControlStorageHandle[]> {\n const storage = await this.#sourceControl();\n return [...this.#versionControlIntegrationIds].map(integrationId => storage.forIntegration(integrationId));\n }\n\n async #project(orgId: string, id: string) {\n return (await this.#projects()).get({ orgId, id });\n }\n\n async #findConnection({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const connection = await handle.connections.get({ orgId, id });\n if (connection?.factoryProjectId === projectId) return { handle, connection };\n }\n return null;\n }\n\n async #findProjectRepository({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const projectRepository = await handle.projectRepositories.get({ orgId, id });\n if (!projectRepository) continue;\n const connection = await handle.connections.get({ orgId, id: projectRepository.connectionId });\n if (connection?.factoryProjectId === projectId) return { handle, connection, projectRepository };\n }\n return null;\n }\n\n async #repositoryPayload(handle: SourceControlStorageHandle, orgId: string, projectRepository: ProjectRepository) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n return { ...projectRepository, repository };\n }\n\n /** Every link of the project across every handle, with the handle that owns it, in position order. */\n async #environmentLinks(orgId: string, projectId: string) {\n const links: Array<{ handle: SourceControlStorageHandle; projectRepository: ProjectRepository }> = [];\n for (const handle of await this.#handles()) {\n for (const projectRepository of await handle.projectRepositories.listByProject({\n orgId,\n factoryProjectId: projectId,\n })) {\n links.push({ handle, projectRepository });\n }\n }\n return links.sort(\n (a, b) =>\n a.projectRepository.position - b.projectRepository.position ||\n a.projectRepository.createdAt.getTime() - b.projectRepository.createdAt.getTime(),\n );\n }\n\n async #environmentPayload(orgId: string, project: FactoryProject) {\n const repositories = [];\n for (const { handle, projectRepository } of await this.#environmentLinks(orgId, project.id)) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n repositories.push({\n projectRepositoryId: projectRepository.id,\n connectionId: projectRepository.connectionId,\n repositoryId: projectRepository.repositoryId,\n slug: repository?.slug ?? null,\n defaultBranch: repository?.defaultBranch ?? null,\n position: projectRepository.position,\n inEnvironment: projectRepository.inEnvironment,\n setupCommand: projectRepository.setupCommand,\n teardownCommand: projectRepository.teardownCommand,\n lastBuildStatus: projectRepository.lastBuildStatus,\n lastBuildError: projectRepository.lastBuildError,\n lastBuiltAt: projectRepository.lastBuiltAt,\n });\n }\n return {\n environment: {\n sandbox: this.#describeSandbox(),\n settings: project.sandboxSettings ?? {},\n sandboxWorkingDirectory: project.sandboxWorkingDirectory,\n workspaceSetupCommand: project.workspaceSetupCommand,\n activeTemplateId: project.activeTemplateId,\n activeTemplateHeads: project.activeTemplateHeads,\n repositories,\n ...(await this.#buildPayload(project)),\n },\n };\n }\n\n /**\n * The build keys of the environment payload, present only when the\n * sandbox can build. Reads the project row and the schedule row; never\n * the provider (live status is the builds routes' job).\n */\n async #buildPayload(project: FactoryProject) {\n const builds = this.deps.environmentBuilds;\n if (!builds) return {};\n const schedules = builds.scheduleAvailable ? builds.schedules() : undefined;\n const schedule = schedules\n ? await readSchedule(schedules, project.id)\n : { enabled: false, cron: null, timezone: null };\n return {\n buildTriggers: {\n schedule: { ...schedule, scheduleAvailable: builds.scheduleAvailable },\n push: { enabled: project.buildOnPushEnabled, debounceMinutes: project.buildPushDebounceMinutes },\n },\n build: project.lastBuildId\n ? { buildId: project.lastBuildId, attemptedAt: project.lastBuildAttemptedAt?.toISOString() ?? null }\n : null,\n };\n }\n\n /** The builds capability behind the routes, or the 404 to answer with. */\n #builds(context: Context) {\n const builds = this.deps.sandbox?.builds;\n const runner = this.deps.environmentBuilds;\n if (!builds || !runner) return { response: context.json({ error: 'no_builds' }, 404) };\n return { builds, runner };\n }\n\n #buildRoutes(): ApiRoute[] {\n return [\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuild.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuild.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n const { heads: _heads, ...outcome } = await capability.runner.start(project.id, 'manual');\n return context.json(outcome);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuilds.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuilds.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n if (!capability.builds.list) return context.json({ error: 'no_history' }, 404);\n const read = await capability.runner.readContext(project.id);\n if (!read) return context.json({ error: 'no_environment' }, 404);\n const builds = await capability.builds.list(read.ctx, read.settings);\n return context.json({ builds: builds.map(redactBuild) });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuildGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentBuildGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const capability = this.#builds(context);\n if ('response' in capability) return capability.response;\n // A composite provider id (E2B's `<templateId>:<buildId>`) travels\n // encoded; Hono hands the param back decoded.\n const buildId = context.req.param('buildId') ?? '';\n if (!buildId) return context.json({ error: 'Build not found' }, 404);\n const read = await capability.runner.readContext(project.id);\n if (!read) return context.json({ error: 'no_environment' }, 404);\n // Providers answer for any id on the host's account, so the route\n // only forwards ids that belong to this project: its last build, or\n // one the provider lists under the project's template.\n if (buildId !== project.lastBuildId) {\n const listed = capability.builds.list ? await capability.builds.list(read.ctx, read.settings) : [];\n if (!listed.some(build => build.buildId === buildId)) {\n return context.json({ error: 'Build not found' }, 404);\n }\n }\n return context.json({ build: redactBuild(await capability.builds.get(read.ctx, read.settings, buildId)) });\n },\n }),\n ];\n }\n\n /** Best effort: a deleted project's cron schedule must not keep firing. */\n async #dropSchedule(projectId: string): Promise<void> {\n const runner = this.deps.environmentBuilds;\n if (!runner?.scheduleAvailable) return;\n try {\n await runner.schedules()!.delete(scheduleIdFor(projectId));\n } catch (error) {\n if (error instanceof MastraError && error.id === 'SCHEDULES_NOT_FOUND') return;\n console.warn('[factory] could not delete the environment build schedule of a deleted project:', error);\n }\n }\n\n async #retireProjectRepositorySessions(\n handle: SourceControlStorageHandle,\n orgId: string,\n projectRepositoryId: string,\n ): Promise<boolean> {\n const sessions = await handle.sessions.listByProjectRepository({ projectRepositoryId });\n if (sessions.length === 0) return true;\n if (!this.deps.sessionRetirement) return false;\n await this.deps.sessionRetirement.retireProjectRepositorySessions({\n sourceControl: handle,\n ...(this.deps.workItems ? { workItems: this.deps.workItems } : {}),\n orgId,\n projectRepositoryId,\n });\n return true;\n }\n\n async #resolveTenant(context: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(context);\n const tenant = this.deps.auth.tenant(context);\n if (!tenant) return { response: context.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: context.json(\n { error: 'organization_required', message: 'Factory projects require an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectList.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectList.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectCreate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectCreate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsed = FACTORY_ROUTE_CONTRACTS.projectCreate.bodySchema.safeParse(await readJson(context));\n if (!parsed.success) return context.json({ error: 'invalid_project' }, 400);\n const project = await (\n await this.#projects()\n ).create({ orgId: tenant.orgId, userId: tenant.userId, input: parsed.data });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectGet.pathSchema.safeParse({ id: context.req.param('id') });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\n const project = await this.#project(tenant.orgId, id);\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectUpdate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectUpdate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectUpdate.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const parsedBody = FACTORY_ROUTE_CONTRACTS.projectUpdate.bodySchema.safeParse(await readJson(context));\n if (!parsedBody.success) return context.json({ error: 'invalid_project' }, 400);\n // Scope decides whose credentials every session bills against, so only\n // admins may switch a shared Factory onto owners' personal keys. The\n // thinking default sets the reasoning spend of every run that has not\n // picked its own, so it is admin-only too.\n const adminField =\n parsedBody.data.modelScope !== undefined\n ? 'model scope'\n : parsedBody.data.defaultThinkingLevel !== undefined\n ? 'thinking default'\n : undefined;\n if (\n adminField &&\n this.deps.auth.enabled() &&\n !(await this.deps.auth.isOrganizationAdmin(context, tenant.orgId))\n ) {\n return context.json(\n {\n error: 'forbidden',\n message: `Organization administrator access is required to change the ${adminField}.`,\n },\n 403,\n );\n }\n const project = await (\n await this.#projects()\n ).update({ orgId: tenant.orgId, id: parsedPath.data.id, input: parsedBody.data });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n if (!(await this.deps.auth.isOrganizationAdmin(context, tenant.orgId))) {\n return context.json(\n {\n error: 'forbidden',\n message: 'Organization administrator access is required to update running sessions.',\n },\n 403,\n );\n }\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectApplyDefaultModel.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, parsedPath.data.id);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const modelId = project.defaultModelId;\n if (!modelId) {\n return context.json(\n { error: 'default_model_not_set', message: 'Set a default model on the project first.' },\n 400,\n );\n }\n const { workItems, controller } = this.deps;\n if (!workItems || !controller) return context.json({ error: 'model_apply_unavailable' }, 503);\n\n const bindings = (await workItems.listRunBindings(tenant.orgId, project.id)).filter(\n binding => binding.status === 'active',\n );\n const seen = new Set<string>();\n const applied: string[] = [];\n const skipped: Array<{\n threadId: string;\n reason:\n | 'not-running'\n | 'work-item-missing'\n | 'stage-inactive'\n | 'thread-missing'\n | 'mode-unknown'\n | 'apply-failed';\n }> = [];\n\n for (const binding of bindings) {\n const key = `${binding.sessionId}:${binding.threadId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n\n const item = await workItems.get({ orgId: tenant.orgId, id: binding.workItemId });\n if (!item || item.factoryProjectId !== project.id) {\n skipped.push({ threadId: binding.threadId, reason: 'work-item-missing' });\n continue;\n }\n if (!ACTIVE_RUN_BINDING_STAGES.has(item.stages[0] ?? '')) {\n skipped.push({ threadId: binding.threadId, reason: 'stage-inactive' });\n continue;\n }\n const session = await controller.getSession(binding.sessionId);\n if (!session) {\n skipped.push({ threadId: binding.threadId, reason: 'not-running' });\n continue;\n }\n const thread = await session.thread.getById({ threadId: binding.threadId });\n if (!thread) {\n skipped.push({ threadId: binding.threadId, reason: 'thread-missing' });\n continue;\n }\n try {\n // This route targets inactive bound threads, so persist the controller-owned key through the session gateway.\n await session.thread.setSettingOn({\n threadId: binding.threadId,\n key: 'currentModelId',\n value: modelId,\n });\n applied.push(binding.threadId);\n } catch (error) {\n console.warn('[factory] apply-default-model failed for thread', binding.threadId, error);\n skipped.push({ threadId: binding.threadId, reason: 'apply-failed' });\n }\n }\n\n return context.json({ modelId, applied, skipped });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectDelete.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectDelete.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const parsedPath = FACTORY_ROUTE_CONTRACTS.projectDelete.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\n if (!(await this.#project(tenant.orgId, id))) return context.json({ error: 'Project not found' }, 404);\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({ orgId: tenant.orgId, factoryProjectId: id })) {\n for (const projectRepository of await handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId: connection.id,\n })) {\n if (!(await this.#retireProjectRepositorySessions(handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await handle.connections.delete({ orgId: tenant.orgId, id: connection.id });\n }\n }\n await (\n await this.#projects()\n ).delete({ orgId: tenant.orgId, id }, await this.#intakeRelease(tenant.orgId, id));\n await this.#dropSchedule(id);\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const connections = [];\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n })) {\n const installation = await handle.installations.get({\n orgId: tenant.orgId,\n id: connection.installationId,\n });\n // Skip orphaned connections whose installation was pruned (e.g.\n // the user uninstalled the GitHub App). Otherwise\n // `projectRepositories.list` throws `requireConnection` and the\n // whole endpoint 500s, which hangs the web UI on the page\n // loader for every project. New code cascade-deletes these on\n // installation removal, but this defensive skip lets already-\n // orphaned rows in existing databases self-heal on read.\n if (!installation) continue;\n const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });\n connections.push({\n ...connection,\n installation,\n repositories: await Promise.all(links.map(link => this.#repositoryPayload(handle, tenant.orgId, link))),\n });\n }\n }\n return context.json({ connections });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const input = parseConnectionInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_source_control_connection' }, 400);\n if (!this.#versionControlIntegrationIds.has(input.integrationId))\n return context.json({ error: 'Source-control integration not found' }, 404);\n const handle = (await this.#sourceControl()).forIntegration(input.integrationId);\n if (!(await handle.installations.get({ orgId: tenant.orgId, id: input.installationId })))\n return context.json({ error: 'Source-control installation not found' }, 404);\n const connection = await handle.connections.create({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n installationId: input.installationId,\n createdByUserId: tenant.userId,\n });\n return context.json({ connection }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n for (const projectRepository of await found.handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId,\n })) {\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await found.handle.connections.delete({ orgId: tenant.orgId, id: connectionId });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId/repositories', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n const input = parseRepositoryLinkInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const repository = input.repositoryId\n ? await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId })\n : input.repository && this.deps.resolveRepository\n ? await this.deps.resolveRepository({\n integrationId: found.connection.integrationId,\n orgId: tenant.orgId,\n userId: tenant.userId,\n installationId: found.connection.installationId,\n ...input.repository,\n })\n : null;\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const { repositoryId: _repositoryId, repository: _repository, ...linkInput } = input;\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n repositoryId: repository.id,\n ...linkInput,\n });\n try {\n this.deps.onProjectRepositoryLinked?.({ orgId: tenant.orgId, projectRepository });\n } catch (error) {\n console.warn('[factory] onProjectRepositoryLinked failed after a successful repository link:', error);\n }\n return context.json(\n { projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) },\n 201,\n );\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentGet.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n return context.json(await this.#environmentPayload(tenant.orgId, project));\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.method,\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, projectId);\n if (!project) return context.json({ error: 'Project not found' }, 404);\n const parsed = FACTORY_ROUTE_CONTRACTS.projectEnvironmentUpdate.bodySchema.safeParse(await readJson(context));\n if (!parsed.success) return context.json({ error: 'invalid_environment' }, 400);\n const {\n repositories: repositoryPatches,\n settings: settingsPatch,\n buildTriggers,\n ...projectInput\n } = parsed.data;\n const input: UpdateFactoryProjectInput = projectInput;\n const runner = this.deps.environmentBuilds;\n if (buildTriggers !== undefined) {\n if (!runner) return context.json({ error: 'no_builds' }, 400);\n if (buildTriggers.push?.enabled !== undefined) input.buildOnPushEnabled = buildTriggers.push.enabled;\n if (buildTriggers.push?.debounceMinutes !== undefined) {\n input.buildPushDebounceMinutes = buildTriggers.push.debounceMinutes;\n }\n if (buildTriggers.schedule) {\n if (!runner.scheduleAvailable) {\n if (buildTriggers.schedule.enabled) return context.json({ error: 'schedules_unavailable' }, 400);\n } else {\n const { cron, timezone } = buildTriggers.schedule;\n const stored = await readSchedule(runner.schedules()!, project.id);\n const nextCron = cron ?? stored.cron;\n // Disabling a schedule that never existed needs no cron: it is a no-op.\n if (!nextCron && buildTriggers.schedule.enabled)\n return context.json(\n {\n error: 'invalid_environment',\n issues: [{ message: 'cron is required', path: ['buildTriggers', 'schedule', 'cron'] }],\n },\n 400,\n );\n const nextTimezone = timezone ?? stored.timezone ?? undefined;\n const message = nextCron ? invalidCronMessage(nextCron, nextTimezone) : undefined;\n if (message) {\n return context.json(\n {\n error: 'invalid_environment',\n issues: [{ message, path: ['buildTriggers', 'schedule', 'cron'] }],\n },\n 400,\n );\n }\n }\n }\n }\n if (settingsPatch !== undefined && !this.deps.sandbox) return context.json({ error: 'no_sandbox' }, 400);\n\n let updated = project;\n if (Object.keys(input).length > 0 || settingsPatch !== undefined || repositoryPatches !== undefined) {\n let issues: ReadonlyArray<{ message: string; path?: unknown }> | undefined;\n let repositoryOutcome: 'updated' | 'not_found' | 'incomplete_reorder' | undefined;\n const sourceControl = await this.#sourceControl();\n // One transaction holds the project row, so the settings merge and the link\n // writes either all land or none do, and concurrent patches apply in turn.\n const result = await (\n await this.#projects()\n ).update({\n orgId: tenant.orgId,\n id: projectId,\n input: async (current, ops) => {\n let sandboxSettings: Record<string, unknown> | null | undefined;\n if (settingsPatch !== undefined) {\n const settings = await this.#mergeSettings(current.sandboxSettings ?? {}, settingsPatch);\n if ('issues' in settings) {\n issues = settings.issues;\n return null;\n }\n sandboxSettings = settings.merged;\n }\n if (repositoryPatches !== undefined) {\n repositoryOutcome = await sourceControl.updateEnvironmentRepositories(ops, {\n factoryProjectId: current.id,\n integrationIds: [...this.#versionControlIntegrationIds],\n patches: repositoryPatches,\n });\n if (repositoryOutcome !== 'updated') return null;\n }\n return { ...input, ...(sandboxSettings !== undefined ? { sandboxSettings } : {}) };\n },\n });\n if (repositoryOutcome === 'not_found') return context.json({ error: 'Project repository not found' }, 404);\n if (repositoryOutcome === 'incomplete_reorder') return context.json({ error: 'invalid_environment' }, 400);\n if (issues) {\n return context.json(\n {\n error: 'invalid_environment',\n issues: issues.map(issue => ({\n message: issue.message,\n path: Array.isArray(issue.path)\n ? issue.path.map(segment =>\n typeof segment === 'object' && segment !== null && 'key' in segment ? segment.key : segment,\n )\n : [],\n })),\n },\n 400,\n );\n }\n if (!result) return context.json({ error: 'Project not found' }, 404);\n updated = result;\n }\n if (buildTriggers?.schedule && runner?.scheduleAvailable) {\n const stored = await readSchedule(runner.schedules()!, project.id);\n const cron = buildTriggers.schedule.cron ?? stored.cron;\n // Without a cron the request disables a schedule that never existed: nothing to write.\n if (cron)\n await ensureSchedule(runner.schedules()!, project.id, {\n enabled: buildTriggers.schedule.enabled,\n cron,\n ...((buildTriggers.schedule.timezone ?? stored.timezone)\n ? { timezone: (buildTriggers.schedule.timezone ?? stored.timezone)! }\n : {}),\n });\n }\n // Saving never builds: the next Build now, schedule or push run\n // picks the changed template up.\n return context.json(await this.#environmentPayload(tenant.orgId, updated));\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n const input = parseRepositoryUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const projectRepository = await found.handle.projectRepositories.update({\n orgId: tenant.orgId,\n id: projectRepositoryId,\n input,\n });\n return context.json({\n projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository!),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepositoryId))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n await found.handle.projectRepositories.unlink({ orgId: tenant.orgId, id: projectRepositoryId });\n return context.body(null, 204);\n },\n }),\n ...this.#buildRoutes(),\n ];\n }\n}\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ import type { InlineSkill } from '@mastra/core/skills';
6
6
  import type { FactoryStorage } from '@mastra/core/storage';
7
7
  import type { FactorySandbox } from '@mastra/core/workspace';
8
8
  import type { BoardRegistry } from '../boards/index.js';
9
+ import type { ProjectBoardCache, ProjectBoardSource } from '../boards/project-boards.js';
9
10
  import type { FactoryIntegration, IntegrationContext, IntegrationHooks } from '../integrations/base.js';
10
11
  import type { GithubIntegration } from '../integrations/github/integration.js';
11
12
  import type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';
@@ -14,6 +15,7 @@ import { FactoryTransitionService } from '../rules/transition-service.js';
14
15
  import type { LiveSessions } from '../session/live-sessions.js';
15
16
  import type { StateSigner } from '../state-signing.js';
16
17
  import type { AuditEmitter, AuditRecorder } from '../storage/domains/audit/domain.js';
18
+ import type { StoredBoardsStorage } from '../storage/domains/boards/base.js';
17
19
  import type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';
18
20
  import type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';
19
21
  import type { CommentsDomain } from '../storage/domains/comments/domain.js';
@@ -83,8 +85,13 @@ export interface FactoryApiRoutesDeps {
83
85
  knowledgeKey?: string;
84
86
  /** Resolved Factory rule set, threaded from the host (no service locator). */
85
87
  configVersion: string;
86
- /** Boards installed for this Factory instance. */
87
- boardRegistry: BoardRegistry;
88
+ /** Each project's boards; integrations use the code boards (`codeRegistry`). */
89
+ boardRegistry: BoardRegistry | ProjectBoardSource;
90
+ /** Stored boards and their cache; mounts the board management routes when present. */
91
+ storedBoards?: {
92
+ storage: StoredBoardsStorage;
93
+ cache: ProjectBoardCache;
94
+ };
88
95
  /** Work-item feed service, handed to integrations that ingest platform messages. */
89
96
  feed: CommentsDomain;
90
97
  factoryTransitionService?: FactoryTransitionService;
@@ -107,7 +114,7 @@ export interface FactoryApiRoutesDeps {
107
114
  * browser and no interactive user, so nothing else would catch a regression in
108
115
  * what it forwards.
109
116
  */
110
- export declare function prepareFactoryRuleBinding(sourceControlProvider: SourceControlStorageHandle | Pick<GithubIntegration, 'sourceControlStorage'>, coordinator: Pick<FactoryStartCoordinator, 'prepare'>, projects: FactoryProjectsStorage, boards: BoardRegistry, input: FactoryBindingPreparationInput, intake?: Pick<IntakeStorage, 'getConfig'>): Promise<void>;
117
+ export declare function prepareFactoryRuleBinding(sourceControlProvider: SourceControlStorageHandle | Pick<GithubIntegration, 'sourceControlStorage'>, coordinator: Pick<FactoryStartCoordinator, 'prepare'>, projects: FactoryProjectsStorage, boards: BoardRegistry | ProjectBoardSource, input: FactoryBindingPreparationInput, intake?: Pick<IntakeStorage, 'getConfig'>): Promise<void>;
111
118
  /**
112
119
  * Build the {@link IntegrationContext} handed to an integration when the
113
120
  * factory collects its capabilities (routes, workers). One shape everywhere:
@@ -1 +1 @@
1
- {"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { boardForWorkItem } from "../boards/semantics.js";
2
2
  import { legacyGithubWorkItemBranch, workItemBranch, workItemBranchSource, workItemThreadTitle } from "../work-item-branch.js";
3
3
  import "../boards/index.js";
4
4
  import { MaterializeError } from "../integrations/github/sandbox.js";
5
+ import { toBoardSource } from "../boards/project-boards.js";
5
6
  import { FactoryDispatchError } from "../rules/dispatch-errors.js";
6
7
  import { isAgentActor } from "../storage/domains/work-items/base.js";
7
8
  import { legacyPlanRoleFor, workLaneForRole } from "../boards/legacy-plan-role.js";
@@ -17,6 +18,7 @@ import { invalidateCustomProvidersSnapshots } from "./custom-provider-source.js"
17
18
  import { getGithubFeatureDiagnostics } from "../integrations/github/config.js";
18
19
  import { FactoryTransitionService } from "../rules/transition-service.js";
19
20
  import { buildAutomationRunRoutes } from "./automation-runs.js";
21
+ import { BoardRoutes } from "./boards.js";
20
22
  import { buildFsRoutes } from "./fs.js";
21
23
  import { IntakeRoutes } from "./intake.js";
22
24
  import { KnowledgeRoutes } from "./knowledge.js";
@@ -132,7 +134,7 @@ async function prepareFactoryRuleBinding(sourceControlProvider, coordinator, pro
132
134
  metadata: input.item.metadata
133
135
  });
134
136
  const boardId = boardForWorkItem(input.item);
135
- const board = boards.get(boardId);
137
+ const board = (await toBoardSource(boards).load(input.record)).get(boardId);
136
138
  const currentStage = input.item.stages.length === 1 ? input.item.stages[0] : void 0;
137
139
  const currentKind = currentStage === void 0 ? void 0 : board?.phaseKind(currentStage);
138
140
  const destinationStage = currentKind === void 0 ? void 0 : currentKind === "resting" ? restingExitLane(board, input.role) : currentStage;
@@ -370,6 +372,7 @@ function assembleFactoryApiRoutes(deps) {
370
372
  if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);
371
373
  const context = buildIntegrationContext({
372
374
  ...deps,
375
+ boardRegistry: toBoardSource(deps.boardRegistry).codeRegistry,
373
376
  stateSigner: deps.stateSigner,
374
377
  emitAudit,
375
378
  ...githubRegistration ? { sourceControlOwnerId: "github" } : {}
@@ -520,6 +523,14 @@ function assembleFactoryApiRoutes(deps) {
520
523
  startCoordinator,
521
524
  liveSessions: deps.liveSessions
522
525
  }).routes() : [],
526
+ ...deps.factoryReady && deps.storedBoards ? new BoardRoutes({
527
+ auth: deps.auth,
528
+ audit: deps.audit,
529
+ projects: deps.domains.projects,
530
+ workItems: deps.domains.workItems,
531
+ storage: deps.storedBoards.storage,
532
+ boards: deps.storedBoards.cache
533
+ }).routes() : [],
523
534
  ...deps.factoryReady ? buildAutomationRunRoutes({
524
535
  auth: deps.auth,
525
536
  audit: deps.audit,