@mastra/factory 0.23.0-alpha.2 → 0.23.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +2 -1
- package/dist/factory.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +4 -39
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +17 -77
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/config.d.ts +3 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +72 -2
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.d.ts +12 -2
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +2 -2
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +7 -2
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +3 -1
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +4 -2
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +3 -2
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +2 -1
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/session/credential-policy.d.ts +13 -5
- package/dist/session/credential-policy.d.ts.map +1 -1
- package/dist/session/credential-policy.js +23 -10
- package/dist/session/credential-policy.js.map +1 -1
- package/dist/session/default-model-hydration.d.ts +6 -1
- package/dist/session/default-model-hydration.d.ts.map +1 -1
- package/dist/session/default-model-hydration.js +18 -17
- package/dist/session/default-model-hydration.js.map +1 -1
- package/dist/session/factory-session.d.ts +5 -3
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +2 -1
- package/dist/session/factory-session.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +13 -5
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +16 -7
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +8 -0
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +14 -0
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/session.d.ts.map +1 -1
- package/dist/supervisor/session.js +2 -1
- package/dist/supervisor/session.js.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-coordinator.js","names":["#controller","#storage","#transitionService","#sourceControl","#intake","#projects","#modelDefaults"],"sources":["../../src/rules/start-coordinator.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { boardForWorkItem } from '../boards/index.js';\nimport { primeTenantCredentialsForTenant } from '../routes/tenant-credentials.js';\nimport { hydrateFactorySession, resolveFactorySessionModel } from '../session/factory-session.js';\nimport { resolveWorkItemRepository } from '../session/work-item-repository.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { FactoryModelScope, FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { SourceControlSession, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryRuleStage, FactoryTransitionResult } from './types.js';\n\nexport interface FactoryStartRequest {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n sessionId: string;\n threadTitle: string;\n kickoffKey: string;\n /** Where the arrival path lands the card; a session opened on an existing card names none. */\n destinationStage?: FactoryRuleStage;\n modelScope: FactoryModelScope;\n defaultModelId?: string;\n workItem: {\n id?: string;\n role: string;\n input: CreateWorkItemInput;\n };\n requestContext?: RequestContext;\n}\n\nexport class WorkItemRepositoryError extends Error {\n constructor(\n readonly code: 'repository_mismatch' | 'repository_ambiguous' | 'repository_unlinked',\n message: string,\n readonly candidates: string[] = [],\n ) {\n super(message);\n this.name = 'WorkItemRepositoryError';\n }\n}\n\nexport class FactoryStartTransitionError extends Error {\n readonly result: Extract<FactoryTransitionResult, { status: 'rejected' }>;\n\n constructor(result: Extract<FactoryTransitionResult, { status: 'rejected' }>) {\n super(result.reason);\n this.name = 'FactoryStartTransitionError';\n this.result = result;\n }\n}\n\nexport interface FactoryStartPreparedResult {\n workItemId: string;\n bindingId: string;\n threadId: string;\n resourceId: string;\n sessionId: string;\n branch: string;\n revision: number;\n kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';\n replayed: boolean;\n}\n\ntype FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nasync function resolveSourceSession(\n storage: SourceControlStorageHandle,\n request: FactoryStartRequest,\n): Promise<SourceControlSession & { projectRepositoryId: string }> {\n const session = await storage.sessions.getBySessionId(request.sessionId);\n if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n throw new Error('Factory session not found');\n }\n const projectRepositoryId = session.projectRepositoryId;\n if (!projectRepositoryId) throw new Error('Factory session repository not found');\n const projectRepository = await storage.projectRepositories.get({ orgId: request.orgId, id: projectRepositoryId });\n if (!projectRepository) throw new Error('Factory session repository not found');\n const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });\n if (!connection || connection.factoryProjectId !== request.factoryProjectId) {\n throw new Error('Factory session does not belong to this project');\n }\n return { ...session, projectRepositoryId };\n}\n\nasync function configureThread(session: FactorySession, request: FactoryStartRequest): Promise<string> {\n const threadId = session.thread.requireId();\n // Derived from the work item, not a user rename, so leave the title\n // unpinned and let auto-naming keep refining it.\n await session.thread.rename({ title: request.threadTitle, pin: false });\n await session.thread.setSetting({ key: 'factorySessionId', value: request.sessionId });\n return threadId;\n}\n\nexport class FactoryStartCoordinator {\n readonly #controller: FactoryController;\n readonly #storage: WorkItemsStorage;\n readonly #transitionService?: Pick<FactoryTransitionService, 'transition'>;\n readonly #sourceControl?:\n | SourceControlStorageHandle\n | ((\n request: FactoryStartRequest,\n ) => SourceControlStorageHandle | undefined | Promise<SourceControlStorageHandle | undefined>);\n readonly #intake?: IntakeStorage;\n readonly #projects?: FactoryProjectsStorage;\n readonly #modelDefaults?: ModelDefaultsStorage;\n\n constructor(\n controller: FactoryController,\n storage: WorkItemsStorage,\n transitionService?: Pick<FactoryTransitionService, 'transition'>,\n sourceControl?:\n | SourceControlStorageHandle\n | ((\n request: FactoryStartRequest,\n ) => SourceControlStorageHandle | undefined | Promise<SourceControlStorageHandle | undefined>),\n intake?: IntakeStorage,\n sessionModel?: { projects: FactoryProjectsStorage; modelDefaults: ModelDefaultsStorage },\n ) {\n this.#controller = controller;\n this.#storage = storage;\n this.#transitionService = transitionService;\n this.#sourceControl = sourceControl;\n this.#intake = intake;\n this.#projects = sessionModel?.projects;\n this.#modelDefaults = sessionModel?.modelDefaults;\n }\n\n async prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult> {\n const storage = this.#storage;\n const sourceControl =\n typeof this.#sourceControl === 'function' ? await this.#sourceControl(request) : this.#sourceControl;\n if (!sourceControl) throw new Error('Factory source control storage is unavailable');\n const sourceSession = await resolveSourceSession(sourceControl, request);\n const sessionModel =\n this.#projects && this.#modelDefaults\n ? await resolveFactorySessionModel({\n projects: this.#projects,\n modelDefaults: this.#modelDefaults,\n factoryProjectId: request.factoryProjectId,\n orgId: request.orgId,\n ownerUserId: sourceSession.userId,\n })\n : { modelScope: request.modelScope, modelId: request.defaultModelId };\n const storedItem = request.workItem.id\n ? await storage.get({ orgId: request.orgId, id: request.workItem.id })\n : null;\n const intakeConfig = await this.#intake?.getConfig({ orgId: request.orgId, integrationIds: ['linear'] });\n let repository = await resolveWorkItemRepository({\n sourceControl,\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n item: storedItem ?? { metadata: request.workItem.input.metadata ?? null },\n linearRepositoryMap: intakeConfig?.linear?.repositoryByLinearProject,\n });\n if (repository.status === 'ambiguous') {\n // A retry of this role's existing session keeps its already-chosen\n // repository; a newly minted session must not choose for the card.\n if (storedItem?.sessions[request.workItem.role]?.sessionId !== sourceSession.sessionId) {\n throw new WorkItemRepositoryError(\n 'repository_ambiguous',\n `Choose a repository for this work item: ${repository.candidates.join(', ')}.`,\n repository.candidates,\n );\n }\n const link = await sourceControl.projectRepositories.get({\n orgId: request.orgId,\n id: sourceSession.projectRepositoryId,\n });\n const linked = link && (await sourceControl.repositories.get({ orgId: request.orgId, id: link.repositoryId }));\n const currentLinks = link\n ? await sourceControl.projectRepositories.list({ orgId: request.orgId, connectionId: link.connectionId })\n : [];\n if (\n !linked ||\n !repository.candidates.includes(linked.slug) ||\n !currentLinks.some(candidate => candidate.id === sourceSession.projectRepositoryId)\n ) {\n throw new WorkItemRepositoryError(\n 'repository_unlinked',\n 'The existing session repository is not linked to this Factory.',\n );\n }\n repository = { status: 'resolved', slug: linked.slug, projectRepositoryId: sourceSession.projectRepositoryId };\n }\n if (repository.status === 'unlinked') {\n throw new WorkItemRepositoryError('repository_unlinked', repository.hint);\n }\n if (repository.projectRepositoryId !== sourceSession.projectRepositoryId) {\n throw new WorkItemRepositoryError(\n 'repository_mismatch',\n `This run session is bound to a different repository than ${repository.slug}.`,\n );\n }\n const requestContext = request.requestContext ?? new RequestContext();\n // Preserve an authenticated caller when present. Autonomous starts still\n // need a principal so deployed credential resolution can authenticate the\n // request before selecting the stamped session owner's exclusive scope.\n const existingUser = requestContext.get('user');\n if (!existingUser || typeof existingUser !== 'object') {\n requestContext.set('user', {\n workosId: request.userId,\n organizationId: request.orgId,\n });\n }\n // Sessions kicked off against third-party content (a PR under review, or\n // any pull-request-sourced work item) get `untrustedCheckout` so the SDK\n // never ingests the checkout's AGENTS.md/CLAUDE.md into the system prompt\n // or reminders — those files are attacker-writable in a PR branch.\n const untrustedCheckout = request.workItem.input.externalSource?.type === 'pull-request';\n // The trusted ref the SDK may serve project instruction files from on an\n // untrusted checkout (the PR's base branch). Prefer the session record's\n // base branch; fall back to the intake metadata captured from the PR.\n const metadataBaseBranch = request.workItem.input.metadata?.baseBranch;\n const baseRef =\n (sourceSession.baseBranch || undefined) ??\n (typeof metadataBaseBranch === 'string' && metadataBaseBranch ? metadataBaseBranch : undefined);\n const sessionTags = {\n factoryProjectId: request.factoryProjectId,\n projectRepositoryId: sourceSession.projectRepositoryId,\n };\n const session = await this.#controller.createSession({\n id: sourceSession.sessionId,\n ownerId: request.userId,\n resourceId: sourceSession.sessionId,\n threadId: sourceSession.sessionId,\n requestContext,\n tags: sessionTags,\n });\n // Bound-agent authority gates (the transition tool, the factory-phase\n // processor, workspace token selection) resolve the session address from\n // controller state. Seed it server-side — `tags` covers fresh creation,\n // the explicit setState covers get-or-create returning a session another\n // caller created without them — so autonomous runs never depend on a\n // browser connecting to populate the state. `untrustedCheckout` is a\n // boolean so it rides only on state (tags are string-valued).\n const targetRepositoryInstruction = `Target repository: ${repository.slug}. Before editing files or creating a pull request, verify this session's checkout matches the target repository.`;\n const existingPluginInstructions = session.state.get()?.pluginInstructions ?? [];\n await session.state.set({\n ...sessionTags,\n // The authoritative org id for every downstream identity read (the\n // memory seam's organizationId): the session owner is a USER id, not an\n // org, so it must never be improvised from ownerId.\n factoryOrgId: request.orgId,\n factoryModelScope: sessionModel.modelScope,\n factoryOwnerUserId: sourceSession.userId,\n pluginInstructions: [\n ...existingPluginInstructions.filter(instruction => instruction !== targetRepositoryInstruction),\n targetRepositoryInstruction,\n ],\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n await primeTenantCredentialsForTenant({\n orgId: request.orgId,\n userId: sourceSession.userId,\n credentialScope: sessionModel.modelScope,\n });\n // Apply the factory's main model before any model-dependent run work.\n // Observational-memory settings are loaded per invocation by the input processor.\n await hydrateFactorySession(session, {\n orgId: request.orgId,\n modelScope: sessionModel.modelScope,\n ownerUserId: sourceSession.userId,\n defaultModelId: sessionModel.modelId,\n });\n // The tool is `requireApproval`, and that prompt parks the run whether or not\n // someone pressed Start — a person is reading the plan, not an approval queue.\n // Starting the run was already the say-so; the rules engine still governs.\n await session.permissions.setForTool({ toolName: 'factory_transition_work_item', policy: 'allow' });\n const threadId = await configureThread(session, request);\n const prepared = await storage.prepareRunStart({\n orgId: request.orgId,\n userId: request.userId,\n factoryProjectId: request.factoryProjectId,\n workItem: { id: request.workItem.id, input: request.workItem.input },\n role: request.workItem.role,\n session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },\n resourceId: sourceSession.sessionId,\n kickoffKey: request.kickoffKey,\n kickoffMessage: null,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n const destinationStage = request.destinationStage;\n if (destinationStage && (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== destinationStage)) {\n if (!this.#transitionService) throw new Error('Factory transition service is unavailable.');\n const transition = await this.#transitionService.transition({\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n workItemId: prepared.item.id,\n board: boardForWorkItem(prepared.item),\n stage: destinationStage,\n expectedRevision: prepared.item.revision,\n actor: { type: 'human', id: request.userId },\n ingress: { type: 'human', identity: `start:${request.kickoffKey}:transition` },\n cause: 'run_start',\n });\n if (transition.status === 'rejected') {\n await storage.markPendingStart(prepared.binding.id, 'failed', transition.reason);\n throw new FactoryStartTransitionError(transition);\n }\n revision = transition.revision;\n }\n\n await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\n\n return {\n workItemId: prepared.item.id,\n bindingId: prepared.binding.id,\n threadId,\n resourceId: sourceSession.sessionId,\n sessionId: sourceSession.sessionId,\n branch: sourceSession.branch,\n revision,\n kickoffStatus: prepared.pendingStart.status,\n replayed: prepared.replayed,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"start-coordinator.js","names":["#controller","#storage","#transitionService","#sourceControl","#intake","#projects","#modelDefaults"],"sources":["../../src/rules/start-coordinator.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport { RequestContext } from '@mastra/core/request-context';\n\nimport { boardForWorkItem } from '../boards/index.js';\nimport { primeTenantCredentialsForTenant } from '../routes/tenant-credentials.js';\nimport { hydrateFactorySession, resolveFactorySessionModel } from '../session/factory-session.js';\nimport { resolveWorkItemRepository } from '../session/work-item-repository.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { EffectiveModelScope, FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { SourceControlSession, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { CreateWorkItemInput, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport type { FactoryRuleStage, FactoryTransitionResult } from './types.js';\n\nexport interface FactoryStartRequest {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n sessionId: string;\n threadTitle: string;\n kickoffKey: string;\n /** Where the arrival path lands the card; a session opened on an existing card names none. */\n destinationStage?: FactoryRuleStage;\n /** The run's effective scope; a hybrid Factory's runs use the Factory's model and credentials. */\n modelScope: EffectiveModelScope;\n defaultModelId?: string;\n workItem: {\n id?: string;\n role: string;\n input: CreateWorkItemInput;\n };\n requestContext?: RequestContext;\n}\n\nexport class WorkItemRepositoryError extends Error {\n constructor(\n readonly code: 'repository_mismatch' | 'repository_ambiguous' | 'repository_unlinked',\n message: string,\n readonly candidates: string[] = [],\n ) {\n super(message);\n this.name = 'WorkItemRepositoryError';\n }\n}\n\nexport class FactoryStartTransitionError extends Error {\n readonly result: Extract<FactoryTransitionResult, { status: 'rejected' }>;\n\n constructor(result: Extract<FactoryTransitionResult, { status: 'rejected' }>) {\n super(result.reason);\n this.name = 'FactoryStartTransitionError';\n this.result = result;\n }\n}\n\nexport interface FactoryStartPreparedResult {\n workItemId: string;\n bindingId: string;\n threadId: string;\n resourceId: string;\n sessionId: string;\n branch: string;\n revision: number;\n kickoffStatus: 'pending' | 'leased' | 'retry' | 'sent' | 'failed';\n replayed: boolean;\n}\n\ntype FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nasync function resolveSourceSession(\n storage: SourceControlStorageHandle,\n request: FactoryStartRequest,\n): Promise<SourceControlSession & { projectRepositoryId: string }> {\n const session = await storage.sessions.getBySessionId(request.sessionId);\n if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n throw new Error('Factory session not found');\n }\n const projectRepositoryId = session.projectRepositoryId;\n if (!projectRepositoryId) throw new Error('Factory session repository not found');\n const projectRepository = await storage.projectRepositories.get({ orgId: request.orgId, id: projectRepositoryId });\n if (!projectRepository) throw new Error('Factory session repository not found');\n const connection = await storage.connections.get({ orgId: request.orgId, id: projectRepository.connectionId });\n if (!connection || connection.factoryProjectId !== request.factoryProjectId) {\n throw new Error('Factory session does not belong to this project');\n }\n return { ...session, projectRepositoryId };\n}\n\nasync function configureThread(session: FactorySession, request: FactoryStartRequest): Promise<string> {\n const threadId = session.thread.requireId();\n // Derived from the work item, not a user rename, so leave the title\n // unpinned and let auto-naming keep refining it.\n await session.thread.rename({ title: request.threadTitle, pin: false });\n await session.thread.setSetting({ key: 'factorySessionId', value: request.sessionId });\n return threadId;\n}\n\nexport class FactoryStartCoordinator {\n readonly #controller: FactoryController;\n readonly #storage: WorkItemsStorage;\n readonly #transitionService?: Pick<FactoryTransitionService, 'transition'>;\n readonly #sourceControl?:\n | SourceControlStorageHandle\n | ((\n request: FactoryStartRequest,\n ) => SourceControlStorageHandle | undefined | Promise<SourceControlStorageHandle | undefined>);\n readonly #intake?: IntakeStorage;\n readonly #projects?: FactoryProjectsStorage;\n readonly #modelDefaults?: ModelDefaultsStorage;\n\n constructor(\n controller: FactoryController,\n storage: WorkItemsStorage,\n transitionService?: Pick<FactoryTransitionService, 'transition'>,\n sourceControl?:\n | SourceControlStorageHandle\n | ((\n request: FactoryStartRequest,\n ) => SourceControlStorageHandle | undefined | Promise<SourceControlStorageHandle | undefined>),\n intake?: IntakeStorage,\n sessionModel?: { projects: FactoryProjectsStorage; modelDefaults: ModelDefaultsStorage },\n ) {\n this.#controller = controller;\n this.#storage = storage;\n this.#transitionService = transitionService;\n this.#sourceControl = sourceControl;\n this.#intake = intake;\n this.#projects = sessionModel?.projects;\n this.#modelDefaults = sessionModel?.modelDefaults;\n }\n\n async prepare(request: FactoryStartRequest): Promise<FactoryStartPreparedResult> {\n const storage = this.#storage;\n const sourceControl =\n typeof this.#sourceControl === 'function' ? await this.#sourceControl(request) : this.#sourceControl;\n if (!sourceControl) throw new Error('Factory source control storage is unavailable');\n const sourceSession = await resolveSourceSession(sourceControl, request);\n const sessionModel =\n this.#projects && this.#modelDefaults\n ? await resolveFactorySessionModel({\n projects: this.#projects,\n modelDefaults: this.#modelDefaults,\n factoryProjectId: request.factoryProjectId,\n orgId: request.orgId,\n ownerUserId: sourceSession.userId,\n context: 'run',\n })\n : { modelScope: request.modelScope, modelId: request.defaultModelId };\n const storedItem = request.workItem.id\n ? await storage.get({ orgId: request.orgId, id: request.workItem.id })\n : null;\n const intakeConfig = await this.#intake?.getConfig({ orgId: request.orgId, integrationIds: ['linear'] });\n let repository = await resolveWorkItemRepository({\n sourceControl,\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n item: storedItem ?? { metadata: request.workItem.input.metadata ?? null },\n linearRepositoryMap: intakeConfig?.linear?.repositoryByLinearProject,\n });\n if (repository.status === 'ambiguous') {\n // A retry of this role's existing session keeps its already-chosen\n // repository; a newly minted session must not choose for the card.\n if (storedItem?.sessions[request.workItem.role]?.sessionId !== sourceSession.sessionId) {\n throw new WorkItemRepositoryError(\n 'repository_ambiguous',\n `Choose a repository for this work item: ${repository.candidates.join(', ')}.`,\n repository.candidates,\n );\n }\n const link = await sourceControl.projectRepositories.get({\n orgId: request.orgId,\n id: sourceSession.projectRepositoryId,\n });\n const linked = link && (await sourceControl.repositories.get({ orgId: request.orgId, id: link.repositoryId }));\n const currentLinks = link\n ? await sourceControl.projectRepositories.list({ orgId: request.orgId, connectionId: link.connectionId })\n : [];\n if (\n !linked ||\n !repository.candidates.includes(linked.slug) ||\n !currentLinks.some(candidate => candidate.id === sourceSession.projectRepositoryId)\n ) {\n throw new WorkItemRepositoryError(\n 'repository_unlinked',\n 'The existing session repository is not linked to this Factory.',\n );\n }\n repository = { status: 'resolved', slug: linked.slug, projectRepositoryId: sourceSession.projectRepositoryId };\n }\n if (repository.status === 'unlinked') {\n throw new WorkItemRepositoryError('repository_unlinked', repository.hint);\n }\n if (repository.projectRepositoryId !== sourceSession.projectRepositoryId) {\n throw new WorkItemRepositoryError(\n 'repository_mismatch',\n `This run session is bound to a different repository than ${repository.slug}.`,\n );\n }\n const requestContext = request.requestContext ?? new RequestContext();\n // Preserve an authenticated caller when present. Autonomous starts still\n // need a principal so deployed credential resolution can authenticate the\n // request before selecting the stamped session owner's exclusive scope.\n const existingUser = requestContext.get('user');\n if (!existingUser || typeof existingUser !== 'object') {\n requestContext.set('user', {\n workosId: request.userId,\n organizationId: request.orgId,\n });\n }\n // Sessions kicked off against third-party content (a PR under review, or\n // any pull-request-sourced work item) get `untrustedCheckout` so the SDK\n // never ingests the checkout's AGENTS.md/CLAUDE.md into the system prompt\n // or reminders — those files are attacker-writable in a PR branch.\n const untrustedCheckout = request.workItem.input.externalSource?.type === 'pull-request';\n // The trusted ref the SDK may serve project instruction files from on an\n // untrusted checkout (the PR's base branch). Prefer the session record's\n // base branch; fall back to the intake metadata captured from the PR.\n const metadataBaseBranch = request.workItem.input.metadata?.baseBranch;\n const baseRef =\n (sourceSession.baseBranch || undefined) ??\n (typeof metadataBaseBranch === 'string' && metadataBaseBranch ? metadataBaseBranch : undefined);\n const sessionTags = {\n factoryProjectId: request.factoryProjectId,\n projectRepositoryId: sourceSession.projectRepositoryId,\n };\n const session = await this.#controller.createSession({\n id: sourceSession.sessionId,\n ownerId: request.userId,\n resourceId: sourceSession.sessionId,\n threadId: sourceSession.sessionId,\n requestContext,\n tags: sessionTags,\n });\n // Bound-agent authority gates (the transition tool, the factory-phase\n // processor, workspace token selection) resolve the session address from\n // controller state. Seed it server-side — `tags` covers fresh creation,\n // the explicit setState covers get-or-create returning a session another\n // caller created without them — so autonomous runs never depend on a\n // browser connecting to populate the state. `untrustedCheckout` is a\n // boolean so it rides only on state (tags are string-valued).\n const targetRepositoryInstruction = `Target repository: ${repository.slug}. Before editing files or creating a pull request, verify this session's checkout matches the target repository.`;\n const existingPluginInstructions = session.state.get()?.pluginInstructions ?? [];\n await session.state.set({\n ...sessionTags,\n // The authoritative org id for every downstream identity read (the\n // memory seam's organizationId): the session owner is a USER id, not an\n // org, so it must never be improvised from ownerId.\n factoryOrgId: request.orgId,\n factoryModelScope: sessionModel.modelScope,\n factoryOwnerUserId: sourceSession.userId,\n pluginInstructions: [\n ...existingPluginInstructions.filter(instruction => instruction !== targetRepositoryInstruction),\n targetRepositoryInstruction,\n ],\n ...(untrustedCheckout ? { untrustedCheckout: true, ...(baseRef ? { baseRef } : {}) } : {}),\n });\n await primeTenantCredentialsForTenant({\n orgId: request.orgId,\n userId: sourceSession.userId,\n credentialScope: sessionModel.modelScope,\n });\n // Apply the factory's main model before any model-dependent run work.\n // Observational-memory settings are loaded per invocation by the input processor.\n await hydrateFactorySession(session, {\n orgId: request.orgId,\n modelScope: sessionModel.modelScope,\n ownerUserId: sourceSession.userId,\n defaultModelId: sessionModel.modelId,\n });\n // The tool is `requireApproval`, and that prompt parks the run whether or not\n // someone pressed Start — a person is reading the plan, not an approval queue.\n // Starting the run was already the say-so; the rules engine still governs.\n await session.permissions.setForTool({ toolName: 'factory_transition_work_item', policy: 'allow' });\n const threadId = await configureThread(session, request);\n const prepared = await storage.prepareRunStart({\n orgId: request.orgId,\n userId: request.userId,\n factoryProjectId: request.factoryProjectId,\n workItem: { id: request.workItem.id, input: request.workItem.input },\n role: request.workItem.role,\n session: { sessionId: sourceSession.sessionId, branch: sourceSession.branch, threadId },\n resourceId: sourceSession.sessionId,\n kickoffKey: request.kickoffKey,\n kickoffMessage: null,\n });\n await session.thread.setSetting({ key: 'factoryWorkItemId', value: prepared.item.id });\n\n let revision = prepared.item.revision;\n const destinationStage = request.destinationStage;\n if (destinationStage && (prepared.item.stages.length !== 1 || prepared.item.stages[0] !== destinationStage)) {\n if (!this.#transitionService) throw new Error('Factory transition service is unavailable.');\n const transition = await this.#transitionService.transition({\n orgId: request.orgId,\n factoryProjectId: request.factoryProjectId,\n workItemId: prepared.item.id,\n board: boardForWorkItem(prepared.item),\n stage: destinationStage,\n expectedRevision: prepared.item.revision,\n actor: { type: 'human', id: request.userId },\n ingress: { type: 'human', identity: `start:${request.kickoffKey}:transition` },\n cause: 'run_start',\n });\n if (transition.status === 'rejected') {\n await storage.markPendingStart(prepared.binding.id, 'failed', transition.reason);\n throw new FactoryStartTransitionError(transition);\n }\n revision = transition.revision;\n }\n\n await storage.markPendingStart(prepared.binding.id, 'sent');\n prepared.pendingStart.status = 'sent';\n\n return {\n workItemId: prepared.item.id,\n bindingId: prepared.binding.id,\n threadId,\n resourceId: sourceSession.sessionId,\n sessionId: sourceSession.sessionId,\n branch: sourceSession.branch,\n revision,\n kickoffStatus: prepared.pendingStart.status,\n replayed: prepared.replayed,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,18 @@ import type { RequestContext } from '@mastra/core/request-context';
|
|
|
3
3
|
import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
4
4
|
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
5
5
|
import type { SourceControlSessionLookup } from './factory-session.js';
|
|
6
|
+
interface FactoryModelPolicyDependencies {
|
|
7
|
+
projects: Pick<FactoryProjectsStorage, 'getById'>;
|
|
8
|
+
sourceControl: Pick<SourceControlSessionLookup, 'getBySessionId'>;
|
|
9
|
+
workItems: Pick<WorkItemsStorage, 'findRunBindingByThread'>;
|
|
10
|
+
}
|
|
6
11
|
/** Resolve billing authority from authenticated identity and durable bindings, never session state. */
|
|
7
|
-
export declare function resolveFactoryCredentialTenant(requestContext: RequestContext, deps:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
export declare function resolveFactoryCredentialTenant(requestContext: RequestContext, deps: FactoryModelPolicyDependencies): Promise<CredentialTenant | undefined>;
|
|
13
|
+
/** Shared invocation and model-picker policy; no client-supplied owner or scope is trusted. */
|
|
14
|
+
export declare function resolveFactoryModelPolicy(requestContext: RequestContext, deps: FactoryModelPolicyDependencies): Promise<{
|
|
15
|
+
tenant: CredentialTenant;
|
|
16
|
+
factoryProjectId?: string;
|
|
17
|
+
factoryDefaultModelId?: string | null;
|
|
18
|
+
} | undefined>;
|
|
19
|
+
export {};
|
|
12
20
|
//# sourceMappingURL=credential-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-policy.d.ts","sourceRoot":"","sources":["../../src/session/credential-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,6CAA6C,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"credential-policy.d.ts","sourceRoot":"","sources":["../../src/session/credential-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,6CAA6C,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEvE,UAAU,8BAA8B;IACtC,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;IAClD,aAAa,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,CAAC;IAClE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;CAC7D;AAED,uGAAuG;AACvG,wBAAsB,8BAA8B,CAClD,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAEvC;AAED,+FAA+F;AAC/F,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,SAAS,CAAC,CA0DrH"}
|
|
@@ -1,24 +1,33 @@
|
|
|
1
|
+
import { effectiveModelScope } from "../storage/domains/projects/base.js";
|
|
1
2
|
import { parseSupervisorResourceId } from "../supervisor/session.js";
|
|
2
3
|
import { resolveAuthenticatedCredentialTenant } from "@mastra/code-sdk/agents/credential-resolver";
|
|
3
4
|
//#region src/session/credential-policy.ts
|
|
4
5
|
/** Resolve billing authority from authenticated identity and durable bindings, never session state. */
|
|
5
6
|
async function resolveFactoryCredentialTenant(requestContext, deps) {
|
|
7
|
+
return (await resolveFactoryModelPolicy(requestContext, deps))?.tenant;
|
|
8
|
+
}
|
|
9
|
+
/** Shared invocation and model-picker policy; no client-supplied owner or scope is trusted. */
|
|
10
|
+
async function resolveFactoryModelPolicy(requestContext, deps) {
|
|
6
11
|
const principal = resolveAuthenticatedCredentialTenant(requestContext);
|
|
7
12
|
if (!principal) return void 0;
|
|
8
13
|
const controller = requestContext.get("controller");
|
|
9
|
-
if (!controller?.resourceId) return principal;
|
|
14
|
+
if (!controller?.resourceId) return { tenant: principal };
|
|
10
15
|
const supervisorProjectId = parseSupervisorResourceId(controller.resourceId);
|
|
11
16
|
if (supervisorProjectId) {
|
|
12
17
|
const project = await deps.projects.getById({ id: supervisorProjectId });
|
|
13
18
|
if (!project || project.orgId !== principal.orgId) throw new Error("Factory model policy is unavailable.");
|
|
14
19
|
return {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
factoryProjectId: project.id,
|
|
21
|
+
factoryDefaultModelId: project.defaultModelId,
|
|
22
|
+
tenant: {
|
|
23
|
+
orgId: project.orgId,
|
|
24
|
+
userId: project.createdBy,
|
|
25
|
+
credentialScope: effectiveModelScope(project.modelScope, "run")
|
|
26
|
+
}
|
|
18
27
|
};
|
|
19
28
|
}
|
|
20
29
|
const sourceSession = await deps.sourceControl.getBySessionId(controller.resourceId);
|
|
21
|
-
const binding = principal.orgId && controller.threadId ? await deps.workItems.
|
|
30
|
+
const binding = principal.orgId && controller.threadId ? await deps.workItems.findRunBindingByThread({
|
|
22
31
|
orgId: principal.orgId,
|
|
23
32
|
resourceId: controller.resourceId,
|
|
24
33
|
sessionId: controller.resourceId,
|
|
@@ -26,7 +35,7 @@ async function resolveFactoryCredentialTenant(requestContext, deps) {
|
|
|
26
35
|
rejectAmbiguous: true
|
|
27
36
|
}) : null;
|
|
28
37
|
if (!sourceSession) {
|
|
29
|
-
if (!binding && (controller.resourceId === principal.userId || controller.resourceId.startsWith("channel:"))) return principal;
|
|
38
|
+
if (!binding && (controller.resourceId === principal.userId || controller.resourceId.startsWith("channel:"))) return { tenant: principal };
|
|
30
39
|
throw new Error("Factory session ownership is unavailable.");
|
|
31
40
|
}
|
|
32
41
|
if (sourceSession.orgId !== principal.orgId || sourceSession.visibility === "private" && sourceSession.userId !== principal.userId) throw new Error("Factory session is not available to the current user.");
|
|
@@ -35,12 +44,16 @@ async function resolveFactoryCredentialTenant(requestContext, deps) {
|
|
|
35
44
|
const project = await deps.projects.getById({ id: projectId });
|
|
36
45
|
if (!project || project.orgId !== sourceSession.orgId) throw new Error("Factory model policy is unavailable.");
|
|
37
46
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
factoryProjectId: project.id,
|
|
48
|
+
factoryDefaultModelId: project.defaultModelId,
|
|
49
|
+
tenant: {
|
|
50
|
+
orgId: project.orgId,
|
|
51
|
+
userId: sourceSession.userId,
|
|
52
|
+
credentialScope: effectiveModelScope(project.modelScope, binding ? "run" : "session")
|
|
53
|
+
}
|
|
41
54
|
};
|
|
42
55
|
}
|
|
43
56
|
//#endregion
|
|
44
|
-
export { resolveFactoryCredentialTenant };
|
|
57
|
+
export { resolveFactoryCredentialTenant, resolveFactoryModelPolicy };
|
|
45
58
|
|
|
46
59
|
//# sourceMappingURL=credential-policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-policy.js","names":[],"sources":["../../src/session/credential-policy.ts"],"sourcesContent":["import {\n resolveAuthenticatedCredentialTenant,\n type CredentialTenant,\n} from '@mastra/code-sdk/agents/credential-resolver';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { parseSupervisorResourceId } from '../supervisor/session.js';\nimport type { SourceControlSessionLookup } from './factory-session.js';\n\n/** Resolve billing authority from authenticated identity and durable bindings, never session state. */\nexport async function resolveFactoryCredentialTenant(\n requestContext: RequestContext,\n deps: {\n
|
|
1
|
+
{"version":3,"file":"credential-policy.js","names":[],"sources":["../../src/session/credential-policy.ts"],"sourcesContent":["import {\n resolveAuthenticatedCredentialTenant,\n type CredentialTenant,\n} from '@mastra/code-sdk/agents/credential-resolver';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { effectiveModelScope } from '../storage/domains/projects/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { parseSupervisorResourceId } from '../supervisor/session.js';\nimport type { SourceControlSessionLookup } from './factory-session.js';\n\ninterface FactoryModelPolicyDependencies {\n projects: Pick<FactoryProjectsStorage, 'getById'>;\n sourceControl: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n workItems: Pick<WorkItemsStorage, 'findRunBindingByThread'>;\n}\n\n/** Resolve billing authority from authenticated identity and durable bindings, never session state. */\nexport async function resolveFactoryCredentialTenant(\n requestContext: RequestContext,\n deps: FactoryModelPolicyDependencies,\n): Promise<CredentialTenant | undefined> {\n return (await resolveFactoryModelPolicy(requestContext, deps))?.tenant;\n}\n\n/** Shared invocation and model-picker policy; no client-supplied owner or scope is trusted. */\nexport async function resolveFactoryModelPolicy(\n requestContext: RequestContext,\n deps: FactoryModelPolicyDependencies,\n): Promise<{ tenant: CredentialTenant; factoryProjectId?: string; factoryDefaultModelId?: string | null } | undefined> {\n const principal = resolveAuthenticatedCredentialTenant(requestContext);\n if (!principal) return undefined;\n const controller = requestContext.get('controller') as AgentControllerRequestContext | undefined;\n if (!controller?.resourceId) return { tenant: principal };\n\n const supervisorProjectId = parseSupervisorResourceId(controller.resourceId);\n if (supervisorProjectId) {\n const project = await deps.projects.getById({ id: supervisorProjectId });\n if (!project || project.orgId !== principal.orgId) throw new Error('Factory model policy is unavailable.');\n // Supervisor turns are Factory-owned automation, so a hybrid Factory bills them as runs.\n return {\n factoryProjectId: project.id,\n factoryDefaultModelId: project.defaultModelId,\n tenant: {\n orgId: project.orgId,\n userId: project.createdBy,\n credentialScope: effectiveModelScope(project.modelScope, 'run'),\n },\n };\n }\n\n const sourceSession = await deps.sourceControl.getBySessionId(controller.resourceId);\n const binding =\n principal.orgId && controller.threadId\n ? await deps.workItems.findRunBindingByThread({\n orgId: principal.orgId,\n resourceId: controller.resourceId,\n sessionId: controller.resourceId,\n threadId: controller.threadId,\n rejectAmbiguous: true,\n })\n : null;\n if (!sourceSession) {\n if (!binding && (controller.resourceId === principal.userId || controller.resourceId.startsWith('channel:')))\n return { tenant: principal };\n throw new Error('Factory session ownership is unavailable.');\n }\n if (\n sourceSession.orgId !== principal.orgId ||\n (sourceSession.visibility === 'private' && sourceSession.userId !== principal.userId)\n ) {\n throw new Error('Factory session is not available to the current user.');\n }\n if (!sourceSession.factoryProjectId) throw new Error('Factory session ownership is unavailable.');\n const projectId = binding?.factoryProjectId ?? sourceSession.factoryProjectId;\n const project = await deps.projects.getById({ id: projectId });\n if (!project || project.orgId !== sourceSession.orgId) throw new Error('Factory model policy is unavailable.');\n // Work-item threads keep their billing scope after revocation; history never restores run authority.\n return {\n factoryProjectId: project.id,\n factoryDefaultModelId: project.defaultModelId,\n tenant: {\n orgId: project.orgId,\n userId: sourceSession.userId,\n credentialScope: effectiveModelScope(project.modelScope, binding ? 'run' : 'session'),\n },\n };\n}\n"],"mappings":";;;;;AAoBA,eAAsB,+BACpB,gBACA,MACuC;CACvC,QAAQ,MAAM,0BAA0B,gBAAgB,IAAI,EAAA,EAAI;AAClE;;AAGA,eAAsB,0BACpB,gBACA,MACqH;CACrH,MAAM,YAAY,qCAAqC,cAAc;CACrE,IAAI,CAAC,WAAW,OAAO,KAAA;CACvB,MAAM,aAAa,eAAe,IAAI,YAAY;CAClD,IAAI,CAAC,YAAY,YAAY,OAAO,EAAE,QAAQ,UAAU;CAExD,MAAM,sBAAsB,0BAA0B,WAAW,UAAU;CAC3E,IAAI,qBAAqB;EACvB,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,EAAE,IAAI,oBAAoB,CAAC;EACvE,IAAI,CAAC,WAAW,QAAQ,UAAU,UAAU,OAAO,MAAM,IAAI,MAAM,sCAAsC;EAEzG,OAAO;GACL,kBAAkB,QAAQ;GAC1B,uBAAuB,QAAQ;GAC/B,QAAQ;IACN,OAAO,QAAQ;IACf,QAAQ,QAAQ;IAChB,iBAAiB,oBAAoB,QAAQ,YAAY,KAAK;GAChE;EACF;CACF;CAEA,MAAM,gBAAgB,MAAM,KAAK,cAAc,eAAe,WAAW,UAAU;CACnF,MAAM,UACJ,UAAU,SAAS,WAAW,WAC1B,MAAM,KAAK,UAAU,uBAAuB;EAC1C,OAAO,UAAU;EACjB,YAAY,WAAW;EACvB,WAAW,WAAW;EACtB,UAAU,WAAW;EACrB,iBAAiB;CACnB,CAAC,IACD;CACN,IAAI,CAAC,eAAe;EAClB,IAAI,CAAC,YAAY,WAAW,eAAe,UAAU,UAAU,WAAW,WAAW,WAAW,UAAU,IACxG,OAAO,EAAE,QAAQ,UAAU;EAC7B,MAAM,IAAI,MAAM,2CAA2C;CAC7D;CACA,IACE,cAAc,UAAU,UAAU,SACjC,cAAc,eAAe,aAAa,cAAc,WAAW,UAAU,QAE9E,MAAM,IAAI,MAAM,uDAAuD;CAEzE,IAAI,CAAC,cAAc,kBAAkB,MAAM,IAAI,MAAM,2CAA2C;CAChG,MAAM,YAAY,SAAS,oBAAoB,cAAc;CAC7D,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,EAAE,IAAI,UAAU,CAAC;CAC7D,IAAI,CAAC,WAAW,QAAQ,UAAU,cAAc,OAAO,MAAM,IAAI,MAAM,sCAAsC;CAE7G,OAAO;EACL,kBAAkB,QAAQ;EAC1B,uBAAuB,QAAQ;EAC/B,QAAQ;GACN,OAAO,QAAQ;GACf,QAAQ,cAAc;GACtB,iBAAiB,oBAAoB,QAAQ,YAAY,UAAU,QAAQ,SAAS;EACtF;CACF;AACF"}
|
|
@@ -19,6 +19,11 @@ export interface DefaultModelHydrationSession extends DefaultModelApplicableSess
|
|
|
19
19
|
readonly identity: {
|
|
20
20
|
getResourceId(): string;
|
|
21
21
|
};
|
|
22
|
+
model: DefaultModelApplicableSession['model'] & {
|
|
23
|
+
setDefault(args: {
|
|
24
|
+
modelId: string;
|
|
25
|
+
}): void;
|
|
26
|
+
};
|
|
22
27
|
state: {
|
|
23
28
|
get(): Record<string, unknown> | undefined;
|
|
24
29
|
set(updates: Record<string, unknown>): Promise<unknown>;
|
|
@@ -33,7 +38,7 @@ export interface DefaultModelHydrationSession extends DefaultModelApplicableSess
|
|
|
33
38
|
export interface DefaultModelHydrationDependencies {
|
|
34
39
|
sourceControl: Pick<SourceControlSessionLookup, 'getBySessionId'>;
|
|
35
40
|
projects: Pick<FactoryProjectsStorage, 'getById'>;
|
|
36
|
-
workItems: Pick<WorkItemsStorage, '
|
|
41
|
+
workItems: Pick<WorkItemsStorage, 'findRunBindingByThread'>;
|
|
37
42
|
modelDefaults: Pick<ModelDefaultsStorage, 'get'>;
|
|
38
43
|
}
|
|
39
44
|
/** Seed a new chat's scoped default model; a chat with a user message keeps its model. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-model-hydration.d.ts","sourceRoot":"","sources":["../../src/session/default-model-hydration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"default-model-hydration.d.ts","sourceRoot":"","sources":["../../src/session/default-model-hydration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEtF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEvE,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE;QAAE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IACrD,SAAS,EAAE;QAAE,KAAK,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;CAC/F;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B;IACjF,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE,6BAA6B,CAAC,OAAO,CAAC,GAAG;QAAE,UAAU,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,CAAC;IAChG,KAAK,EAAE;QACL,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;QAC3C,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACzD,CAAC;IACF,MAAM,EAAE;QACN,KAAK,IAAI,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QACnC,gBAAgB,CAAC,IAAI,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAChE,CAAC;CACH;AAUD,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,CAAC;IAClE,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;IAClD,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,wBAAwB,CAAC,CAAC;IAC5D,aAAa,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;CAClD;AAED,0FAA0F;AAC1F,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,4BAA4B,EACrC,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,iCAAiC,GACvF,OAAO,CAAC,IAAI,CAAC,CAsEf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { primeTenantCredentialsForTenant } from "../routes/tenant-credentials.js";
|
|
2
|
+
import { effectiveModelScope } from "../storage/domains/projects/base.js";
|
|
2
3
|
//#region src/session/default-model-hydration.ts
|
|
3
4
|
async function applyDefaultModel(session, modelId) {
|
|
4
5
|
await session.model.switch(modelId);
|
|
@@ -37,12 +38,20 @@ async function hydrateSessionDefaultModel(session, { sourceControl, projects, wo
|
|
|
37
38
|
await clearFactoryIdentity();
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
41
|
+
const threadId = session.thread.getId();
|
|
42
|
+
const binding = threadId ? await workItems.findRunBindingByThread({
|
|
43
|
+
orgId: sourceSession.orgId,
|
|
44
|
+
threadId,
|
|
45
|
+
resourceId,
|
|
46
|
+
sessionId: resourceId,
|
|
47
|
+
rejectAmbiguous: true
|
|
48
|
+
}) : null;
|
|
49
|
+
const project = await projects.getById({ id: binding?.factoryProjectId ?? sourceSession.factoryProjectId });
|
|
50
|
+
if (!project || project.orgId !== sourceSession.orgId) {
|
|
42
51
|
await clearFactoryIdentity();
|
|
43
52
|
return;
|
|
44
53
|
}
|
|
45
|
-
const modelScope = project.modelScope
|
|
54
|
+
const modelScope = effectiveModelScope(project.modelScope, binding ? "run" : "session");
|
|
46
55
|
await session.state.set({
|
|
47
56
|
factoryOrgId: sourceSession.orgId,
|
|
48
57
|
factoryModelScope: modelScope,
|
|
@@ -54,23 +63,15 @@ async function hydrateSessionDefaultModel(session, { sourceControl, projects, wo
|
|
|
54
63
|
userId: sourceSession.userId,
|
|
55
64
|
credentialScope: modelScope
|
|
56
65
|
});
|
|
57
|
-
|
|
58
|
-
if (threadId && await workItems.findActiveRunBindingByThread({
|
|
59
|
-
orgId: sourceSession.orgId,
|
|
60
|
-
threadId,
|
|
61
|
-
resourceId,
|
|
62
|
-
sessionId: resourceId
|
|
63
|
-
})) return;
|
|
66
|
+
if (binding) return;
|
|
64
67
|
if (threadId && await session.thread.firstUserMessage({ threadId })) return;
|
|
65
|
-
|
|
66
|
-
if (project.defaultModelId) await applyDefaultModel(session, project.defaultModelId);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const modelDefault = await modelDefaults.get({
|
|
68
|
+
const scopedModelId = modelScope === "factory" ? project.defaultModelId : (await modelDefaults.get({
|
|
70
69
|
orgId: sourceSession.orgId,
|
|
71
70
|
userId: sourceSession.userId
|
|
72
|
-
});
|
|
73
|
-
if (
|
|
71
|
+
}))?.modelId;
|
|
72
|
+
if (!scopedModelId) return;
|
|
73
|
+
session.model.setDefault({ modelId: scopedModelId });
|
|
74
|
+
await applyDefaultModel(session, scopedModelId);
|
|
74
75
|
} catch (error) {
|
|
75
76
|
if (!identityResolved && !hadFactoryIdentity) try {
|
|
76
77
|
await failClosedWithoutIdentity();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-model-hydration.js","names":[],"sources":["../../src/session/default-model-hydration.ts"],"sourcesContent":["import { primeTenantCredentialsForTenant } from '../routes/tenant-credentials.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { SourceControlSessionLookup } from './factory-session.js';\n\nexport interface DefaultModelApplicableSession {\n model: { switch(modelId: string): Promise<unknown> };\n subagents: { model: { set(args: { modelId: string; agentType: string }): Promise<unknown> } };\n}\n\nexport interface DefaultModelHydrationSession extends DefaultModelApplicableSession {\n readonly identity: { getResourceId(): string };\n state: {\n get(): Record<string, unknown> | undefined;\n set(updates: Record<string, unknown>): Promise<unknown>;\n };\n thread: {\n getId(): string | null | undefined;\n firstUserMessage(args: { threadId: string }): Promise<unknown>;\n };\n}\n\nasync function applyDefaultModel(session: DefaultModelApplicableSession, modelId: string): Promise<void> {\n await session.model.switch(modelId);\n\n for (const agentType of ['explore', 'plan', 'execute']) {\n await session.subagents.model.set({ modelId, agentType });\n }\n}\n\nexport interface DefaultModelHydrationDependencies {\n sourceControl: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n projects: Pick<FactoryProjectsStorage, 'getById'>;\n workItems: Pick<WorkItemsStorage, '
|
|
1
|
+
{"version":3,"file":"default-model-hydration.js","names":[],"sources":["../../src/session/default-model-hydration.ts"],"sourcesContent":["import { primeTenantCredentialsForTenant } 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 { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { SourceControlSessionLookup } from './factory-session.js';\n\nexport interface DefaultModelApplicableSession {\n model: { switch(modelId: string): Promise<unknown> };\n subagents: { model: { set(args: { modelId: string; agentType: string }): Promise<unknown> } };\n}\n\nexport interface DefaultModelHydrationSession extends DefaultModelApplicableSession {\n readonly identity: { getResourceId(): string };\n model: DefaultModelApplicableSession['model'] & { setDefault(args: { modelId: string }): void };\n state: {\n get(): Record<string, unknown> | undefined;\n set(updates: Record<string, unknown>): Promise<unknown>;\n };\n thread: {\n getId(): string | null | undefined;\n firstUserMessage(args: { threadId: string }): Promise<unknown>;\n };\n}\n\nasync function applyDefaultModel(session: DefaultModelApplicableSession, modelId: string): Promise<void> {\n await session.model.switch(modelId);\n\n for (const agentType of ['explore', 'plan', 'execute']) {\n await session.subagents.model.set({ modelId, agentType });\n }\n}\n\nexport interface DefaultModelHydrationDependencies {\n sourceControl: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n projects: Pick<FactoryProjectsStorage, 'getById'>;\n workItems: Pick<WorkItemsStorage, 'findRunBindingByThread'>;\n modelDefaults: Pick<ModelDefaultsStorage, 'get'>;\n}\n\n/** Seed a new chat's scoped default model; a chat with a user message keeps its model. */\nexport async function hydrateSessionDefaultModel(\n session: DefaultModelHydrationSession,\n { sourceControl, projects, workItems, modelDefaults }: DefaultModelHydrationDependencies,\n): Promise<void> {\n const resourceId = session.identity.getResourceId();\n const initialState = session.state.get() ?? {};\n if (initialState.factoryProjectId || typeof session.thread.firstUserMessage !== 'function') return;\n const hadFactoryIdentity = initialState.factoryModelScope === 'factory' || initialState.factoryModelScope === 'user';\n const clearFactoryIdentity = () =>\n session.state.set({ factoryOrgId: undefined, factoryModelScope: undefined, factoryOwnerUserId: undefined });\n const failClosedWithoutIdentity = () =>\n session.state.set({ factoryModelScope: 'user', factoryOwnerUserId: undefined });\n let identityResolved = false;\n try {\n const sourceSession = await sourceControl.getBySessionId(resourceId);\n if (!sourceSession) {\n await clearFactoryIdentity();\n return;\n }\n if (!sourceSession.factoryProjectId) {\n await clearFactoryIdentity();\n return;\n }\n // Historical thread membership keeps completed work items on Factory billing in hybrid scope.\n const threadId = session.thread.getId();\n const binding = threadId\n ? await workItems.findRunBindingByThread({\n orgId: sourceSession.orgId,\n threadId,\n resourceId,\n sessionId: resourceId,\n rejectAmbiguous: true,\n })\n : null;\n const project = await projects.getById({ id: binding?.factoryProjectId ?? sourceSession.factoryProjectId });\n if (!project || project.orgId !== sourceSession.orgId) {\n await clearFactoryIdentity();\n return;\n }\n const modelScope = effectiveModelScope(project.modelScope, binding ? 'run' : 'session');\n await session.state.set({\n factoryOrgId: sourceSession.orgId,\n factoryModelScope: modelScope,\n factoryOwnerUserId: sourceSession.userId,\n });\n identityResolved = true;\n await primeTenantCredentialsForTenant({\n orgId: sourceSession.orgId,\n userId: sourceSession.userId,\n credentialScope: modelScope,\n });\n\n if (binding) return;\n if (threadId && (await session.thread.firstUserMessage({ threadId }))) return;\n\n const scopedModelId =\n modelScope === 'factory'\n ? project.defaultModelId\n : (await modelDefaults.get({ orgId: sourceSession.orgId, userId: sourceSession.userId }))?.modelId;\n if (!scopedModelId) return;\n // A thread the session creates later (the UI binding its conventional\n // thread after a thread-agnostic request opened the session) starts from\n // the session default, not from the thread hydrated here.\n session.model.setDefault({ modelId: scopedModelId });\n await applyDefaultModel(session, scopedModelId);\n } catch (error) {\n if (!identityResolved && !hadFactoryIdentity) {\n try {\n await failClosedWithoutIdentity();\n } catch {}\n }\n console.warn('[Factory default-model hydration] Unable to apply the scoped default model.', error);\n }\n}\n"],"mappings":";;;AAyBA,eAAe,kBAAkB,SAAwC,SAAgC;CACvG,MAAM,QAAQ,MAAM,OAAO,OAAO;CAElC,KAAK,MAAM,aAAa;EAAC;EAAW;EAAQ;CAAS,GACnD,MAAM,QAAQ,UAAU,MAAM,IAAI;EAAE;EAAS;CAAU,CAAC;AAE5D;;AAUA,eAAsB,2BACpB,SACA,EAAE,eAAe,UAAU,WAAW,iBACvB;CACf,MAAM,aAAa,QAAQ,SAAS,cAAc;CAClD,MAAM,eAAe,QAAQ,MAAM,IAAI,KAAK,CAAC;CAC7C,IAAI,aAAa,oBAAoB,OAAO,QAAQ,OAAO,qBAAqB,YAAY;CAC5F,MAAM,qBAAqB,aAAa,sBAAsB,aAAa,aAAa,sBAAsB;CAC9G,MAAM,6BACJ,QAAQ,MAAM,IAAI;EAAE,cAAc,KAAA;EAAW,mBAAmB,KAAA;EAAW,oBAAoB,KAAA;CAAU,CAAC;CAC5G,MAAM,kCACJ,QAAQ,MAAM,IAAI;EAAE,mBAAmB;EAAQ,oBAAoB,KAAA;CAAU,CAAC;CAChF,IAAI,mBAAmB;CACvB,IAAI;EACF,MAAM,gBAAgB,MAAM,cAAc,eAAe,UAAU;EACnE,IAAI,CAAC,eAAe;GAClB,MAAM,qBAAqB;GAC3B;EACF;EACA,IAAI,CAAC,cAAc,kBAAkB;GACnC,MAAM,qBAAqB;GAC3B;EACF;EAEA,MAAM,WAAW,QAAQ,OAAO,MAAM;EACtC,MAAM,UAAU,WACZ,MAAM,UAAU,uBAAuB;GACrC,OAAO,cAAc;GACrB;GACA;GACA,WAAW;GACX,iBAAiB;EACnB,CAAC,IACD;EACJ,MAAM,UAAU,MAAM,SAAS,QAAQ,EAAE,IAAI,SAAS,oBAAoB,cAAc,iBAAiB,CAAC;EAC1G,IAAI,CAAC,WAAW,QAAQ,UAAU,cAAc,OAAO;GACrD,MAAM,qBAAqB;GAC3B;EACF;EACA,MAAM,aAAa,oBAAoB,QAAQ,YAAY,UAAU,QAAQ,SAAS;EACtF,MAAM,QAAQ,MAAM,IAAI;GACtB,cAAc,cAAc;GAC5B,mBAAmB;GACnB,oBAAoB,cAAc;EACpC,CAAC;EACD,mBAAmB;EACnB,MAAM,gCAAgC;GACpC,OAAO,cAAc;GACrB,QAAQ,cAAc;GACtB,iBAAiB;EACnB,CAAC;EAED,IAAI,SAAS;EACb,IAAI,YAAa,MAAM,QAAQ,OAAO,iBAAiB,EAAE,SAAS,CAAC,GAAI;EAEvE,MAAM,gBACJ,eAAe,YACX,QAAQ,kBACP,MAAM,cAAc,IAAI;GAAE,OAAO,cAAc;GAAO,QAAQ,cAAc;EAAO,CAAC,EAAA,EAAI;EAC/F,IAAI,CAAC,eAAe;EAIpB,QAAQ,MAAM,WAAW,EAAE,SAAS,cAAc,CAAC;EACnD,MAAM,kBAAkB,SAAS,aAAa;CAChD,SAAS,OAAO;EACd,IAAI,CAAC,oBAAoB,CAAC,oBACxB,IAAI;GACF,MAAM,0BAA0B;EAClC,QAAQ,CAAC;EAEX,QAAQ,KAAK,+EAA+E,KAAK;CACnG;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MastraCodeState } from '@mastra/code-sdk/schema';
|
|
2
2
|
import type { AgentController } from '@mastra/core/agent-controller';
|
|
3
3
|
import type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { EffectiveModelScope, FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
5
5
|
import { type SourceControlSession, type SourceControlStorageHandle } from '../storage/domains/source-control/base.js';
|
|
6
6
|
type FactorySession = Awaited<ReturnType<AgentController<MastraCodeState>['createSession']>>;
|
|
7
7
|
export declare class FactorySourceControlConflictError extends Error {
|
|
@@ -13,8 +13,10 @@ export declare function resolveFactorySessionModel(args: {
|
|
|
13
13
|
factoryProjectId: string | undefined;
|
|
14
14
|
orgId: string;
|
|
15
15
|
ownerUserId: string;
|
|
16
|
+
/** A work-item run or an interactive session; a hybrid Factory resolves a different scope for each. */
|
|
17
|
+
context: 'run' | 'session';
|
|
16
18
|
}): Promise<{
|
|
17
|
-
modelScope:
|
|
19
|
+
modelScope: EffectiveModelScope;
|
|
18
20
|
modelId?: string;
|
|
19
21
|
}>;
|
|
20
22
|
export interface SourceControlSessionLookup {
|
|
@@ -140,7 +142,7 @@ export declare function resolvePrimaryEnvironmentRepository(args: {
|
|
|
140
142
|
export declare function ensureFactorySourceSession(args: EnsureFactorySourceSessionArgs): Promise<EnsuredFactorySourceSession>;
|
|
141
143
|
export interface HydrateFactorySessionArgs {
|
|
142
144
|
orgId: string;
|
|
143
|
-
modelScope:
|
|
145
|
+
modelScope: EffectiveModelScope;
|
|
144
146
|
ownerUserId: string;
|
|
145
147
|
/** The model selected by the Factory scope. Without it the SDK mode default is applied. */
|
|
146
148
|
defaultModelId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"factory-session.d.ts","sourceRoot":"","sources":["../../src/session/factory-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEtF,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AACvG,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAChC,MAAM,2CAA2C,CAAC;AAInD,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAE7F,qBAAa,iCAAkC,SAAQ,KAAK;CAAG;AAE/D,8EAA8E;AAC9E,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAChD,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,OAAO,EAAE,KAAK,GAAG,SAAS,CAAC;CAC5B,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,mBAAmB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBjE;AAED,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxE,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAAC;IAC3F,MAAM,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,gBAAgB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,uBAAuB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,SAAS,0BAA0B,EAAE,GACpD,0BAA0B,CA2B5B;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,cAAc,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAwClD;AAED,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,aAAa,EAAE,0BAA0B,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,mCAAoC,SAAQ,KAAK;IAChD,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY;gBAAnC,MAAM,EAAE,YAAY,GAAG,YAAY;CAQzD;AAED,MAAM,WAAW,+BAA+B;IAC9C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,6BAA6B,GACrC,CAAC;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG,+BAA+B,CAAC,GACnD;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,wBAAsB,mCAAmC,CAAC,IAAI,EAAE;IAC9D,aAAa,EAAE,0BAA0B,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAiBzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CA6BtC;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCnH"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { effectiveModelScope } from "../storage/domains/projects/base.js";
|
|
1
2
|
import { SourceControlConnectionNotFoundError } from "../storage/domains/source-control/base.js";
|
|
2
3
|
import { resolveEnvironmentRepositories } from "./environment-repositories.js";
|
|
3
4
|
import { seedSessionOrg } from "./org-seed.js";
|
|
@@ -8,7 +9,7 @@ async function resolveFactorySessionModel(args) {
|
|
|
8
9
|
if (!args.projects || !args.factoryProjectId) throw new Error("Factory model policy is unavailable.");
|
|
9
10
|
const project = await args.projects.getById({ id: args.factoryProjectId });
|
|
10
11
|
if (!project || project.orgId !== args.orgId) throw new Error("Factory model policy is unavailable.");
|
|
11
|
-
if (project.modelScope
|
|
12
|
+
if (effectiveModelScope(project.modelScope, args.context) === "factory") return {
|
|
12
13
|
modelScope: "factory",
|
|
13
14
|
...project.defaultModelId ? { modelId: project.defaultModelId } : {}
|
|
14
15
|
};
|
|
@@ -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 type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { FactoryModelScope, 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}): Promise<{ modelScope: FactoryModelScope; 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 (project.modelScope !== 'user') {\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: FactoryModelScope;\n ownerUserId: string;\n /** The model selected by the Factory scope. Without it the SDK mode default is applied. */\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(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\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 const selectedModelId = args.defaultModelId ?? session.mode.resolve().defaultModelId;\n if (!selectedModelId) return;\n\n try {\n await session.model.switch(selectedModelId);\n } catch (error) {\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}\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 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 /** The model selected by the Factory scope. Without it the SDK mode default is applied. */\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(session: FactorySession, args: HydrateFactorySessionArgs): Promise<void> {\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 const selectedModelId = args.defaultModelId ?? session.mode.resolve().defaultModelId;\n if (!selectedModelId) return;\n\n try {\n await session.model.switch(selectedModelId);\n } catch (error) {\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}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { FactoryStorageDomain } from '@mastra/core/storage';
|
|
2
2
|
import type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';
|
|
3
3
|
export declare const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;
|
|
4
|
-
export declare const FACTORY_MODEL_SCOPES: readonly ["factory", "user"];
|
|
4
|
+
export declare const FACTORY_MODEL_SCOPES: readonly ["factory", "user", "hybrid"];
|
|
5
5
|
export type FactoryModelScope = (typeof FACTORY_MODEL_SCOPES)[number];
|
|
6
|
+
/** The single credential scope one session or run resolves from; `hybrid` is never effective. */
|
|
7
|
+
export type EffectiveModelScope = Exclude<FactoryModelScope, 'hybrid'>;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the scope one context runs under. `hybrid` splits by context: work-item runs use the
|
|
10
|
+
* Factory's model and credentials, interactive sessions use the session owner's.
|
|
11
|
+
*/
|
|
12
|
+
export declare function effectiveModelScope(scope: FactoryModelScope, context: 'run' | 'session'): EffectiveModelScope;
|
|
6
13
|
export interface FactoryProject {
|
|
7
14
|
id: string;
|
|
8
15
|
orgId: string;
|
|
@@ -11,10 +18,11 @@ export interface FactoryProject {
|
|
|
11
18
|
description: string | null;
|
|
12
19
|
/** Default model for sessions/runs started under this Factory (null = harness default). */
|
|
13
20
|
defaultModelId: string | null;
|
|
14
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Whose model and credentials sessions and runs use: the Factory's, the session owner's, or
|
|
23
|
+
* `hybrid` (owner's for interactive sessions, the Factory's for work-item runs).
|
|
24
|
+
*/
|
|
15
25
|
modelScope: FactoryModelScope;
|
|
16
|
-
/** Whether new Slack sessions create Work-board items for this Factory. */
|
|
17
|
-
slackWorkItemsEnabled: boolean;
|
|
18
26
|
/** Whether rules may start agent runs on their own; off, a run waits for approval on its card. */
|
|
19
27
|
autoRunEnabled: boolean;
|
|
20
28
|
/** Whether the Factory answers a run's plan itself instead of waiting for a person. */
|
|
@@ -55,13 +63,13 @@ export interface CreateFactoryProjectInput {
|
|
|
55
63
|
name: string;
|
|
56
64
|
description?: string | null;
|
|
57
65
|
defaultModelId?: string | null;
|
|
66
|
+
modelScope?: FactoryModelScope;
|
|
58
67
|
}
|
|
59
68
|
export interface UpdateFactoryProjectInput {
|
|
60
69
|
name?: string;
|
|
61
70
|
description?: string | null;
|
|
62
71
|
defaultModelId?: string | null;
|
|
63
72
|
modelScope?: FactoryModelScope;
|
|
64
|
-
slackWorkItemsEnabled?: boolean;
|
|
65
73
|
autoRunEnabled?: boolean;
|
|
66
74
|
autoApprovePlans?: boolean;
|
|
67
75
|
sandboxWorkingDirectory?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/projects/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEhF,eAAO,MAAM,mCAAmC,KAAK,CAAC;AACtD,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/projects/base.ts"],"names":[],"mappings":"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"}
|