@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.2

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 (196) hide show
  1. package/README.md +80 -4
  2. package/dist/auth.d.ts +8 -0
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +15 -1
  5. package/dist/auth.js.map +1 -1
  6. package/dist/boards/work-transition-policy.d.ts.map +1 -1
  7. package/dist/boards/work-transition-policy.js +10 -0
  8. package/dist/boards/work-transition-policy.js.map +1 -1
  9. package/dist/boards/work.d.ts +14 -0
  10. package/dist/boards/work.d.ts.map +1 -1
  11. package/dist/boards/work.js +70 -7
  12. package/dist/boards/work.js.map +1 -1
  13. package/dist/capabilities/version-control.d.ts +26 -0
  14. package/dist/capabilities/version-control.d.ts.map +1 -1
  15. package/dist/factory.d.ts.map +1 -1
  16. package/dist/factory.js +173 -11
  17. package/dist/factory.js.map +1 -1
  18. package/dist/integrations/github/integration.d.ts.map +1 -1
  19. package/dist/integrations/github/integration.js +37 -1
  20. package/dist/integrations/github/integration.js.map +1 -1
  21. package/dist/integrations/gitlab/api.d.ts +15 -0
  22. package/dist/integrations/gitlab/api.d.ts.map +1 -1
  23. package/dist/integrations/gitlab/api.js +9 -0
  24. package/dist/integrations/gitlab/api.js.map +1 -1
  25. package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
  26. package/dist/integrations/gitlab/version-control.js +37 -3
  27. package/dist/integrations/gitlab/version-control.js.map +1 -1
  28. package/dist/integrations/incidentio/api.d.ts +9 -0
  29. package/dist/integrations/incidentio/api.d.ts.map +1 -1
  30. package/dist/integrations/incidentio/api.js +3 -0
  31. package/dist/integrations/incidentio/api.js.map +1 -1
  32. package/dist/integrations/incidentio/integration.d.ts.map +1 -1
  33. package/dist/integrations/incidentio/integration.js +1 -0
  34. package/dist/integrations/incidentio/integration.js.map +1 -1
  35. package/dist/integrations/jira/api.d.ts +7 -0
  36. package/dist/integrations/jira/api.d.ts.map +1 -1
  37. package/dist/integrations/jira/api.js +4 -0
  38. package/dist/integrations/jira/api.js.map +1 -1
  39. package/dist/integrations/platform/api-client.d.ts +6 -0
  40. package/dist/integrations/platform/api-client.d.ts.map +1 -1
  41. package/dist/integrations/platform/api-client.js +6 -1
  42. package/dist/integrations/platform/api-client.js.map +1 -1
  43. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  44. package/dist/integrations/platform/github/integration.js +27 -1
  45. package/dist/integrations/platform/github/integration.js.map +1 -1
  46. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  47. package/dist/integrations/platform/incidentio/integration.js +1 -0
  48. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  49. package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
  50. package/dist/integrations/platform/jira/integration.js +1 -0
  51. package/dist/integrations/platform/jira/integration.js.map +1 -1
  52. package/dist/integrations/subscription-session.d.ts +15 -0
  53. package/dist/integrations/subscription-session.d.ts.map +1 -1
  54. package/dist/integrations/subscription-session.js +30 -13
  55. package/dist/integrations/subscription-session.js.map +1 -1
  56. package/dist/provider-display-name.d.ts +2 -0
  57. package/dist/provider-display-name.d.ts.map +1 -0
  58. package/dist/provider-display-name.js +16 -0
  59. package/dist/provider-display-name.js.map +1 -0
  60. package/dist/routes/config.js +2 -2
  61. package/dist/routes/config.js.map +1 -1
  62. package/dist/routes/contracts.d.ts +4 -0
  63. package/dist/routes/contracts.d.ts.map +1 -1
  64. package/dist/routes/contracts.js +3 -1
  65. package/dist/routes/contracts.js.map +1 -1
  66. package/dist/routes/oauth.js +1 -1
  67. package/dist/routes/oauth.js.map +1 -1
  68. package/dist/routes/provider-credentials.d.ts +6 -2
  69. package/dist/routes/provider-credentials.d.ts.map +1 -1
  70. package/dist/routes/provider-credentials.js +20 -7
  71. package/dist/routes/provider-credentials.js.map +1 -1
  72. package/dist/routes/source-control-sessions.d.ts +1 -0
  73. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  74. package/dist/routes/source-control-sessions.js +1 -1
  75. package/dist/routes/source-control-sessions.js.map +1 -1
  76. package/dist/routes/surface.d.ts +1 -0
  77. package/dist/routes/surface.d.ts.map +1 -1
  78. package/dist/routes/surface.js +1 -0
  79. package/dist/routes/surface.js.map +1 -1
  80. package/dist/routes/work-items.d.ts.map +1 -1
  81. package/dist/routes/work-items.js +57 -117
  82. package/dist/routes/work-items.js.map +1 -1
  83. package/dist/rules/dispatcher.d.ts +7 -0
  84. package/dist/rules/dispatcher.d.ts.map +1 -1
  85. package/dist/rules/dispatcher.js +60 -3
  86. package/dist/rules/dispatcher.js.map +1 -1
  87. package/dist/rules/processor.d.ts.map +1 -1
  88. package/dist/rules/processor.js +2 -1
  89. package/dist/rules/processor.js.map +1 -1
  90. package/dist/rules/tools.d.ts.map +1 -1
  91. package/dist/rules/tools.js +82 -3
  92. package/dist/rules/tools.js.map +1 -1
  93. package/dist/rules/transition-service.d.ts +17 -0
  94. package/dist/rules/transition-service.d.ts.map +1 -1
  95. package/dist/rules/transition-service.js +25 -0
  96. package/dist/rules/transition-service.js.map +1 -1
  97. package/dist/rules/types.d.ts +2 -0
  98. package/dist/rules/types.d.ts.map +1 -1
  99. package/dist/rules/types.js.map +1 -1
  100. package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
  101. package/dist/sandbox/local-factory-sandbox.js +1 -0
  102. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  103. package/dist/session/factory-session.d.ts +15 -1
  104. package/dist/session/factory-session.d.ts.map +1 -1
  105. package/dist/session/factory-session.js +4 -2
  106. package/dist/session/factory-session.js.map +1 -1
  107. package/dist/session/reply-credential-processor.d.ts +15 -0
  108. package/dist/session/reply-credential-processor.d.ts.map +1 -0
  109. package/dist/session/reply-credential-processor.js +40 -0
  110. package/dist/session/reply-credential-processor.js.map +1 -0
  111. package/dist/session/source-control-tools.d.ts +19 -1
  112. package/dist/session/source-control-tools.d.ts.map +1 -1
  113. package/dist/session/source-control-tools.js +70 -3
  114. package/dist/session/source-control-tools.js.map +1 -1
  115. package/dist/storage/domains/projects/base.d.ts +6 -0
  116. package/dist/storage/domains/projects/base.d.ts.map +1 -1
  117. package/dist/storage/domains/projects/base.js +12 -0
  118. package/dist/storage/domains/projects/base.js.map +1 -1
  119. package/dist/storage/domains/source-control/base.d.ts +17 -0
  120. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  121. package/dist/storage/domains/source-control/base.js +23 -0
  122. package/dist/storage/domains/source-control/base.js.map +1 -1
  123. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  124. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  125. package/dist/storage/domains/source-control/inmemory.js +5 -1
  126. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  127. package/dist/storage/domains/work-items/base.d.ts +56 -0
  128. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  129. package/dist/storage/domains/work-items/base.js +139 -25
  130. package/dist/storage/domains/work-items/base.js.map +1 -1
  131. package/dist/tools/approval-tools.d.ts +15 -0
  132. package/dist/tools/approval-tools.d.ts.map +1 -0
  133. package/dist/tools/approval-tools.js +77 -0
  134. package/dist/tools/approval-tools.js.map +1 -0
  135. package/dist/tools/deliver.d.ts +54 -0
  136. package/dist/tools/deliver.d.ts.map +1 -0
  137. package/dist/tools/deliver.js +105 -0
  138. package/dist/tools/deliver.js.map +1 -0
  139. package/dist/tools/index.d.ts +29 -0
  140. package/dist/tools/index.d.ts.map +1 -0
  141. package/dist/tools/index.js +39 -0
  142. package/dist/tools/index.js.map +1 -0
  143. package/dist/tools/messaging-tools.d.ts +28 -0
  144. package/dist/tools/messaging-tools.d.ts.map +1 -0
  145. package/dist/tools/messaging-tools.js +312 -0
  146. package/dist/tools/messaging-tools.js.map +1 -0
  147. package/dist/tools/permissions.d.ts +31 -0
  148. package/dist/tools/permissions.d.ts.map +1 -0
  149. package/dist/tools/permissions.js +44 -0
  150. package/dist/tools/permissions.js.map +1 -0
  151. package/dist/tools/read-tools.d.ts +27 -0
  152. package/dist/tools/read-tools.d.ts.map +1 -0
  153. package/dist/tools/read-tools.js +285 -0
  154. package/dist/tools/read-tools.js.map +1 -0
  155. package/dist/tools/reports.d.ts +163 -0
  156. package/dist/tools/reports.d.ts.map +1 -0
  157. package/dist/tools/reports.js +352 -0
  158. package/dist/tools/reports.js.map +1 -0
  159. package/dist/tools/run-watch.d.ts +46 -0
  160. package/dist/tools/run-watch.d.ts.map +1 -0
  161. package/dist/tools/run-watch.js +102 -0
  162. package/dist/tools/run-watch.js.map +1 -0
  163. package/dist/tools/scope.d.ts +45 -0
  164. package/dist/tools/scope.d.ts.map +1 -0
  165. package/dist/tools/scope.js +73 -0
  166. package/dist/tools/scope.js.map +1 -0
  167. package/dist/tools/session-tools.d.ts +31 -0
  168. package/dist/tools/session-tools.d.ts.map +1 -0
  169. package/dist/tools/session-tools.js +312 -0
  170. package/dist/tools/session-tools.js.map +1 -0
  171. package/dist/tools/sessions-state-processor.d.ts +37 -0
  172. package/dist/tools/sessions-state-processor.d.ts.map +1 -0
  173. package/dist/tools/sessions-state-processor.js +206 -0
  174. package/dist/tools/sessions-state-processor.js.map +1 -0
  175. package/dist/tools/work-item-tools.d.ts +28 -0
  176. package/dist/tools/work-item-tools.d.ts.map +1 -0
  177. package/dist/tools/work-item-tools.js +322 -0
  178. package/dist/tools/work-item-tools.js.map +1 -0
  179. package/dist/work-items/create-work-item.d.ts +55 -0
  180. package/dist/work-items/create-work-item.d.ts.map +1 -0
  181. package/dist/work-items/create-work-item.js +101 -0
  182. package/dist/work-items/create-work-item.js.map +1 -0
  183. package/dist/work-items/decision-actions.d.ts +42 -0
  184. package/dist/work-items/decision-actions.d.ts.map +1 -0
  185. package/dist/work-items/decision-actions.js +70 -0
  186. package/dist/work-items/decision-actions.js.map +1 -0
  187. package/dist/workspace.d.ts.map +1 -1
  188. package/dist/workspace.js +3 -1
  189. package/dist/workspace.js.map +1 -1
  190. package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
  191. package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
  192. package/factory-skills/factory-plan/SKILL.md +4 -4
  193. package/factory-skills/factory-rereview/SKILL.md +3 -1
  194. package/factory-skills/factory-review/SKILL.md +3 -1
  195. package/factory-skills/factory-self-review/SKILL.md +46 -0
  196. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport { resolveTenantProviderDefaultModel } from '../routes/tenant-credentials.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport { effectiveModelScope } from '../storage/domains/projects/base.js';\nimport type { EffectiveModelScope, FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { resolveEnvironmentRepositories } from './environment-repositories.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/** Resolve the model and exclusive credential scope for a Factory session. */\nexport async function resolveFactorySessionModel(args: {\n projects: FactoryProjectsStorage | undefined;\n modelDefaults: ModelDefaultsStorage | undefined;\n factoryProjectId: string | undefined;\n orgId: string;\n ownerUserId: string;\n /** A work-item run or an interactive session; a hybrid Factory resolves a different scope for each. */\n context: 'run' | 'session';\n}): Promise<{ modelScope: EffectiveModelScope; modelId?: string }> {\n if (!args.projects || !args.factoryProjectId) throw new Error('Factory model policy is unavailable.');\n\n const project = await args.projects.getById({ id: args.factoryProjectId });\n if (!project || project.orgId !== args.orgId) throw new Error('Factory model policy is unavailable.');\n if (effectiveModelScope(project.modelScope, args.context) === 'factory') {\n return { modelScope: 'factory', ...(project.defaultModelId ? { modelId: project.defaultModelId } : {}) };\n }\n\n try {\n const userDefault = await args.modelDefaults?.get({ orgId: args.orgId, userId: args.ownerUserId });\n return { modelScope: 'user', ...(userDefault?.modelId ? { modelId: userDefault.modelId } : {}) };\n } catch (error) {\n console.warn('[Factory] Owner default model lookup failed; using the mode default model', {\n orgId: args.orgId,\n ownerUserId: args.ownerUserId,\n error: error instanceof Error ? error.message : String(error),\n });\n return { modelScope: 'user' };\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /**\n * The repository link the session is filed under: the one a work item\n * targets. Defaults to the factory's position-1 environment link (D1), for\n * callers where nothing names a repository (chat threads, user sessions).\n */\n projectRepositoryId?: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolvePrimaryEnvironmentRepository}. A miss carries which\n * step failed: callers differ on whether that is an error (an autonomous run\n * cannot proceed) or a routine fallback (a chat integration drops to a\n * chat-only session), and the two steps fail for different reasons worth\n * reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * The repository link a new session is filed under. By default the position-1\n * `inEnvironment` link of the factory (decision D1); a work item that targets\n * a repository names its link through `projectRepositoryId`, which must be an\n * environment link of the same factory. Session start no longer picks a\n * repository to work in, the sandbox holds every environment repository. The\n * PR tools default to this link, and subscriptions, audit and the work item's\n * repository check still read it; moving them to the per-repository\n * `sessionRepositories` rows is a follow-up.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolvePrimaryEnvironmentRepository(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n projectRepositoryId?: string;\n}): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId, projectRepositoryId } = args;\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n if (!connections.some(candidate => candidate.integrationId === sourceControl.integrationId)) {\n return { found: false, reason: 'connection' };\n }\n const repositories = await resolveEnvironmentRepositories({ sourceControl, orgId, factoryProjectId });\n const primary = repositories.find(\n candidate => projectRepositoryId === undefined || candidate.link.id === projectRepositoryId,\n );\n if (!primary) return { found: false, reason: 'repository' };\n return {\n found: true,\n projectRepositoryId: primary.link.id,\n baseBranch: primary.link.branch ?? primary.repository.defaultBranch,\n connectedByUserId: primary.connection.createdByUserId,\n };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, the\n * work item's repository link (else the position-1 environment link, D1) as\n * the session's link, and a session on the requested branch with that\n * repository's pinned or default branch as the base. The sandbox itself boots\n * every environment repository.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, projectRepositoryId } = args;\n\n const resolved = await resolvePrimaryEnvironmentRepository({\n sourceControl,\n orgId,\n factoryProjectId,\n projectRepositoryId,\n });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n factoryProjectId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: 'org',\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n modelScope: EffectiveModelScope;\n ownerUserId: string;\n /**\n * The model selected by the Factory scope. Without it the provider default of a connected\n * provider is applied when the SDK mode default has no credential, else the mode default.\n */\n defaultModelId?: string;\n}\n\n/**\n * Apply a factory project's main model to a session on every run.\n *\n * The model switch is best-effort. A retired model id must not sink a run that\n * is otherwise ready, and the reason is logged. Observational-memory settings are resolved from the\n * authoritative row per invocation before memory processors run.\n */\nexport async function hydrateFactorySession(\n session: FactorySession,\n args: HydrateFactorySessionArgs,\n): Promise<string | undefined> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n await session.state.set({\n factoryModelScope: args.modelScope,\n factoryOwnerUserId: args.ownerUserId,\n });\n\n let selectedModelId = args.defaultModelId;\n if (!selectedModelId) {\n const modeModelId = session.mode.resolve().defaultModelId;\n const providerModelId = await resolveTenantProviderDefaultModel(\n { orgId: args.orgId, userId: args.ownerUserId, credentialScope: args.modelScope },\n modeModelId,\n ).catch(error => {\n console.warn('[Factory Start] Failed to resolve a provider default model', {\n error: error instanceof Error ? error.message : String(error),\n });\n return undefined;\n });\n selectedModelId = providerModelId ?? modeModelId;\n }\n if (!selectedModelId) return undefined;\n\n let switched = true;\n try {\n await session.model.switch(selectedModelId);\n } catch (error) {\n switched = false;\n console.warn('[Factory Start] Failed to apply scoped default model', {\n modelId: selectedModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: selectedModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply scoped default subagent model', {\n agentType,\n modelId: selectedModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n // Callers pin this id to the thread, so only report a model the session actually runs on.\n return switched ? selectedModelId : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory-session.js","names":[],"sources":["../../src/session/factory-session.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\n\nimport { resolveTenantProviderDefaultModel } from '../routes/tenant-credentials.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport { effectiveModelScope } from '../storage/domains/projects/base.js';\nimport type { EffectiveModelScope, FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport {\n SourceControlConnectionNotFoundError,\n type SourceControlSession,\n type SourceControlSessionVisibility,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { resolveEnvironmentRepositories } from './environment-repositories.js';\nimport { seedSessionOrg } from './org-seed.js';\n\ntype FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;\n\nexport class FactorySourceControlConflictError extends Error {}\n\n/** Resolve the model and exclusive credential scope for a Factory session. */\nexport async function resolveFactorySessionModel(args: {\n projects: FactoryProjectsStorage | undefined;\n modelDefaults: ModelDefaultsStorage | undefined;\n factoryProjectId: string | undefined;\n orgId: string;\n ownerUserId: string;\n /** A work-item run or an interactive session; a hybrid Factory resolves a different scope for each. */\n context: 'run' | 'session';\n}): Promise<{ modelScope: EffectiveModelScope; modelId?: string }> {\n if (!args.projects || !args.factoryProjectId) throw new Error('Factory model policy is unavailable.');\n\n const project = await args.projects.getById({ id: args.factoryProjectId });\n if (!project || project.orgId !== args.orgId) throw new Error('Factory model policy is unavailable.');\n if (effectiveModelScope(project.modelScope, args.context) === 'factory') {\n return { modelScope: 'factory', ...(project.defaultModelId ? { modelId: project.defaultModelId } : {}) };\n }\n\n try {\n const userDefault = await args.modelDefaults?.get({ orgId: args.orgId, userId: args.ownerUserId });\n return { modelScope: 'user', ...(userDefault?.modelId ? { modelId: userDefault.modelId } : {}) };\n } catch (error) {\n console.warn('[Factory] Owner default model lookup failed; using the mode default model', {\n orgId: args.orgId,\n ownerUserId: args.ownerUserId,\n error: error instanceof Error ? error.message : String(error),\n });\n return { modelScope: 'user' };\n }\n}\n\nexport interface SourceControlSessionLookup {\n getBySessionId(sessionId: string): Promise<SourceControlSession | null>;\n getSourceControlBySessionId(sessionId: string): Promise<SourceControlStorageHandle | null>;\n /** Side sessions started by `originSessionId`, across every provider. */\n listByOrigin(args: { orgId: string; originSessionId: string }): Promise<SourceControlSession[]>;\n rename(args: { sessionId: string; title: string }): Promise<void>;\n markFirstMessage(args: { sessionId: string }): Promise<void>;\n markFirstMeaningfulExec(args: { sessionId: string }): Promise<void>;\n}\n\n/**\n * Read or update a session across every registered source-control partition.\n * Session ids are globally generated, so more than one match is invalid and\n * fails closed instead of mutating an arbitrary provider.\n */\nexport function createSourceControlSessionLookup(\n sourceControls: readonly SourceControlStorageHandle[],\n): SourceControlSessionLookup {\n const resolve = async (\n sessionId: string,\n ): Promise<{ sourceControl: SourceControlStorageHandle; session: SourceControlSession } | null> => {\n const matches = (\n await Promise.all(\n sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(sessionId),\n })),\n )\n ).filter(\n (match): match is { sourceControl: SourceControlStorageHandle; session: SourceControlSession } =>\n match.session !== null,\n );\n if (matches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n return matches[0] ?? null;\n };\n\n return {\n getBySessionId: async sessionId => (await resolve(sessionId))?.session ?? null,\n getSourceControlBySessionId: async sessionId => (await resolve(sessionId))?.sourceControl ?? null,\n listByOrigin: async args =>\n (await Promise.all(sourceControls.map(sourceControl => sourceControl.sessions.listByOrigin(args)))).flat(),\n rename: async args => (await resolve(args.sessionId))?.sourceControl.sessions.rename(args),\n markFirstMessage: async args => (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMessage(args),\n markFirstMeaningfulExec: async args =>\n (await resolve(args.sessionId))?.sourceControl.sessions.markFirstMeaningfulExec(args),\n };\n}\n\n/**\n * Resolve the source-control provider from durable Factory repository links,\n * never from the issue tracker that happened to create the work item.\n */\nexport async function resolveFactorySourceControl(args: {\n sourceControls: readonly SourceControlStorageHandle[];\n orgId: string;\n factoryProjectId: string;\n sessionId?: string;\n}): Promise<SourceControlStorageHandle | undefined> {\n if (args.sessionId) {\n const sessionMatches = (\n await Promise.all(\n args.sourceControls.map(async sourceControl => ({\n sourceControl,\n session: await sourceControl.sessions.getBySessionId(args.sessionId!),\n })),\n )\n ).filter(match => match.session !== null);\n if (sessionMatches.length > 1) throw new Error('Factory session exists in multiple source-control providers.');\n if (sessionMatches[0]) return sessionMatches[0].sourceControl;\n }\n\n const linked = [];\n for (const sourceControl of args.sourceControls) {\n const connections = await sourceControl.connections.list({\n orgId: args.orgId,\n factoryProjectId: args.factoryProjectId,\n });\n let hasLinkedRepository = false;\n for (const connection of connections) {\n try {\n if (\n (await sourceControl.projectRepositories.list({ orgId: args.orgId, connectionId: connection.id })).length > 0\n ) {\n hasLinkedRepository = true;\n break;\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n }\n }\n if (hasLinkedRepository) linked.push(sourceControl);\n }\n if (linked.length > 1)\n throw new FactorySourceControlConflictError(\n 'Factory project has repositories linked through multiple source-control providers.',\n );\n return linked[0];\n}\n\nexport interface EnsureFactorySourceSessionArgs {\n /**\n * Storage handle of the integration that owns source control. Nothing here is\n * provider-specific: the connection is matched by the handle's own\n * `integrationId`, so GitHub, Slack-on-behalf-of-GitHub, or any future owner\n * all resolve through the same traversal.\n */\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n branch: string;\n /**\n * The repository link the session is filed under: the one a work item\n * targets. Defaults to the factory's position-1 environment link (D1), for\n * callers where nothing names a repository (chat threads, user sessions).\n */\n projectRepositoryId?: string;\n /**\n * Attribute the run to this user instead of the repo connector. Set when the\n * run has an interactive user — e.g. the person who approved a proposed run.\n */\n attributeToUserId?: string;\n /** A pre-generated session id, so a caller can name the branch after it. Defaults to a fresh UUID. */\n sessionId?: string;\n /** Defaults to 'org'. A side session copies its parent's visibility. */\n visibility?: SourceControlSessionVisibility;\n /** The session and thread that started this one as a side session. */\n origin?: { sessionId: string; threadId: string };\n}\n\nexport interface EnsuredFactorySourceSession {\n sessionId: string;\n userId: string;\n projectRepositoryId: string;\n branch: string;\n baseBranch: string;\n}\n\nexport class FactorySourceSessionResolutionError extends Error {\n constructor(readonly reason: 'connection' | 'repository') {\n super(\n reason === 'connection'\n ? 'Factory source-control connection not found.'\n : 'Factory source-control repository not found.',\n );\n this.name = 'FactorySourceSessionResolutionError';\n }\n}\n\nexport interface ResolvedFactorySourceRepository {\n projectRepositoryId: string;\n /** The repository's pinned branch, else its default branch. */\n baseBranch: string;\n /** Who connected the repository. The attribution for runs with no interactive user. */\n connectedByUserId: string;\n}\n\n/**\n * Outcome of {@link resolvePrimaryEnvironmentRepository}. A miss carries which\n * step failed: callers differ on whether that is an error (an autonomous run\n * cannot proceed) or a routine fallback (a chat integration drops to a\n * chat-only session), and the two steps fail for different reasons worth\n * reporting apart.\n */\nexport type FactorySourceRepositoryResult =\n | ({ found: true } & ResolvedFactorySourceRepository)\n | { found: false; reason: 'connection' | 'repository' };\n\n/**\n * The repository link a new session is filed under. By default the position-1\n * `inEnvironment` link of the factory (decision D1); a work item that targets\n * a repository names its link through `projectRepositoryId`, which must be an\n * environment link of the same factory. Session start no longer picks a\n * repository to work in, the sandbox holds every environment repository. The\n * PR tools default to this link, and subscriptions, audit and the work item's\n * repository check still read it; moving them to the per-repository\n * `sessionRepositories` rows is a follow-up.\n *\n * The owner is whichever integration owns source control, matched by the\n * handle's own `integrationId` — nothing here is provider-specific.\n */\nexport async function resolvePrimaryEnvironmentRepository(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n projectRepositoryId?: string;\n}): Promise<FactorySourceRepositoryResult> {\n const { sourceControl, orgId, factoryProjectId, projectRepositoryId } = args;\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n if (!connections.some(candidate => candidate.integrationId === sourceControl.integrationId)) {\n return { found: false, reason: 'connection' };\n }\n const repositories = await resolveEnvironmentRepositories({ sourceControl, orgId, factoryProjectId });\n const primary = repositories.find(\n candidate => projectRepositoryId === undefined || candidate.link.id === projectRepositoryId,\n );\n if (!primary) return { found: false, reason: 'repository' };\n return {\n found: true,\n projectRepositoryId: primary.link.id,\n baseBranch: primary.link.branch ?? primary.repository.defaultBranch,\n connectedByUserId: primary.connection.createdByUserId,\n };\n}\n\n/**\n * Create the source-control session a repo-backed factory run needs.\n *\n * `FactoryStartCoordinator.prepare` requires this record to already exist —\n * `resolveSourceSession` throws `Factory session not found` otherwise — so every\n * autonomous entry point has to produce one before it can start a run. This is\n * that step, in one place: the owner's connection on the factory project, the\n * work item's repository link (else the position-1 environment link, D1) as\n * the session's link, and a session on the requested branch with that\n * repository's pinned or default branch as the base. The sandbox itself boots\n * every environment repository.\n *\n * The run is attributed to `attributeToUserId` when the caller has an\n * interactive user (e.g. the approver of a proposed run), and otherwise falls\n * back to whoever connected the repository (`connection.createdByUserId`),\n * because a genuinely autonomous run has no interactive user of its own.\n */\nexport async function ensureFactorySourceSession(\n args: EnsureFactorySourceSessionArgs,\n): Promise<EnsuredFactorySourceSession> {\n const { sourceControl, orgId, factoryProjectId, branch, projectRepositoryId } = args;\n\n const resolved = await resolvePrimaryEnvironmentRepository({\n sourceControl,\n orgId,\n factoryProjectId,\n projectRepositoryId,\n });\n if (!resolved.found) throw new FactorySourceSessionResolutionError(resolved.reason);\n\n const userId = args.attributeToUserId ?? resolved.connectedByUserId;\n const session = await sourceControl.sessions.create({\n sessionId: args.sessionId ?? globalThis.crypto.randomUUID(),\n projectRepositoryId: resolved.projectRepositoryId,\n factoryProjectId,\n orgId,\n userId,\n branch,\n baseBranch: resolved.baseBranch,\n visibility: args.visibility ?? 'org',\n ...(args.origin ? { origin: args.origin } : {}),\n });\n return {\n sessionId: session.sessionId,\n userId,\n projectRepositoryId: resolved.projectRepositoryId,\n branch: session.branch,\n baseBranch: resolved.baseBranch,\n };\n}\n\nexport interface HydrateFactorySessionArgs {\n orgId: string;\n modelScope: EffectiveModelScope;\n ownerUserId: string;\n /**\n * The model selected by the Factory scope. Without it the provider default of a connected\n * provider is applied when the SDK mode default has no credential, else the mode default.\n */\n defaultModelId?: string;\n}\n\n/**\n * Apply a factory project's main model to a session on every run.\n *\n * The model switch is best-effort. A retired model id must not sink a run that\n * is otherwise ready, and the reason is logged. Observational-memory settings are resolved from the\n * authoritative row per invocation before memory processors run.\n */\nexport async function hydrateFactorySession(\n session: FactorySession,\n args: HydrateFactorySessionArgs,\n): Promise<string | undefined> {\n // The org rung knowledge curation scopes on. Seeded first so it lands even if\n // a later best-effort step fails; an empty org marks the session unresolved.\n await seedSessionOrg(session, args.orgId);\n await session.state.set({\n factoryModelScope: args.modelScope,\n factoryOwnerUserId: args.ownerUserId,\n });\n\n let selectedModelId = args.defaultModelId;\n if (!selectedModelId) {\n const modeModelId = session.mode.resolve().defaultModelId;\n const providerModelId = await resolveTenantProviderDefaultModel(\n { orgId: args.orgId, userId: args.ownerUserId, credentialScope: args.modelScope },\n modeModelId,\n ).catch(error => {\n console.warn('[Factory Start] Failed to resolve a provider default model', {\n error: error instanceof Error ? error.message : String(error),\n });\n return undefined;\n });\n selectedModelId = providerModelId ?? modeModelId;\n }\n if (!selectedModelId) return undefined;\n\n let switched = true;\n try {\n await session.model.switch(selectedModelId);\n } catch (error) {\n switched = false;\n console.warn('[Factory Start] Failed to apply scoped default model', {\n modelId: selectedModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n // Subagents otherwise keep the server-wide settings (or the SDK's built-in\n // default), which may name a provider this factory has no credentials for.\n // Each role is independent, so one failure doesn't strand the others.\n for (const agentType of ['explore', 'plan', 'execute']) {\n try {\n await session.subagents.model.set({ modelId: selectedModelId, agentType });\n } catch (error) {\n console.warn('[Factory Start] Failed to apply scoped default subagent model', {\n agentType,\n modelId: selectedModelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n // Callers pin this id to the thread, so only report a model the session actually runs on.\n return switched ? selectedModelId : undefined;\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';
2
+ /**
3
+ * Stamps each assistant reply with the credential that answered it, as
4
+ * `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
5
+ * and, for a personal credential, `content.metadata.credentialOwnerId`. A
6
+ * reply without an owner id was answered by the organization's credential.
7
+ * Stamped at send time so a reply keeps its label after the thread's
8
+ * credential scope changes.
9
+ */
10
+ export declare class FactoryReplyCredentialProcessor implements Processor {
11
+ readonly id = "factory-reply-credential";
12
+ readonly name = "Factory reply credential";
13
+ processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs): import("@mastra/core/agent").MessageList;
14
+ }
15
+ //# sourceMappingURL=reply-credential-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply-credential-processor.d.ts","sourceRoot":"","sources":["../../src/session/reply-credential-processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAKhF;;;;;;;GAOG;AACH,qBAAa,+BAAgC,YAAW,SAAS;IAC/D,QAAQ,CAAC,EAAE,8BAA8B;IACzC,QAAQ,CAAC,IAAI,8BAA8B;IAE3C,iBAAiB,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,qBAAqB;CAwBzE"}
@@ -0,0 +1,40 @@
1
+ import { WEB_OAUTH_PROVIDERS, getAuthProviderId } from "../routes/provider-credentials.js";
2
+ import { providerDisplayName } from "../provider-display-name.js";
3
+ import { resolveCredentialStore, resolveTenantFromRequestContext } from "@mastra/code-sdk/agents/credential-resolver";
4
+ import { normalizeLegacyProviderId } from "@mastra/code-sdk/providers/model-ids";
5
+ //#region src/session/reply-credential-processor.ts
6
+ /**
7
+ * Stamps each assistant reply with the credential that answered it, as
8
+ * `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
9
+ * and, for a personal credential, `content.metadata.credentialOwnerId`. A
10
+ * reply without an owner id was answered by the organization's credential.
11
+ * Stamped at send time so a reply keeps its label after the thread's
12
+ * credential scope changes.
13
+ */
14
+ var FactoryReplyCredentialProcessor = class {
15
+ id = "factory-reply-credential";
16
+ name = "Factory reply credential";
17
+ processOutputStep({ messageList, requestContext }) {
18
+ const tenant = resolveTenantFromRequestContext(requestContext);
19
+ const store = resolveCredentialStore(requestContext);
20
+ if (!tenant || !store) return messageList;
21
+ for (const message of messageList.get.response.db()) {
22
+ const metadata = message.content.metadata;
23
+ if (message.role !== "assistant" || !metadata || typeof metadata.provider !== "string") continue;
24
+ if (metadata.credentialLabel !== void 0) continue;
25
+ const [sdkProviderRoot = metadata.provider] = metadata.provider.split(".");
26
+ const provider = normalizeLegacyProviderId(sdkProviderRoot);
27
+ const credential = store.get(getAuthProviderId(provider));
28
+ const subscription = WEB_OAUTH_PROVIDERS[provider]?.subscription;
29
+ const credentialLabel = credential?.type === "api_key" ? `${providerDisplayName(provider)} API key` : credential?.type === "oauth" && subscription ? subscription : void 0;
30
+ if (!credentialLabel) continue;
31
+ metadata.credentialLabel = credentialLabel;
32
+ if (tenant.credentialScope === "user") metadata.credentialOwnerId = tenant.userId;
33
+ }
34
+ return messageList;
35
+ }
36
+ };
37
+ //#endregion
38
+ export { FactoryReplyCredentialProcessor };
39
+
40
+ //# sourceMappingURL=reply-credential-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply-credential-processor.js","names":[],"sources":["../../src/session/reply-credential-processor.ts"],"sourcesContent":["import { resolveCredentialStore, resolveTenantFromRequestContext } from '@mastra/code-sdk/agents/credential-resolver';\nimport { normalizeLegacyProviderId } from '@mastra/code-sdk/providers/model-ids';\nimport type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';\n\nimport { providerDisplayName } from '../provider-display-name.js';\nimport { getAuthProviderId, WEB_OAUTH_PROVIDERS } from '../routes/provider-credentials.js';\n\n/**\n * Stamps each assistant reply with the credential that answered it, as\n * `content.metadata.credentialLabel` (\"Claude Pro/Max\", \"Anthropic API key\")\n * and, for a personal credential, `content.metadata.credentialOwnerId`. A\n * reply without an owner id was answered by the organization's credential.\n * Stamped at send time so a reply keeps its label after the thread's\n * credential scope changes.\n */\nexport class FactoryReplyCredentialProcessor implements Processor {\n readonly id = 'factory-reply-credential';\n readonly name = 'Factory reply credential';\n\n processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs) {\n const tenant = resolveTenantFromRequestContext(requestContext);\n const store = resolveCredentialStore(requestContext);\n if (!tenant || !store) return messageList;\n for (const message of messageList.get.response.db()) {\n const metadata = message.content.metadata;\n if (message.role !== 'assistant' || !metadata || typeof metadata.provider !== 'string') continue;\n if (metadata.credentialLabel !== undefined) continue;\n const [sdkProviderRoot = metadata.provider] = metadata.provider.split('.');\n const provider = normalizeLegacyProviderId(sdkProviderRoot);\n const credential = store.get(getAuthProviderId(provider));\n const subscription = WEB_OAUTH_PROVIDERS[provider]?.subscription;\n const credentialLabel =\n credential?.type === 'api_key'\n ? `${providerDisplayName(provider)} API key`\n : credential?.type === 'oauth' && subscription\n ? subscription\n : undefined;\n if (!credentialLabel) continue;\n metadata.credentialLabel = credentialLabel;\n if (tenant.credentialScope === 'user') metadata.credentialOwnerId = tenant.userId;\n }\n return messageList;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,IAAa,kCAAb,MAAkE;CAChE,KAAc;CACd,OAAgB;CAEhB,kBAAkB,EAAE,aAAa,kBAAyC;EACxE,MAAM,SAAS,gCAAgC,cAAc;EAC7D,MAAM,QAAQ,uBAAuB,cAAc;EACnD,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO;EAC9B,KAAK,MAAM,WAAW,YAAY,IAAI,SAAS,GAAG,GAAG;GACnD,MAAM,WAAW,QAAQ,QAAQ;GACjC,IAAI,QAAQ,SAAS,eAAe,CAAC,YAAY,OAAO,SAAS,aAAa,UAAU;GACxF,IAAI,SAAS,oBAAoB,KAAA,GAAW;GAC5C,MAAM,CAAC,kBAAkB,SAAS,YAAY,SAAS,SAAS,MAAM,GAAG;GACzE,MAAM,WAAW,0BAA0B,eAAe;GAC1D,MAAM,aAAa,MAAM,IAAI,kBAAkB,QAAQ,CAAC;GACxD,MAAM,eAAe,oBAAoB,SAAS,EAAE;GACpD,MAAM,kBACJ,YAAY,SAAS,YACjB,GAAG,oBAAoB,QAAQ,EAAE,YACjC,YAAY,SAAS,WAAW,eAC9B,eACA,KAAA;GACR,IAAI,CAAC,iBAAiB;GACtB,SAAS,kBAAkB;GAC3B,IAAI,OAAO,oBAAoB,QAAQ,SAAS,oBAAoB,OAAO;EAC7E;EACA,OAAO;CACT;AACF"}
@@ -16,10 +16,28 @@ export declare function verdictEventMismatch(event: ReviewEvent, body: string |
16
16
  * disagree, or `undefined` when absent.
17
17
  */
18
18
  export declare function reviewedHeadFromBody(body: string | undefined): string | null | undefined;
19
- export declare function createSourceControlTools({ requestContext, providers, audit, }: {
19
+ /** Fixed server-side so an agent cannot publish under, and impersonate, another required check. */
20
+ export declare const FACTORY_TEST_CHECK_NAME = "Factory Review / tests";
21
+ export declare function createSourceControlTools({ requestContext, providers, audit, testChecksEnabled, selfReviewApprovedHead, boundChangeRequestId, }: {
20
22
  requestContext: RequestContext;
21
23
  providers: readonly SourceControlToolProvider[];
22
24
  audit: AuditAgentEmitter;
25
+ /** Per-project switch for `source_control_publish_test_check`. The host decides the default; Factory projects default to off. */
26
+ testChecksEnabled?: (input: {
27
+ orgId: string;
28
+ factoryProjectId: string;
29
+ }) => Promise<boolean>;
30
+ /**
31
+ * Self-review gate for opening a change request. `undefined` means the session
32
+ * is not gated (no Work item, or its change request predates Self-review);
33
+ * `null` means Self-review has not approved; a SHA is the approved head.
34
+ */
35
+ selfReviewApprovedHead?: () => Promise<string | null | undefined>;
36
+ /**
37
+ * The change request a Review-bound session reviews; test checks may only target it.
38
+ * When provided, sessions that are not Review-bound (it resolves `undefined`) cannot publish.
39
+ */
40
+ boundChangeRequestId?: () => Promise<string | undefined>;
23
41
  }): Promise<IntegrationTools>;
24
42
  export {};
25
43
  //# sourceMappingURL=source-control-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"source-control-tools.d.ts","sourceRoot":"","sources":["../../src/session/source-control-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAUhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAIV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AAUnD,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,0BAA0B,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;CAChC;AAcD,KAAK,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE7D,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAUrG;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CASxF;AAuJD,wBAAsB,wBAAwB,CAAC,EAC7C,cAAc,EACd,SAAS,EACT,KAAK,GACN,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAChD,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8c5B"}
1
+ {"version":3,"file":"source-control-tools.d.ts","sourceRoot":"","sources":["../../src/session/source-control-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAUhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAIV,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AAUnD,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,0BAA0B,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;CAChC;AAcD,KAAK,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE7D,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAUrG;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CASxF;AAkJD,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;AAOhE,wBAAsB,wBAAwB,CAAC,EAC7C,cAAc,EACd,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,GACrB,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAChD,KAAK,EAAE,iBAAiB,CAAC;IACzB,iIAAiI;IACjI,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7F;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAClE;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1D,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqiB5B"}
@@ -144,9 +144,11 @@ async function emitAgentAudit(audit, requestContext, input) {
144
144
  });
145
145
  }
146
146
  }
147
+ /** Fixed server-side so an agent cannot publish under, and impersonate, another required check. */
148
+ const FACTORY_TEST_CHECK_NAME = "Factory Review / tests";
147
149
  const repositorySchema = z.object({ repository: z.string().trim().min(1).optional() });
148
150
  const changeRequestSchema = repositorySchema.extend({ changeRequestId: z.union([z.string().trim().min(1), z.number().int().positive()]) });
149
- async function createSourceControlTools({ requestContext, providers, audit }) {
151
+ async function createSourceControlTools({ requestContext, providers, audit, testChecksEnabled, selfReviewApprovedHead, boundChangeRequestId }) {
150
152
  if (!requestContext || typeof requestContext.get !== "function") return {};
151
153
  const controller = requestContext.get("controller");
152
154
  const identity = authIdentity(requestContext);
@@ -264,8 +266,10 @@ async function createSourceControlTools({ requestContext, providers, audit }) {
264
266
  body: z.string().optional(),
265
267
  draft: z.boolean().optional()
266
268
  }),
267
- execute: async (input) => {
269
+ execute: async (input, execution) => {
268
270
  const target = await targetFor(input);
271
+ const approvedHead = target.ownRepository ? await selfReviewApprovedHead?.() : void 0;
272
+ if (approvedHead === null) throw new Error("Self-review has not approved this branch. Move the work item to Self-review; the change request opens after it approves.");
269
273
  const baseBranch = target.ownRepository ? target.session.baseBranch : target.projectRepository.branch ?? target.repository.defaultBranch;
270
274
  const created = await target.provider.versionControl.createPullRequest({
271
275
  ...await reference(target),
@@ -275,6 +279,13 @@ async function createSourceControlTools({ requestContext, providers, audit }) {
275
279
  headBranch: target.session.branch,
276
280
  ...input.draft !== void 0 ? { draft: input.draft } : {}
277
281
  });
282
+ if (approvedHead !== void 0 && created.headSha.toLowerCase() !== approvedHead.toLowerCase()) {
283
+ await target.provider.versionControl.closePullRequest({
284
+ ...await reference(target),
285
+ pullRequestId: created.id
286
+ });
287
+ throw new Error(`Self-review approved ${approvedHead}, but the pushed branch is at ${created.headSha}; the change request was closed. Push the approved head, or move the work item back to Self-review to review the new commits.`);
288
+ }
278
289
  await target.provider.storage.sessionRepositories.upsert({
279
290
  sessionId: target.session.sessionId,
280
291
  projectRepositoryId: target.projectRepository.id,
@@ -546,6 +557,62 @@ async function createSourceControlTools({ requestContext, providers, audit }) {
546
557
  });
547
558
  }
548
559
  }),
560
+ source_control_publish_test_check: createTool({
561
+ id: "source_control_publish_test_check",
562
+ description: `Publish the Factory review test result as a "${FACTORY_TEST_CHECK_NAME}" commit check on the change request's current head. Publish "in-progress" before running the test plan and a final status after. Refused when headSha is not the current head. ${repositoryArgument}`,
563
+ inputSchema: changeRequestSchema.extend({
564
+ headSha: z.string().trim().regex(/^([0-9a-f]{40}|[0-9a-f]{64})$/i, "headSha must be a full 40- or 64-character commit SHA."),
565
+ status: z.enum([
566
+ "in-progress",
567
+ "success",
568
+ "failure",
569
+ "neutral",
570
+ "cancelled",
571
+ "skipped"
572
+ ]),
573
+ title: z.string().trim().min(1),
574
+ summary: z.string().trim().min(1),
575
+ details: z.string().optional()
576
+ }),
577
+ execute: async (input) => {
578
+ const target = await targetFor(input);
579
+ if (testChecksEnabled) {
580
+ const connection = await target.provider.storage.connections.get({
581
+ orgId: target.orgId,
582
+ id: target.projectRepository.connectionId
583
+ });
584
+ if (connection?.factoryProjectId && !await testChecksEnabled({
585
+ orgId: target.orgId,
586
+ factoryProjectId: connection.factoryProjectId
587
+ })) throw new Error("Test check publishing is turned off for this Factory project; nothing was published. Report the test results in the test report comment instead.");
588
+ }
589
+ const publishCommitCheck = target.provider.versionControl.publishCommitCheck;
590
+ if (!publishCommitCheck) throw new Error(`${target.provider.id} does not support publishing commit checks; nothing was published.`);
591
+ const bound = boundChangeRequestId ? await boundChangeRequestId() : void 0;
592
+ if (bound === void 0) throw new Error("Only a Review session bound to this change request can publish test checks; nothing was published.");
593
+ if (bound !== changeRequestId(input.changeRequestId)) throw new Error(`This session reviews change request ${bound}, not ${input.changeRequestId}; nothing was published.`);
594
+ const ref = await reference(target);
595
+ const pullRequest = await target.provider.versionControl.getPullRequest({
596
+ ...ref,
597
+ pullRequestId: changeRequestId(input.changeRequestId)
598
+ });
599
+ if (!pullRequest || pullRequest.state !== "open") throw new Error(`Change request ${input.changeRequestId} is not open; nothing was published.`);
600
+ const headSha = input.headSha.toLowerCase();
601
+ if (pullRequest.headSha.toLowerCase() !== headSha) throw new Error(`headSha ${headSha} is not the current change-request head ${pullRequest.headSha}. Nothing was published. Re-run the test plan on the current head.`);
602
+ return publishCommitCheck({
603
+ connection: ref.connection,
604
+ sourceId: ref.sourceId,
605
+ actingUserId: ref.actingUserId,
606
+ headSha,
607
+ ref: pullRequest.headBranch,
608
+ name: FACTORY_TEST_CHECK_NAME,
609
+ status: input.status,
610
+ title: input.title,
611
+ summary: input.summary,
612
+ ...input.details !== void 0 ? { details: input.details } : {}
613
+ });
614
+ }
615
+ }),
549
616
  source_control_request_reviewers: createTool({
550
617
  id: "source_control_request_reviewers",
551
618
  description: `Request individual or team reviewers on a pull request or merge request. Provider limitations are returned explicitly. ${repositoryArgument}`,
@@ -589,6 +656,6 @@ async function createSourceControlTools({ requestContext, providers, audit }) {
589
656
  };
590
657
  }
591
658
  //#endregion
592
- export { createSourceControlTools, reviewedHeadFromBody, verdictEventMismatch };
659
+ export { FACTORY_TEST_CHECK_NAME, createSourceControlTools, reviewedHeadFromBody, verdictEventMismatch };
593
660
 
594
661
  //# sourceMappingURL=source-control-tools.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"source-control-tools.js","names":[],"sources":["../../src/session/source-control-tools.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport { z } from 'zod';\n\nimport { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from '../auth.js';\nimport type { VersionControl } from '../capabilities/version-control.js';\nimport type { IntegrationTools } from '../integrations/base.js';\nimport {\n ensureLocalBranch,\n isCheckoutOf,\n pushRepositoryBranch,\n refreshMergeRequestCheckout,\n} from '../integrations/github/sandbox.js';\nimport { normalizedVerdictLine } from '../review-verdict.js';\nimport type { ExecutableSandbox } from '../sandbox/materialization.js';\nimport { resolveSessionWorkdir } from '../sandbox/session-sandbox.js';\nimport type { AuditAgentEmitter } from '../storage/domains/audit/domain.js';\nimport type {\n ProjectRepository,\n SourceControlRepository,\n SourceControlSession,\n SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { mergeRequestNumberFromBranch } from '../work-item-branch.js';\nimport { environmentSlugs, findEnvironmentRepository, resolveSessionRepositories } from './environment-repositories.js';\nimport { updateSessionEnvironmentRepository } from './environment-state-processor.js';\n\ntype RepositorySessionState = {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n};\n\nexport interface SourceControlToolProvider {\n id: string;\n storage: SourceControlStorageHandle;\n versionControl: VersionControl;\n}\n\ninterface SessionTarget {\n context: AgentControllerRequestContext<RepositorySessionState>;\n provider: SourceControlToolProvider;\n session: SourceControlSession;\n projectRepository: ProjectRepository;\n repository: SourceControlRepository;\n orgId: string;\n userId: string;\n /** True when the target is the link the session is filed under, the default. */\n ownRepository: boolean;\n}\n\ntype ReviewEvent = 'approve' | 'request-changes' | 'comment';\n\n/** Returns why a review's leading `Verdict:` line contradicts the submitted event, if it does. */\nexport function verdictEventMismatch(event: ReviewEvent, body: string | undefined): string | undefined {\n const match = /^verdict: ?(approve|request changes|changes requested)\\b/.exec(normalizedVerdictLine(body) ?? '');\n if (!match) return undefined;\n const verdict = match[1] === 'approve' ? 'approve' : 'request changes';\n // GitLab has no request-changes state, so a blocking verdict is published as a comment review.\n const allowed: ReviewEvent[] = verdict === 'approve' ? ['approve'] : ['request-changes', 'comment'];\n if (allowed.includes(event)) return undefined;\n return `Review body opens with \"Verdict: ${verdict}\" but event is \"${event}\". Nothing was posted. Use event ${allowed\n .map(e => `\"${e}\"`)\n .join(' or ')} for this body, or regenerate the body for the verdict you intend.`;\n}\n\nconst REVIEWED_HEAD_LINE = /^[\\s*_>-]*reviewed head[*_]*:[*_]*\\s*(.*)$/gim;\nconst FULL_SHA = /^`?([0-9a-f]{40}|[0-9a-f]{64})`?(?:\\s|$)/i;\n\n/**\n * Returns the full SHA named by the body's `Reviewed head:` lines, `null` when any line is malformed or the lines\n * disagree, or `undefined` when absent.\n */\nexport function reviewedHeadFromBody(body: string | undefined): string | null | undefined {\n const shas = new Set(\n [...(body ?? '').matchAll(REVIEWED_HEAD_LINE)].map(\n line => FULL_SHA.exec(line[1]!.trim())?.[1]?.toLowerCase() ?? null,\n ),\n );\n if (shas.size === 0) return undefined;\n if (shas.size > 1) return null;\n return [...shas][0];\n}\n\nfunction authIdentity(requestContext: RequestContext) {\n const user = getFactoryAuthUserFromContext(requestContext);\n return { orgId: getFactoryAuthOrgId(user), userId: getFactoryAuthUserId(user) };\n}\n\nasync function resolveProviderSession(\n resourceId: string,\n providers: readonly SourceControlToolProvider[],\n): Promise<{ provider: SourceControlToolProvider; session: SourceControlSession }> {\n const matches = (\n await Promise.all(\n providers.map(async provider => ({\n provider,\n session: await provider.storage.sessions.getBySessionId(resourceId),\n })),\n )\n ).filter(\n (match): match is { provider: SourceControlToolProvider; session: SourceControlSession } => match.session !== null,\n );\n if (matches.length !== 1) {\n throw new Error(\n matches.length === 0\n ? 'The active session is not backed by a source-control repository.'\n : 'The active session is ambiguous across source-control providers.',\n );\n }\n return matches[0]!;\n}\n\nasync function resolveSessionTarget(\n requestContext: RequestContext,\n providers: readonly SourceControlToolProvider[],\n options: { repository?: string } = {},\n): Promise<SessionTarget> {\n const context = requestContext.get('controller') as AgentControllerRequestContext<RepositorySessionState> | undefined;\n const { orgId, userId } = authIdentity(requestContext);\n if (!context?.resourceId || !orgId || !userId) {\n throw new Error('Source-control tools require an authenticated repository session.');\n }\n\n const { provider, session } = await resolveProviderSession(context.resourceId, providers);\n if (session.orgId !== orgId || (session.visibility === 'private' && session.userId !== userId)) {\n throw new Error('The active source-control session is not available to the authenticated user.');\n }\n const ownRepository = session.projectRepositoryId\n ? await provider.storage.projectRepositories.get({ orgId, id: session.projectRepositoryId })\n : null;\n if (!ownRepository) throw new Error('The active project repository was not found.');\n const state = context.getState();\n if (state.projectRepositoryId && state.projectRepositoryId !== ownRepository.id) {\n throw new Error('The active source-control session does not match its controller repository.');\n }\n const ownConnection = await provider.storage.connections.get({ orgId, id: ownRepository.connectionId });\n if (!ownConnection) throw new Error('The active source-control connection was not found.');\n if (state.factoryProjectId && state.factoryProjectId !== ownConnection.factoryProjectId) {\n throw new Error('The active source-control session does not match its Factory project.');\n }\n\n if (options.repository !== undefined) {\n const candidates = await resolveSessionRepositories({ sourceControl: provider.storage, session });\n const match = findEnvironmentRepository(candidates, options.repository);\n if (!match) {\n throw new Error(\n `Repository '${options.repository}' is not in this Factory's environment. Valid repositories: ${environmentSlugs(candidates).join(', ')}.`,\n );\n }\n return {\n context,\n provider,\n session,\n projectRepository: match.link,\n repository: match.repository,\n orgId,\n userId,\n ownRepository: match.link.id === ownRepository.id,\n };\n }\n\n const repository = await provider.storage.repositories.get({ orgId, id: ownRepository.repositoryId });\n if (!repository) throw new Error('The active source-control repository was not found.');\n return {\n context,\n provider,\n session,\n projectRepository: ownRepository,\n repository,\n orgId,\n userId,\n ownRepository: true,\n };\n}\n\n/**\n * The slugs a session may target and the one it is filed under, for the tool\n * descriptions. `undefined` when the session cannot be resolved; the tools\n * then describe the default without naming it.\n */\nasync function describeSessionRepositories(\n resourceId: string,\n providers: readonly SourceControlToolProvider[],\n): Promise<{ slugs: string[]; own: string | undefined } | undefined> {\n try {\n const { provider, session } = await resolveProviderSession(resourceId, providers);\n const repositories = await resolveSessionRepositories({ sourceControl: provider.storage, session });\n const own = repositories.find(candidate => candidate.link.id === session.projectRepositoryId);\n return { slugs: environmentSlugs(repositories), own: own?.repository.slug };\n } catch {\n return undefined;\n }\n}\n\nfunction repositoryArgumentDescription(described: { slugs: string[]; own: string | undefined } | undefined): string {\n if (!described || described.slugs.length === 0) {\n return \"Optional `repository` (owner/name) selects another repository of this Factory's environment; defaults to this session's repository.\";\n }\n const fallback = described.own ? `${described.own}, this session's repository` : \"this session's repository\";\n return `Optional \\`repository\\` (owner/name) selects one of this Factory's environment repositories: ${described.slugs.join(', ')}. Defaults to ${fallback}.`;\n}\n\nfunction changeRequestId(value: string | number): string {\n return String(value);\n}\n\nfunction executableSandbox(value: unknown): WorkspaceSandbox & ExecutableSandbox {\n const sandbox = value as Partial<ExecutableSandbox> | undefined;\n if (!sandbox?.executeCommand) throw new Error('The active Factory workspace has no executable sandbox.');\n return sandbox as WorkspaceSandbox & ExecutableSandbox;\n}\n\nasync function emitAgentAudit(\n audit: AuditAgentEmitter,\n requestContext: RequestContext,\n input: Parameters<AuditAgentEmitter['emitAgent']>[0]['input'],\n): Promise<void> {\n try {\n await audit.emitAgent({ requestContext, input });\n } catch (error) {\n console.warn('[Audit] Failed to record brokered source-control action', {\n action: input.action,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nconst repositorySchema = z.object({ repository: z.string().trim().min(1).optional() });\nconst changeRequestSchema = repositorySchema.extend({\n changeRequestId: z.union([z.string().trim().min(1), z.number().int().positive()]),\n});\n\nexport async function createSourceControlTools({\n requestContext,\n providers,\n audit,\n}: {\n requestContext: RequestContext;\n providers: readonly SourceControlToolProvider[];\n audit: AuditAgentEmitter;\n}): Promise<IntegrationTools> {\n if (!requestContext || typeof (requestContext as { get?: unknown }).get !== 'function') return {};\n const controller = requestContext.get('controller') as\n | AgentControllerRequestContext<RepositorySessionState>\n | undefined;\n const identity = authIdentity(requestContext);\n if (!controller?.resourceId || !identity.orgId || !identity.userId || providers.length === 0) return {};\n\n const repositoryArgument = repositoryArgumentDescription(\n await describeSessionRepositories(controller.resourceId, providers),\n );\n const withTarget = (options?: { repository?: string }) => resolveSessionTarget(requestContext, providers, options);\n const targetFor = (input: { repository?: string }) =>\n withTarget(input.repository !== undefined ? { repository: input.repository } : undefined);\n const reference = async (target: SessionTarget) => ({\n ...(await target.provider.versionControl.getRepositoryTarget({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n })),\n actingUserId: target.userId,\n });\n\n return {\n source_control_refresh_change_request_checkout: createTool({\n id: 'source_control_refresh_change_request_checkout',\n description:\n 'Refresh the active GitLab review session checkout to its current MR head. The MR, repository, branch, and credential are resolved server-side; no provider credentials enter the agent workspace.',\n inputSchema: z.object({}),\n execute: async (_input, { workspace }) => {\n const target = await withTarget();\n const mergeRequestNumber = mergeRequestNumberFromBranch(target.session.branch);\n if (target.provider.id !== 'gitlab' || mergeRequestNumber === undefined) {\n throw new Error('Checkout refresh is only available in a bound GitLab merge-request review session.');\n }\n const ref = await reference(target);\n const mergeRequest = await target.provider.versionControl.getPullRequest({\n ...ref,\n pullRequestId: String(mergeRequestNumber),\n });\n if (!mergeRequest || mergeRequest.state !== 'open' || mergeRequest.merged) {\n throw new Error('The bound GitLab merge request is no longer open.');\n }\n const sandbox = executableSandbox(workspace?.sandbox);\n const workdir = await resolveSessionWorkdir(target.session.id, sandbox, target.repository.slug);\n const access = await target.provider.versionControl.getRepositoryAccess({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n });\n const result = await refreshMergeRequestCheckout(sandbox, workdir, {\n branch: target.session.branch,\n mergeRequestNumber,\n expectedHeadSha: mergeRequest.headSha,\n access,\n });\n return result;\n },\n }),\n source_control_push_branch: createTool({\n id: 'source_control_push_branch',\n description: `Push the active Factory session branch to its connected source-control provider. Credentials are resolved and scrubbed server-side; this tool takes no token, remote, or branch arguments. ${repositoryArgument}`,\n inputSchema: repositorySchema,\n execute: async (input, { workspace }) => {\n const target = await targetFor(input);\n const sandbox = executableSandbox(workspace?.sandbox);\n const workdir = await resolveSessionWorkdir(target.session.id, sandbox, target.repository.slug);\n const access = await target.provider.versionControl.getRepositoryAccess({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n });\n // Another environment repository's directory may not be its checkout:\n // a boot skips a link whose directory name collides with another's, and\n // a session that booted single-repository has no siblings at all.\n if (!target.ownRepository && !(await isCheckoutOf(sandbox, workdir, access.cloneUrl))) {\n throw new Error(`There is no checkout of ${target.repository.slug} in this session.`);\n }\n const branch = target.session.branch;\n // Push the local session branch, whatever is checked out. A repository\n // where it was never created gets it at HEAD first, so a later push\n // never sends some other branch's commits.\n await ensureLocalBranch(sandbox, workdir, branch);\n await pushRepositoryBranch(sandbox, workdir, branch, access, target.repository.slug);\n await target.provider.storage.sessionRepositories.upsert({\n sessionId: target.session.sessionId,\n projectRepositoryId: target.projectRepository.id,\n branch,\n });\n updateSessionEnvironmentRepository(target.session.sessionId, target.repository.slug, { branch });\n await emitAgentAudit(audit, requestContext, {\n action: 'factory.agent.push',\n targets: [{ type: 'repository', id: target.repository.slug }],\n metadata: { branch, provider: target.provider.id, repository: target.repository.slug },\n });\n return { pushed: true, branch, repository: target.repository.slug };\n },\n }),\n source_control_get_change_request: createTool({\n id: 'source_control_get_change_request',\n description: `Read a pull request or merge request in the active Factory repository by its numeric repository-local ID. ${repositoryArgument}`,\n inputSchema: changeRequestSchema,\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.getPullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n });\n },\n }),\n source_control_list_change_request_reviews: createTool({\n id: 'source_control_list_change_request_reviews',\n description: `List submitted reviews and approvals on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listReviews({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_create_change_request: createTool({\n id: 'source_control_create_change_request',\n description: `Open a pull request or merge request from the active Factory session branch into the repository's base branch (the session's persisted base branch in its own repository, the linked or default branch elsewhere). Push the branch with source_control_push_branch first. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({\n title: z.string().trim().min(1),\n body: z.string().optional(),\n draft: z.boolean().optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n const baseBranch = target.ownRepository\n ? target.session.baseBranch\n : (target.projectRepository.branch ?? target.repository.defaultBranch);\n const created = await target.provider.versionControl.createPullRequest({\n ...(await reference(target)),\n title: input.title,\n ...(input.body !== undefined ? { body: input.body } : {}),\n baseBranch,\n headBranch: target.session.branch,\n ...(input.draft !== undefined ? { draft: input.draft } : {}),\n });\n await target.provider.storage.sessionRepositories.upsert({\n sessionId: target.session.sessionId,\n projectRepositoryId: target.projectRepository.id,\n // Opening a change request pushes nothing: an existing row keeps its\n // `pushedAt`; the branch only seeds a row no push wrote yet.\n fallbackBranch: target.session.branch,\n changeRequestId: changeRequestId(created.id),\n changeRequestUrl: created.url,\n });\n updateSessionEnvironmentRepository(target.session.sessionId, target.repository.slug, {\n changeRequestUrl: created.url,\n });\n await emitAgentAudit(audit, requestContext, {\n action: 'factory.agent.pr_opened',\n targets: [\n { type: 'pull_request', id: created.url },\n { type: 'repository', id: target.repository.slug },\n ],\n metadata: { url: created.url, provider: target.provider.id, repository: target.repository.slug },\n });\n return created;\n },\n }),\n source_control_update_change_request: createTool({\n id: 'source_control_update_change_request',\n description: `Update the title, body, target branch, or open/closed state of a change request. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n title: z.string().trim().min(1).optional(),\n body: z.string().nullable().optional(),\n baseBranch: z.string().trim().min(1).optional(),\n state: z.enum(['open', 'closed']).optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updatePullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.title !== undefined ? { title: input.title } : {}),\n ...(input.body !== undefined ? { body: input.body } : {}),\n ...(input.baseBranch !== undefined ? { baseBranch: input.baseBranch } : {}),\n ...(input.state !== undefined ? { state: input.state } : {}),\n });\n },\n }),\n source_control_comment_change_request: createTool({\n id: 'source_control_comment_change_request',\n description: `Add a top-level comment to a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.createComment({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n body: input.body,\n });\n },\n }),\n source_control_list_change_request_comments: createTool({\n id: 'source_control_list_change_request_comments',\n description: `List top-level comments on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listComments({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_update_change_request_comment: createTool({\n id: 'source_control_update_change_request_comment',\n description: `Edit a top-level pull-request or merge-request comment in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1), body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updateComment({\n ...(await reference(target)),\n commentId: input.commentId,\n body: input.body,\n });\n },\n }),\n source_control_delete_change_request_comment: createTool({\n id: 'source_control_delete_change_request_comment',\n description: `Delete a top-level pull-request or merge-request comment in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n await target.provider.versionControl.deleteComment({\n ...(await reference(target)),\n commentId: input.commentId,\n });\n return { deleted: true };\n },\n }),\n source_control_list_diff_comments: createTool({\n id: 'source_control_list_diff_comments',\n description: `List line-anchored review comments and discussion replies on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listReviewComments({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_create_diff_comment: createTool({\n id: 'source_control_create_diff_comment',\n description: `Create a line-anchored review comment or reply to an existing diff discussion. Use replyToId alone for a reply; otherwise provide commitId, path, line, and side. ${repositoryArgument}`,\n // A root-level union is not representable as provider function parameters, which must be\n // an object schema, so both modes share one object shape and a refinement.\n inputSchema: changeRequestSchema\n .extend({\n body: z.string().trim().min(1),\n replyToId: z.string().trim().min(1).optional(),\n commitId: z.string().trim().min(1).optional(),\n path: z.string().trim().min(1).optional(),\n line: z.number().int().positive().optional(),\n side: z.enum(['left', 'right']).optional(),\n startLine: z.number().int().positive().optional(),\n startSide: z.enum(['left', 'right']).optional(),\n })\n .refine(\n input => {\n const anchorFields = [input.commitId, input.path, input.line, input.side];\n const anchor = anchorFields.every(field => field !== undefined);\n const reply = input.replyToId !== undefined;\n return (\n (reply ? anchorFields.every(field => field === undefined) : anchor) &&\n (input.startLine === undefined) === (input.startSide === undefined)\n );\n },\n {\n message:\n 'Provide replyToId alone to reply to a diff thread, or commitId, path, line, and side to start one. startLine and startSide must be provided together.',\n },\n ),\n execute: async input => {\n const target = await targetFor(input);\n const base = {\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n body: input.body,\n };\n if (input.replyToId !== undefined) {\n return target.provider.versionControl.createReviewComment({ ...base, replyToId: input.replyToId });\n }\n if (\n input.commitId === undefined ||\n input.path === undefined ||\n input.line === undefined ||\n input.side === undefined\n ) {\n throw new Error('commitId, path, line, and side are required to start a new diff thread.');\n }\n return target.provider.versionControl.createReviewComment({\n ...base,\n commitId: input.commitId,\n path: input.path,\n line: input.line,\n side: input.side,\n ...(input.startLine !== undefined && input.startSide !== undefined\n ? { startLine: input.startLine, startSide: input.startSide }\n : {}),\n });\n },\n }),\n source_control_update_diff_comment: createTool({\n id: 'source_control_update_diff_comment',\n description: `Edit a line-anchored review comment or discussion reply in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1), body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updateReviewComment({\n ...(await reference(target)),\n commentId: input.commentId,\n body: input.body,\n });\n },\n }),\n source_control_delete_diff_comment: createTool({\n id: 'source_control_delete_diff_comment',\n description: `Delete a line-anchored review comment or discussion reply in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n await target.provider.versionControl.deleteReviewComment({\n ...(await reference(target)),\n commentId: input.commentId,\n });\n return { deleted: true };\n },\n }),\n source_control_resolve_diff_thread: createTool({\n id: 'source_control_resolve_diff_thread',\n description: `Resolve or reopen the review thread containing a diff comment. Providers without resolvable threads return an explicit limitation. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({\n commentId: z.string().trim().min(1),\n resolved: z.boolean().default(true),\n }),\n execute: async input => {\n const target = await targetFor(input);\n const resolveReviewThread = target.provider.versionControl.resolveReviewThread;\n if (!resolveReviewThread) {\n throw new Error(`${target.provider.id} does not support resolving review threads.`);\n }\n await resolveReviewThread({\n ...(await reference(target)),\n commentId: input.commentId,\n resolved: input.resolved,\n });\n return { resolved: input.resolved };\n },\n }),\n source_control_review_change_request: createTool({\n id: 'source_control_review_change_request',\n description: `Submit an approve, request-changes, or comment review to a change request. Provider limitations are returned explicitly. ${repositoryArgument}`,\n inputSchema: changeRequestSchema\n .extend({\n event: z.enum(['approve', 'request-changes', 'comment']),\n body: z.string().optional(),\n commitId: z.string().trim().min(1).optional(),\n })\n .refine(input => input.event === 'approve' || Boolean(input.body?.trim()), {\n path: ['body'],\n message: 'request-changes and comment reviews require a body.',\n })\n .superRefine((input, ctx) => {\n const mismatch = verdictEventMismatch(input.event, input.body);\n if (mismatch) ctx.addIssue({ code: 'custom', path: ['body'], message: mismatch });\n const reviewedHead = reviewedHeadFromBody(input.body);\n if (reviewedHead === null) {\n ctx.addIssue({\n code: 'custom',\n path: ['body'],\n message: 'Every \"Reviewed head:\" line must name the same full 40- or 64-character commit SHA.',\n });\n } else if (reviewedHead && input.commitId && input.commitId.toLowerCase() !== reviewedHead) {\n ctx.addIssue({\n code: 'custom',\n path: ['commitId'],\n message: `Review body says \"Reviewed head: ${reviewedHead}\" but commitId is ${input.commitId}. Nothing was posted. Re-review the current head and regenerate the body before publishing.`,\n });\n }\n }),\n execute: async input => {\n const target = await targetFor(input);\n const reviewedHead = reviewedHeadFromBody(input.body);\n if (reviewedHead) {\n const pullRequest = await target.provider.versionControl.getPullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n });\n if (!pullRequest) {\n throw new Error(`Change request ${input.changeRequestId} was not found; nothing was posted.`);\n }\n if (pullRequest.headSha && pullRequest.headSha.toLowerCase() !== reviewedHead) {\n throw new Error(\n `Review body says \"Reviewed head: ${reviewedHead}\" but the current change-request head is ${pullRequest.headSha}. Nothing was posted. Re-review the current head and regenerate the body before publishing.`,\n );\n }\n }\n const base = {\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.commitId !== undefined\n ? { commitId: input.commitId }\n : reviewedHead\n ? { commitId: reviewedHead }\n : {}),\n };\n if (input.event === 'approve') {\n return target.provider.versionControl.createReview({\n ...base,\n event: 'approve',\n ...(input.body !== undefined ? { body: input.body } : {}),\n });\n }\n return target.provider.versionControl.createReview({ ...base, event: input.event, body: input.body! });\n },\n }),\n source_control_request_reviewers: createTool({\n id: 'source_control_request_reviewers',\n description: `Request individual or team reviewers on a pull request or merge request. Provider limitations are returned explicitly. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n users: z.array(z.string().trim().min(1)).default([]),\n teams: z.array(z.string().trim().min(1)).default([]),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.requestReviewers({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n users: input.users,\n teams: input.teams,\n });\n },\n }),\n source_control_merge_change_request: createTool({\n id: 'source_control_merge_change_request',\n description: `Merge a pull request or merge request using the requested provider-supported merge method. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n method: z.enum(['merge', 'squash', 'rebase']).optional(),\n commitTitle: z.string().optional(),\n commitMessage: z.string().optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.mergePullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.method !== undefined ? { method: input.method } : {}),\n ...(input.commitTitle !== undefined ? { commitTitle: input.commitTitle } : {}),\n ...(input.commitMessage !== undefined ? { commitMessage: input.commitMessage } : {}),\n });\n },\n }),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"source-control-tools.js","names":[],"sources":["../../src/session/source-control-tools.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport { z } from 'zod';\n\nimport { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from '../auth.js';\nimport type { VersionControl } from '../capabilities/version-control.js';\nimport type { IntegrationTools } from '../integrations/base.js';\nimport {\n ensureLocalBranch,\n isCheckoutOf,\n pushRepositoryBranch,\n refreshMergeRequestCheckout,\n} from '../integrations/github/sandbox.js';\nimport { normalizedVerdictLine } from '../review-verdict.js';\nimport type { ExecutableSandbox } from '../sandbox/materialization.js';\nimport { resolveSessionWorkdir } from '../sandbox/session-sandbox.js';\nimport type { AuditAgentEmitter } from '../storage/domains/audit/domain.js';\nimport type {\n ProjectRepository,\n SourceControlRepository,\n SourceControlSession,\n SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport { mergeRequestNumberFromBranch } from '../work-item-branch.js';\nimport { environmentSlugs, findEnvironmentRepository, resolveSessionRepositories } from './environment-repositories.js';\nimport { updateSessionEnvironmentRepository } from './environment-state-processor.js';\n\ntype RepositorySessionState = {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n};\n\nexport interface SourceControlToolProvider {\n id: string;\n storage: SourceControlStorageHandle;\n versionControl: VersionControl;\n}\n\ninterface SessionTarget {\n context: AgentControllerRequestContext<RepositorySessionState>;\n provider: SourceControlToolProvider;\n session: SourceControlSession;\n projectRepository: ProjectRepository;\n repository: SourceControlRepository;\n orgId: string;\n userId: string;\n /** True when the target is the link the session is filed under, the default. */\n ownRepository: boolean;\n}\n\ntype ReviewEvent = 'approve' | 'request-changes' | 'comment';\n\n/** Returns why a review's leading `Verdict:` line contradicts the submitted event, if it does. */\nexport function verdictEventMismatch(event: ReviewEvent, body: string | undefined): string | undefined {\n const match = /^verdict: ?(approve|request changes|changes requested)\\b/.exec(normalizedVerdictLine(body) ?? '');\n if (!match) return undefined;\n const verdict = match[1] === 'approve' ? 'approve' : 'request changes';\n // GitLab has no request-changes state, so a blocking verdict is published as a comment review.\n const allowed: ReviewEvent[] = verdict === 'approve' ? ['approve'] : ['request-changes', 'comment'];\n if (allowed.includes(event)) return undefined;\n return `Review body opens with \"Verdict: ${verdict}\" but event is \"${event}\". Nothing was posted. Use event ${allowed\n .map(e => `\"${e}\"`)\n .join(' or ')} for this body, or regenerate the body for the verdict you intend.`;\n}\n\nconst REVIEWED_HEAD_LINE = /^[\\s*_>-]*reviewed head[*_]*:[*_]*\\s*(.*)$/gim;\nconst FULL_SHA = /^`?([0-9a-f]{40}|[0-9a-f]{64})`?(?:\\s|$)/i;\n\n/**\n * Returns the full SHA named by the body's `Reviewed head:` lines, `null` when any line is malformed or the lines\n * disagree, or `undefined` when absent.\n */\nexport function reviewedHeadFromBody(body: string | undefined): string | null | undefined {\n const shas = new Set(\n [...(body ?? '').matchAll(REVIEWED_HEAD_LINE)].map(\n line => FULL_SHA.exec(line[1]!.trim())?.[1]?.toLowerCase() ?? null,\n ),\n );\n if (shas.size === 0) return undefined;\n if (shas.size > 1) return null;\n return [...shas][0];\n}\n\nfunction authIdentity(requestContext: RequestContext) {\n const user = getFactoryAuthUserFromContext(requestContext);\n return { orgId: getFactoryAuthOrgId(user), userId: getFactoryAuthUserId(user) };\n}\n\nasync function resolveProviderSession(\n resourceId: string,\n providers: readonly SourceControlToolProvider[],\n): Promise<{ provider: SourceControlToolProvider; session: SourceControlSession }> {\n const matches = (\n await Promise.all(\n providers.map(async provider => ({\n provider,\n session: await provider.storage.sessions.getBySessionId(resourceId),\n })),\n )\n ).filter(\n (match): match is { provider: SourceControlToolProvider; session: SourceControlSession } => match.session !== null,\n );\n if (matches.length !== 1) {\n throw new Error(\n matches.length === 0\n ? 'The active session is not backed by a source-control repository.'\n : 'The active session is ambiguous across source-control providers.',\n );\n }\n return matches[0]!;\n}\n\nasync function resolveSessionTarget(\n requestContext: RequestContext,\n providers: readonly SourceControlToolProvider[],\n options: { repository?: string } = {},\n): Promise<SessionTarget> {\n const context = requestContext.get('controller') as AgentControllerRequestContext<RepositorySessionState> | undefined;\n const { orgId, userId } = authIdentity(requestContext);\n if (!context?.resourceId || !orgId || !userId) {\n throw new Error('Source-control tools require an authenticated repository session.');\n }\n\n const { provider, session } = await resolveProviderSession(context.resourceId, providers);\n if (session.orgId !== orgId || (session.visibility === 'private' && session.userId !== userId)) {\n throw new Error('The active source-control session is not available to the authenticated user.');\n }\n const ownRepository = session.projectRepositoryId\n ? await provider.storage.projectRepositories.get({ orgId, id: session.projectRepositoryId })\n : null;\n if (!ownRepository) throw new Error('The active project repository was not found.');\n const state = context.getState();\n if (state.projectRepositoryId && state.projectRepositoryId !== ownRepository.id) {\n throw new Error('The active source-control session does not match its controller repository.');\n }\n const ownConnection = await provider.storage.connections.get({ orgId, id: ownRepository.connectionId });\n if (!ownConnection) throw new Error('The active source-control connection was not found.');\n if (state.factoryProjectId && state.factoryProjectId !== ownConnection.factoryProjectId) {\n throw new Error('The active source-control session does not match its Factory project.');\n }\n\n if (options.repository !== undefined) {\n const candidates = await resolveSessionRepositories({ sourceControl: provider.storage, session });\n const match = findEnvironmentRepository(candidates, options.repository);\n if (!match) {\n throw new Error(\n `Repository '${options.repository}' is not in this Factory's environment. Valid repositories: ${environmentSlugs(candidates).join(', ')}.`,\n );\n }\n return {\n context,\n provider,\n session,\n projectRepository: match.link,\n repository: match.repository,\n orgId,\n userId,\n ownRepository: match.link.id === ownRepository.id,\n };\n }\n\n const repository = await provider.storage.repositories.get({ orgId, id: ownRepository.repositoryId });\n if (!repository) throw new Error('The active source-control repository was not found.');\n return {\n context,\n provider,\n session,\n projectRepository: ownRepository,\n repository,\n orgId,\n userId,\n ownRepository: true,\n };\n}\n\n/**\n * The slugs a session may target and the one it is filed under, for the tool\n * descriptions. `undefined` when the session cannot be resolved; the tools\n * then describe the default without naming it.\n */\nasync function describeSessionRepositories(\n resourceId: string,\n providers: readonly SourceControlToolProvider[],\n): Promise<{ slugs: string[]; own: string | undefined } | undefined> {\n try {\n const { provider, session } = await resolveProviderSession(resourceId, providers);\n const repositories = await resolveSessionRepositories({ sourceControl: provider.storage, session });\n const own = repositories.find(candidate => candidate.link.id === session.projectRepositoryId);\n return { slugs: environmentSlugs(repositories), own: own?.repository.slug };\n } catch {\n return undefined;\n }\n}\n\nfunction repositoryArgumentDescription(described: { slugs: string[]; own: string | undefined } | undefined): string {\n if (!described || described.slugs.length === 0) {\n return \"Optional `repository` (owner/name) selects another repository of this Factory's environment; defaults to this session's repository.\";\n }\n const fallback = described.own ? `${described.own}, this session's repository` : \"this session's repository\";\n return `Optional \\`repository\\` (owner/name) selects one of this Factory's environment repositories: ${described.slugs.join(', ')}. Defaults to ${fallback}.`;\n}\n\nfunction changeRequestId(value: string | number): string {\n return String(value);\n}\n\nfunction executableSandbox(value: unknown): WorkspaceSandbox & ExecutableSandbox {\n const sandbox = value as Partial<ExecutableSandbox> | undefined;\n if (!sandbox?.executeCommand) throw new Error('The active Factory workspace has no executable sandbox.');\n return sandbox as WorkspaceSandbox & ExecutableSandbox;\n}\n\nasync function emitAgentAudit(\n audit: AuditAgentEmitter,\n requestContext: RequestContext,\n input: Parameters<AuditAgentEmitter['emitAgent']>[0]['input'],\n): Promise<void> {\n try {\n await audit.emitAgent({ requestContext, input });\n } catch (error) {\n console.warn('[Audit] Failed to record brokered source-control action', {\n action: input.action,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/** Fixed server-side so an agent cannot publish under, and impersonate, another required check. */\nexport const FACTORY_TEST_CHECK_NAME = 'Factory Review / tests';\n\nconst repositorySchema = z.object({ repository: z.string().trim().min(1).optional() });\nconst changeRequestSchema = repositorySchema.extend({\n changeRequestId: z.union([z.string().trim().min(1), z.number().int().positive()]),\n});\n\nexport async function createSourceControlTools({\n requestContext,\n providers,\n audit,\n testChecksEnabled,\n selfReviewApprovedHead,\n boundChangeRequestId,\n}: {\n requestContext: RequestContext;\n providers: readonly SourceControlToolProvider[];\n audit: AuditAgentEmitter;\n /** Per-project switch for `source_control_publish_test_check`. The host decides the default; Factory projects default to off. */\n testChecksEnabled?: (input: { orgId: string; factoryProjectId: string }) => Promise<boolean>;\n /**\n * Self-review gate for opening a change request. `undefined` means the session\n * is not gated (no Work item, or its change request predates Self-review);\n * `null` means Self-review has not approved; a SHA is the approved head.\n */\n selfReviewApprovedHead?: () => Promise<string | null | undefined>;\n /**\n * The change request a Review-bound session reviews; test checks may only target it.\n * When provided, sessions that are not Review-bound (it resolves `undefined`) cannot publish.\n */\n boundChangeRequestId?: () => Promise<string | undefined>;\n}): Promise<IntegrationTools> {\n if (!requestContext || typeof (requestContext as { get?: unknown }).get !== 'function') return {};\n const controller = requestContext.get('controller') as\n | AgentControllerRequestContext<RepositorySessionState>\n | undefined;\n const identity = authIdentity(requestContext);\n if (!controller?.resourceId || !identity.orgId || !identity.userId || providers.length === 0) return {};\n\n const repositoryArgument = repositoryArgumentDescription(\n await describeSessionRepositories(controller.resourceId, providers),\n );\n const withTarget = (options?: { repository?: string }) => resolveSessionTarget(requestContext, providers, options);\n const targetFor = (input: { repository?: string }) =>\n withTarget(input.repository !== undefined ? { repository: input.repository } : undefined);\n const reference = async (target: SessionTarget) => ({\n ...(await target.provider.versionControl.getRepositoryTarget({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n })),\n actingUserId: target.userId,\n });\n\n return {\n source_control_refresh_change_request_checkout: createTool({\n id: 'source_control_refresh_change_request_checkout',\n description:\n 'Refresh the active GitLab review session checkout to its current MR head. The MR, repository, branch, and credential are resolved server-side; no provider credentials enter the agent workspace.',\n inputSchema: z.object({}),\n execute: async (_input, { workspace }) => {\n const target = await withTarget();\n const mergeRequestNumber = mergeRequestNumberFromBranch(target.session.branch);\n if (target.provider.id !== 'gitlab' || mergeRequestNumber === undefined) {\n throw new Error('Checkout refresh is only available in a bound GitLab merge-request review session.');\n }\n const ref = await reference(target);\n const mergeRequest = await target.provider.versionControl.getPullRequest({\n ...ref,\n pullRequestId: String(mergeRequestNumber),\n });\n if (!mergeRequest || mergeRequest.state !== 'open' || mergeRequest.merged) {\n throw new Error('The bound GitLab merge request is no longer open.');\n }\n const sandbox = executableSandbox(workspace?.sandbox);\n const workdir = await resolveSessionWorkdir(target.session.id, sandbox, target.repository.slug);\n const access = await target.provider.versionControl.getRepositoryAccess({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n });\n const result = await refreshMergeRequestCheckout(sandbox, workdir, {\n branch: target.session.branch,\n mergeRequestNumber,\n expectedHeadSha: mergeRequest.headSha,\n access,\n });\n return result;\n },\n }),\n source_control_push_branch: createTool({\n id: 'source_control_push_branch',\n description: `Push the active Factory session branch to its connected source-control provider. Credentials are resolved and scrubbed server-side; this tool takes no token, remote, or branch arguments. ${repositoryArgument}`,\n inputSchema: repositorySchema,\n execute: async (input, { workspace }) => {\n const target = await targetFor(input);\n const sandbox = executableSandbox(workspace?.sandbox);\n const workdir = await resolveSessionWorkdir(target.session.id, sandbox, target.repository.slug);\n const access = await target.provider.versionControl.getRepositoryAccess({\n orgId: target.orgId,\n repositoryId: target.repository.id,\n });\n // Another environment repository's directory may not be its checkout:\n // a boot skips a link whose directory name collides with another's, and\n // a session that booted single-repository has no siblings at all.\n if (!target.ownRepository && !(await isCheckoutOf(sandbox, workdir, access.cloneUrl))) {\n throw new Error(`There is no checkout of ${target.repository.slug} in this session.`);\n }\n const branch = target.session.branch;\n // Push the local session branch, whatever is checked out. A repository\n // where it was never created gets it at HEAD first, so a later push\n // never sends some other branch's commits.\n await ensureLocalBranch(sandbox, workdir, branch);\n await pushRepositoryBranch(sandbox, workdir, branch, access, target.repository.slug);\n await target.provider.storage.sessionRepositories.upsert({\n sessionId: target.session.sessionId,\n projectRepositoryId: target.projectRepository.id,\n branch,\n });\n updateSessionEnvironmentRepository(target.session.sessionId, target.repository.slug, { branch });\n await emitAgentAudit(audit, requestContext, {\n action: 'factory.agent.push',\n targets: [{ type: 'repository', id: target.repository.slug }],\n metadata: { branch, provider: target.provider.id, repository: target.repository.slug },\n });\n return { pushed: true, branch, repository: target.repository.slug };\n },\n }),\n source_control_get_change_request: createTool({\n id: 'source_control_get_change_request',\n description: `Read a pull request or merge request in the active Factory repository by its numeric repository-local ID. ${repositoryArgument}`,\n inputSchema: changeRequestSchema,\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.getPullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n });\n },\n }),\n source_control_list_change_request_reviews: createTool({\n id: 'source_control_list_change_request_reviews',\n description: `List submitted reviews and approvals on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listReviews({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_create_change_request: createTool({\n id: 'source_control_create_change_request',\n description: `Open a pull request or merge request from the active Factory session branch into the repository's base branch (the session's persisted base branch in its own repository, the linked or default branch elsewhere). Push the branch with source_control_push_branch first. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({\n title: z.string().trim().min(1),\n body: z.string().optional(),\n draft: z.boolean().optional(),\n }),\n execute: async (input, execution) => {\n const target = await targetFor(input);\n const approvedHead = target.ownRepository ? await selfReviewApprovedHead?.() : undefined;\n if (approvedHead === null) {\n throw new Error(\n 'Self-review has not approved this branch. Move the work item to Self-review; the change request opens after it approves.',\n );\n }\n const baseBranch = target.ownRepository\n ? target.session.baseBranch\n : (target.projectRepository.branch ?? target.repository.defaultBranch);\n const created = await target.provider.versionControl.createPullRequest({\n ...(await reference(target)),\n title: input.title,\n ...(input.body !== undefined ? { body: input.body } : {}),\n baseBranch,\n headBranch: target.session.branch,\n ...(input.draft !== undefined ? { draft: input.draft } : {}),\n });\n if (approvedHead !== undefined && created.headSha.toLowerCase() !== approvedHead.toLowerCase()) {\n await target.provider.versionControl.closePullRequest({\n ...(await reference(target)),\n pullRequestId: created.id,\n });\n throw new Error(\n `Self-review approved ${approvedHead}, but the pushed branch is at ${created.headSha}; the change request was closed. Push the approved head, or move the work item back to Self-review to review the new commits.`,\n );\n }\n await target.provider.storage.sessionRepositories.upsert({\n sessionId: target.session.sessionId,\n projectRepositoryId: target.projectRepository.id,\n // Opening a change request pushes nothing: an existing row keeps its\n // `pushedAt`; the branch only seeds a row no push wrote yet.\n fallbackBranch: target.session.branch,\n changeRequestId: changeRequestId(created.id),\n changeRequestUrl: created.url,\n });\n updateSessionEnvironmentRepository(target.session.sessionId, target.repository.slug, {\n changeRequestUrl: created.url,\n });\n await emitAgentAudit(audit, requestContext, {\n action: 'factory.agent.pr_opened',\n targets: [\n { type: 'pull_request', id: created.url },\n { type: 'repository', id: target.repository.slug },\n ],\n metadata: { url: created.url, provider: target.provider.id, repository: target.repository.slug },\n });\n return created;\n },\n }),\n source_control_update_change_request: createTool({\n id: 'source_control_update_change_request',\n description: `Update the title, body, target branch, or open/closed state of a change request. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n title: z.string().trim().min(1).optional(),\n body: z.string().nullable().optional(),\n baseBranch: z.string().trim().min(1).optional(),\n state: z.enum(['open', 'closed']).optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updatePullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.title !== undefined ? { title: input.title } : {}),\n ...(input.body !== undefined ? { body: input.body } : {}),\n ...(input.baseBranch !== undefined ? { baseBranch: input.baseBranch } : {}),\n ...(input.state !== undefined ? { state: input.state } : {}),\n });\n },\n }),\n source_control_comment_change_request: createTool({\n id: 'source_control_comment_change_request',\n description: `Add a top-level comment to a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.createComment({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n body: input.body,\n });\n },\n }),\n source_control_list_change_request_comments: createTool({\n id: 'source_control_list_change_request_comments',\n description: `List top-level comments on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listComments({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_update_change_request_comment: createTool({\n id: 'source_control_update_change_request_comment',\n description: `Edit a top-level pull-request or merge-request comment in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1), body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updateComment({\n ...(await reference(target)),\n commentId: input.commentId,\n body: input.body,\n });\n },\n }),\n source_control_delete_change_request_comment: createTool({\n id: 'source_control_delete_change_request_comment',\n description: `Delete a top-level pull-request or merge-request comment in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n await target.provider.versionControl.deleteComment({\n ...(await reference(target)),\n commentId: input.commentId,\n });\n return { deleted: true };\n },\n }),\n source_control_list_diff_comments: createTool({\n id: 'source_control_list_diff_comments',\n description: `List line-anchored review comments and discussion replies on a pull request or merge request in the active repository. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({ cursor: z.string().trim().min(1).optional() }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.listReviewComments({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.cursor !== undefined ? { cursor: input.cursor } : {}),\n });\n },\n }),\n source_control_create_diff_comment: createTool({\n id: 'source_control_create_diff_comment',\n description: `Create a line-anchored review comment or reply to an existing diff discussion. Use replyToId alone for a reply; otherwise provide commitId, path, line, and side. ${repositoryArgument}`,\n // A root-level union is not representable as provider function parameters, which must be\n // an object schema, so both modes share one object shape and a refinement.\n inputSchema: changeRequestSchema\n .extend({\n body: z.string().trim().min(1),\n replyToId: z.string().trim().min(1).optional(),\n commitId: z.string().trim().min(1).optional(),\n path: z.string().trim().min(1).optional(),\n line: z.number().int().positive().optional(),\n side: z.enum(['left', 'right']).optional(),\n startLine: z.number().int().positive().optional(),\n startSide: z.enum(['left', 'right']).optional(),\n })\n .refine(\n input => {\n const anchorFields = [input.commitId, input.path, input.line, input.side];\n const anchor = anchorFields.every(field => field !== undefined);\n const reply = input.replyToId !== undefined;\n return (\n (reply ? anchorFields.every(field => field === undefined) : anchor) &&\n (input.startLine === undefined) === (input.startSide === undefined)\n );\n },\n {\n message:\n 'Provide replyToId alone to reply to a diff thread, or commitId, path, line, and side to start one. startLine and startSide must be provided together.',\n },\n ),\n execute: async input => {\n const target = await targetFor(input);\n const base = {\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n body: input.body,\n };\n if (input.replyToId !== undefined) {\n return target.provider.versionControl.createReviewComment({ ...base, replyToId: input.replyToId });\n }\n if (\n input.commitId === undefined ||\n input.path === undefined ||\n input.line === undefined ||\n input.side === undefined\n ) {\n throw new Error('commitId, path, line, and side are required to start a new diff thread.');\n }\n return target.provider.versionControl.createReviewComment({\n ...base,\n commitId: input.commitId,\n path: input.path,\n line: input.line,\n side: input.side,\n ...(input.startLine !== undefined && input.startSide !== undefined\n ? { startLine: input.startLine, startSide: input.startSide }\n : {}),\n });\n },\n }),\n source_control_update_diff_comment: createTool({\n id: 'source_control_update_diff_comment',\n description: `Edit a line-anchored review comment or discussion reply in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1), body: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.updateReviewComment({\n ...(await reference(target)),\n commentId: input.commentId,\n body: input.body,\n });\n },\n }),\n source_control_delete_diff_comment: createTool({\n id: 'source_control_delete_diff_comment',\n description: `Delete a line-anchored review comment or discussion reply in the active repository. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({ commentId: z.string().trim().min(1) }),\n execute: async input => {\n const target = await targetFor(input);\n await target.provider.versionControl.deleteReviewComment({\n ...(await reference(target)),\n commentId: input.commentId,\n });\n return { deleted: true };\n },\n }),\n source_control_resolve_diff_thread: createTool({\n id: 'source_control_resolve_diff_thread',\n description: `Resolve or reopen the review thread containing a diff comment. Providers without resolvable threads return an explicit limitation. ${repositoryArgument}`,\n inputSchema: repositorySchema.extend({\n commentId: z.string().trim().min(1),\n resolved: z.boolean().default(true),\n }),\n execute: async input => {\n const target = await targetFor(input);\n const resolveReviewThread = target.provider.versionControl.resolveReviewThread;\n if (!resolveReviewThread) {\n throw new Error(`${target.provider.id} does not support resolving review threads.`);\n }\n await resolveReviewThread({\n ...(await reference(target)),\n commentId: input.commentId,\n resolved: input.resolved,\n });\n return { resolved: input.resolved };\n },\n }),\n source_control_review_change_request: createTool({\n id: 'source_control_review_change_request',\n description: `Submit an approve, request-changes, or comment review to a change request. Provider limitations are returned explicitly. ${repositoryArgument}`,\n inputSchema: changeRequestSchema\n .extend({\n event: z.enum(['approve', 'request-changes', 'comment']),\n body: z.string().optional(),\n commitId: z.string().trim().min(1).optional(),\n })\n .refine(input => input.event === 'approve' || Boolean(input.body?.trim()), {\n path: ['body'],\n message: 'request-changes and comment reviews require a body.',\n })\n .superRefine((input, ctx) => {\n const mismatch = verdictEventMismatch(input.event, input.body);\n if (mismatch) ctx.addIssue({ code: 'custom', path: ['body'], message: mismatch });\n const reviewedHead = reviewedHeadFromBody(input.body);\n if (reviewedHead === null) {\n ctx.addIssue({\n code: 'custom',\n path: ['body'],\n message: 'Every \"Reviewed head:\" line must name the same full 40- or 64-character commit SHA.',\n });\n } else if (reviewedHead && input.commitId && input.commitId.toLowerCase() !== reviewedHead) {\n ctx.addIssue({\n code: 'custom',\n path: ['commitId'],\n message: `Review body says \"Reviewed head: ${reviewedHead}\" but commitId is ${input.commitId}. Nothing was posted. Re-review the current head and regenerate the body before publishing.`,\n });\n }\n }),\n execute: async input => {\n const target = await targetFor(input);\n const reviewedHead = reviewedHeadFromBody(input.body);\n if (reviewedHead) {\n const pullRequest = await target.provider.versionControl.getPullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n });\n if (!pullRequest) {\n throw new Error(`Change request ${input.changeRequestId} was not found; nothing was posted.`);\n }\n if (pullRequest.headSha && pullRequest.headSha.toLowerCase() !== reviewedHead) {\n throw new Error(\n `Review body says \"Reviewed head: ${reviewedHead}\" but the current change-request head is ${pullRequest.headSha}. Nothing was posted. Re-review the current head and regenerate the body before publishing.`,\n );\n }\n }\n const base = {\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.commitId !== undefined\n ? { commitId: input.commitId }\n : reviewedHead\n ? { commitId: reviewedHead }\n : {}),\n };\n if (input.event === 'approve') {\n return target.provider.versionControl.createReview({\n ...base,\n event: 'approve',\n ...(input.body !== undefined ? { body: input.body } : {}),\n });\n }\n return target.provider.versionControl.createReview({ ...base, event: input.event, body: input.body! });\n },\n }),\n source_control_publish_test_check: createTool({\n id: 'source_control_publish_test_check',\n description: `Publish the Factory review test result as a \"${FACTORY_TEST_CHECK_NAME}\" commit check on the change request's current head. Publish \"in-progress\" before running the test plan and a final status after. Refused when headSha is not the current head. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n headSha: z\n .string()\n .trim()\n .regex(/^([0-9a-f]{40}|[0-9a-f]{64})$/i, 'headSha must be a full 40- or 64-character commit SHA.'),\n status: z.enum(['in-progress', 'success', 'failure', 'neutral', 'cancelled', 'skipped']),\n title: z.string().trim().min(1),\n summary: z.string().trim().min(1),\n details: z.string().optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n if (testChecksEnabled) {\n const connection = await target.provider.storage.connections.get({\n orgId: target.orgId,\n id: target.projectRepository.connectionId,\n });\n if (\n connection?.factoryProjectId &&\n !(await testChecksEnabled({ orgId: target.orgId, factoryProjectId: connection.factoryProjectId }))\n ) {\n throw new Error(\n 'Test check publishing is turned off for this Factory project; nothing was published. Report the test results in the test report comment instead.',\n );\n }\n }\n const publishCommitCheck = target.provider.versionControl.publishCommitCheck;\n if (!publishCommitCheck) {\n throw new Error(`${target.provider.id} does not support publishing commit checks; nothing was published.`);\n }\n const bound = boundChangeRequestId ? await boundChangeRequestId() : undefined;\n if (bound === undefined) {\n throw new Error(\n 'Only a Review session bound to this change request can publish test checks; nothing was published.',\n );\n }\n if (bound !== changeRequestId(input.changeRequestId)) {\n throw new Error(\n `This session reviews change request ${bound}, not ${input.changeRequestId}; nothing was published.`,\n );\n }\n const ref = await reference(target);\n const pullRequest = await target.provider.versionControl.getPullRequest({\n ...ref,\n pullRequestId: changeRequestId(input.changeRequestId),\n });\n if (!pullRequest || pullRequest.state !== 'open') {\n throw new Error(`Change request ${input.changeRequestId} is not open; nothing was published.`);\n }\n const headSha = input.headSha.toLowerCase();\n if (pullRequest.headSha.toLowerCase() !== headSha) {\n throw new Error(\n `headSha ${headSha} is not the current change-request head ${pullRequest.headSha}. Nothing was published. Re-run the test plan on the current head.`,\n );\n }\n return publishCommitCheck({\n connection: ref.connection,\n sourceId: ref.sourceId,\n actingUserId: ref.actingUserId,\n headSha,\n ref: pullRequest.headBranch,\n name: FACTORY_TEST_CHECK_NAME,\n status: input.status,\n title: input.title,\n summary: input.summary,\n ...(input.details !== undefined ? { details: input.details } : {}),\n });\n },\n }),\n source_control_request_reviewers: createTool({\n id: 'source_control_request_reviewers',\n description: `Request individual or team reviewers on a pull request or merge request. Provider limitations are returned explicitly. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n users: z.array(z.string().trim().min(1)).default([]),\n teams: z.array(z.string().trim().min(1)).default([]),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.requestReviewers({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n users: input.users,\n teams: input.teams,\n });\n },\n }),\n source_control_merge_change_request: createTool({\n id: 'source_control_merge_change_request',\n description: `Merge a pull request or merge request using the requested provider-supported merge method. ${repositoryArgument}`,\n inputSchema: changeRequestSchema.extend({\n method: z.enum(['merge', 'squash', 'rebase']).optional(),\n commitTitle: z.string().optional(),\n commitMessage: z.string().optional(),\n }),\n execute: async input => {\n const target = await targetFor(input);\n return target.provider.versionControl.mergePullRequest({\n ...(await reference(target)),\n pullRequestId: changeRequestId(input.changeRequestId),\n ...(input.method !== undefined ? { method: input.method } : {}),\n ...(input.commitTitle !== undefined ? { commitTitle: input.commitTitle } : {}),\n ...(input.commitMessage !== undefined ? { commitMessage: input.commitMessage } : {}),\n });\n },\n }),\n };\n}\n"],"mappings":"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"}
@@ -60,6 +60,10 @@ export interface FactoryProject {
60
60
  buildOnPushEnabled: boolean;
61
61
  /** Minutes after a push-triggered attempt during which further pushes do not build. */
62
62
  buildPushDebounceMinutes: number;
63
+ /** Whether Review agents publish their test results as a commit check. Defaults to off. */
64
+ reviewTestChecksEnabled: boolean;
65
+ /** Whether Work items get an independent self-review before the change request opens. Defaults to off. */
66
+ selfReviewEnabled: boolean;
63
67
  createdAt: Date;
64
68
  updatedAt: Date;
65
69
  }
@@ -88,6 +92,8 @@ export interface UpdateFactoryProjectInput {
88
92
  lastBuildAttemptedAt?: Date | null;
89
93
  buildOnPushEnabled?: boolean;
90
94
  buildPushDebounceMinutes?: number;
95
+ reviewTestChecksEnabled?: boolean;
96
+ selfReviewEnabled?: boolean;
91
97
  }
92
98
  export declare const FACTORY_PROJECTS_SCHEMA: CollectionSchema;
93
99
  export declare class FactoryProjectsStorage extends FactoryStorageDomain {