@mastra/factory 0.13.0-alpha.12 → 0.13.0-alpha.14
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/README.md +93 -10
- package/dist/boards/define-board.d.ts +36 -2
- package/dist/boards/define-board.d.ts.map +1 -1
- package/dist/boards/define-board.js +51 -1
- package/dist/boards/define-board.js.map +1 -1
- package/dist/boards/index.d.ts +3 -1
- package/dist/boards/index.d.ts.map +1 -1
- package/dist/boards/index.js +2 -1
- package/dist/boards/index.js.map +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +17 -4
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/semantics.d.ts +28 -0
- package/dist/boards/semantics.d.ts.map +1 -0
- package/dist/boards/semantics.js +41 -0
- package/dist/boards/semantics.js.map +1 -0
- package/dist/boards/work-tool-rules.d.ts +8 -0
- package/dist/boards/work-tool-rules.d.ts.map +1 -0
- package/dist/boards/work-tool-rules.js +20 -0
- package/dist/boards/work-tool-rules.js.map +1 -0
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +14 -1
- package/dist/boards/work.js.map +1 -1
- package/dist/factory.d.ts +5 -6
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +19 -11
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +7 -4
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/github/issue-reconciler.js +4 -3
- package/dist/integrations/github/issue-reconciler.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +3 -2
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +11 -10
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +6 -3
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +21 -2
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/issue-reconciler.d.ts +2 -0
- package/dist/integrations/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/issue-reconciler.js +1 -3
- package/dist/integrations/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/linear/issue-reconciler.js +8 -4
- package/dist/integrations/linear/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts +1 -2
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js +5 -5
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +25 -4
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/linear/integration.js +1 -1
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts +8 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts.map +1 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js +1985 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js.map +1 -0
- package/dist/integrations/slack/emoji.d.ts +3 -0
- package/dist/integrations/slack/emoji.d.ts.map +1 -0
- package/dist/integrations/slack/emoji.js +12 -0
- package/dist/integrations/slack/emoji.js.map +1 -0
- package/dist/integrations/slack/integration.js +1 -1
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +16 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +31 -82
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/contracts.d.ts +601 -0
- package/dist/routes/contracts.d.ts.map +1 -0
- package/dist/routes/contracts.js +450 -0
- package/dist/routes/contracts.js.map +1 -0
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +26 -76
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/supervisor.d.ts +2 -0
- package/dist/routes/supervisor.d.ts.map +1 -1
- package/dist/routes/supervisor.js +7 -6
- package/dist/routes/supervisor.js.map +1 -1
- package/dist/routes/surface.d.ts +4 -4
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +14 -9
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +103 -244
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +3 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +37 -6
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.d.ts +4 -5
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +3 -4
- package/dist/rules/processor.d.ts +3 -3
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +10 -12
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +1 -3
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +1 -4
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +3 -1
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +3 -4
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +7 -7
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +20 -25
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +1 -11
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts +7 -2
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +9 -15
- package/dist/rules/validation.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +9 -3
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +13 -17
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/health-worker.d.ts +2 -0
- package/dist/supervisor/health-worker.d.ts.map +1 -1
- package/dist/supervisor/health-worker.js +3 -1
- package/dist/supervisor/health-worker.js.map +1 -1
- package/dist/supervisor/health.d.ts +10 -12
- package/dist/supervisor/health.d.ts.map +1 -1
- package/dist/supervisor/health.js +11 -40
- package/dist/supervisor/health.js.map +1 -1
- package/dist/supervisor/instructions.d.ts +1 -1
- package/dist/supervisor/instructions.d.ts.map +1 -1
- package/dist/supervisor/instructions.js +10 -8
- package/dist/supervisor/instructions.js.map +1 -1
- package/dist/supervisor/read-tools.d.ts +2 -0
- package/dist/supervisor/read-tools.d.ts.map +1 -1
- package/dist/supervisor/read-tools.js +2 -2
- package/dist/supervisor/read-tools.js.map +1 -1
- package/dist/work-item-branch.d.ts +2 -0
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +6 -1
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +3 -1
- package/dist/workspace.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +18 -10
- package/package.json +9 -8
- package/dist/rules/defaults.d.ts +0 -9
- package/dist/rules/defaults.d.ts.map +0 -1
- package/dist/rules/defaults.js +0 -50
- package/dist/rules/defaults.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-tools.js","names":[],"sources":["../../src/supervisor/read-tools.ts"],"sourcesContent":["/**\n * Supervisor read surface. Every tool is bounded (hard row caps, truncated\n * text) and answers with ids a person can click on the board, so the model\n * can explain a card's state without ever touching the database itself.\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport { FACTORY_RULE_STAGES, factoryRuleStage } from '../rules/types.js';\nimport type { FactoryRuleStage } from '../rules/types.js';\nimport type { AuditStorage } from '../storage/domains/audit/base.js';\nimport type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';\nimport type {\n FactoryDeferredDecisionRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport type { FactoryHealthReport, FactoryHealthThresholds } from './health.js';\nimport { runFactoryHealthCheck } from './health.js';\n\nexport interface SupervisorScope {\n orgId: string;\n factoryProjectId: string;\n}\n\nexport interface SupervisorMessageReader {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n orderBy: { field: 'createdAt'; direction: 'ASC' | 'DESC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n}\n\nexport interface SupervisorReadDependencies {\n scope: SupervisorScope;\n workItems: WorkItemsStorage;\n comments: WorkItemCommentsStorage;\n audit: AuditStorage;\n messageReader?: SupervisorMessageReader;\n healthThresholds?: FactoryHealthThresholds;\n now?: () => Date;\n}\n\nconst MAX_TEXT = 600;\nconst MAX_ERROR = 400;\nconst MAX_LIST = 50;\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\nfunction iso(value: Date | string | null | undefined): string | null {\n if (!value) return null;\n const date = value instanceof Date ? value : new Date(value);\n return Number.isFinite(date.getTime()) ? date.toISOString() : null;\n}\n\nfunction itemNumber(item: WorkItemRow): number | null {\n const number = item.metadata?.number ?? item.metadata?.githubIssueNumber ?? item.metadata?.githubPullRequestNumber;\n return typeof number === 'number' ? number : null;\n}\n\nfunction itemLabels(item: WorkItemRow): string[] {\n const labels = item.metadata?.labels;\n return Array.isArray(labels) ? labels.filter((label): label is string => typeof label === 'string') : [];\n}\n\nfunction summarizeItem(item: WorkItemRow) {\n return {\n id: item.id,\n number: itemNumber(item),\n title: item.title,\n stage: factoryRuleStage(item.stages) ?? item.stages.join('+'),\n source: item.externalSource ? `${item.externalSource.integrationId}:${item.externalSource.type}` : 'manual',\n url: typeof item.metadata?.url === 'string' ? item.metadata.url : null,\n triageType: item.triageType,\n acceptedAt: iso(item.acceptedAt),\n autonomyArmedAt: iso(item.autonomyArmedAt),\n parentWorkItemId: item.parentWorkItemId,\n revision: item.revision,\n updatedAt: iso(item.updatedAt),\n };\n}\n\nfunction summarizeDecision(decision: FactoryDeferredDecisionRecord) {\n const type = typeof decision.decision.type === 'string' ? decision.decision.type : 'decision';\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : null;\n const skill = typeof decision.decision.skillName === 'string' ? decision.decision.skillName : null;\n return {\n id: decision.id,\n workItemId: decision.workItemId,\n type,\n role,\n skill,\n status: decision.status,\n attempts: decision.attempts,\n availableAt: iso(decision.availableAt),\n leaseOwner: decision.leaseOwner,\n leaseExpiresAt: iso(decision.leaseExpiresAt),\n failureCode: decision.failureCode,\n failureLabel: decision.failureCode ? factoryDispatchFailureMetadata(decision.failureCode).label : null,\n canRetry: decision.status === 'failed' ? factoryDispatchFailureMetadata(decision.failureCode).canRetry : false,\n lastError: decision.lastError ? truncate(decision.lastError, MAX_ERROR) : null,\n approvedBy: decision.approvedBy,\n createdAt: iso(decision.createdAt),\n updatedAt: iso(decision.updatedAt),\n completedAt: iso(decision.completedAt),\n };\n}\n\nfunction messageParts(content: unknown): unknown[] {\n if (Array.isArray(content)) return content;\n if (content && typeof content === 'object') {\n const parts = (content as { parts?: unknown }).parts;\n if (Array.isArray(parts)) return parts;\n }\n return [];\n}\n\nfunction messageText(message: MastraDBMessage): string {\n const content: unknown = message.content;\n const parts = messageParts(content);\n const text: string[] = [];\n for (const part of parts) {\n if (part && typeof part === 'object' && (part as { type?: unknown }).type === 'text') {\n const value = (part as { text?: unknown }).text;\n if (typeof value === 'string') text.push(value);\n }\n }\n if (text.length === 0 && typeof content === 'string') text.push(content);\n return text.join('\\n');\n}\n\nfunction messageToolCalls(message: MastraDBMessage): Array<{ tool: string; state: string | null }> {\n const parts = messageParts(message.content);\n const calls: Array<{ tool: string; state: string | null }> = [];\n for (const rawPart of parts) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : typeof part.type === 'string' && part.type.startsWith('tool-')\n ? part\n : undefined;\n if (!invocation) continue;\n const name = invocation.toolName ?? invocation.name ?? (typeof part.type === 'string' ? part.type.slice(5) : null);\n if (typeof name !== 'string') continue;\n calls.push({ tool: name, state: typeof invocation.state === 'string' ? invocation.state : null });\n }\n return calls;\n}\n\nasync function findItem(\n deps: SupervisorReadDependencies,\n ref: { id?: string; number?: number },\n): Promise<WorkItemRow | null> {\n if (ref.id) return deps.workItems.getForProject(deps.scope.orgId, deps.scope.factoryProjectId, ref.id);\n if (ref.number === undefined) return null;\n const items = await deps.workItems.list(deps.scope);\n const matches = items.filter(item => itemNumber(item) === ref.number);\n // Issue and PR numbers share a space on GitHub; prefer the Work-board card.\n return matches.find(item => item.externalSource?.type !== 'pull-request') ?? matches[0] ?? null;\n}\n\nconst itemRefSchema = z\n .object({\n id: z.string().uuid().optional().describe('Work item id.'),\n number: z.number().int().positive().optional().describe('Card number as shown on the board, e.g. 22874.'),\n })\n .refine(ref => ref.id !== undefined || ref.number !== undefined, { message: 'Provide an id or a number.' });\n\nexport function createFactorySupervisorReadTools(deps: SupervisorReadDependencies): IntegrationTools {\n const now = deps.now ?? (() => new Date());\n const scope = deps.scope;\n\n return {\n factory_overview: createTool({\n id: 'factory_overview',\n description:\n 'Counts per pipeline stage, decisions by status, active seats, open proposals and held cards for this Factory. Start here for \"what needs me\" questions.',\n inputSchema: z.object({}).strict(),\n execute: async () => {\n const [items, decisions, bindings, pendingStarts] = await Promise.all([\n deps.workItems.list(scope),\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId),\n deps.workItems.listPendingStarts(scope.orgId, scope.factoryProjectId),\n ]);\n const stages = Object.fromEntries(FACTORY_RULE_STAGES.map(stage => [stage, 0])) as Record<\n FactoryRuleStage,\n number\n >;\n let held = 0;\n for (const item of items) {\n const stage = factoryRuleStage(item.stages);\n if (stage) stages[stage] += 1;\n if (stage === 'triage' && item.triageType && item.triageType !== 'bug' && !item.acceptedAt) held += 1;\n }\n const decisionsByStatus: Record<string, number> = {};\n for (const decision of decisions) {\n decisionsByStatus[decision.status] = (decisionsByStatus[decision.status] ?? 0) + 1;\n }\n const activeSeats = bindings.filter(binding => binding.status === 'active');\n const seatsByRole: Record<string, number> = {};\n for (const seat of activeSeats) seatsByRole[seat.role] = (seatsByRole[seat.role] ?? 0) + 1;\n return {\n checkedAt: now().toISOString(),\n workItems: { total: items.length, byStage: stages, heldForDecision: held },\n decisions: decisionsByStatus,\n openProposals: decisions\n .filter(d => d.status === 'proposed')\n .map(summarizeDecision)\n .slice(0, MAX_LIST),\n failedDecisions: decisions\n .filter(d => d.status === 'failed')\n .map(summarizeDecision)\n .slice(0, MAX_LIST),\n activeSeats: { total: activeSeats.length, byRole: seatsByRole },\n pendingStarts: pendingStarts.filter(start => start.status !== 'sent').length,\n };\n },\n }),\n\n factory_health_check: createTool({\n id: 'factory_health_check',\n description:\n 'Deterministic list of things wrong with this Factory right now (failed or stuck decisions, stalled starts, orphaned or missing seats, proposals and held cards waiting on a person, label drift). Each finding carries evidence and the standard repair. Explain these; do not invent findings that are not listed.',\n inputSchema: z.object({}).strict(),\n execute: async (): Promise<FactoryHealthReport> =>\n runFactoryHealthCheck(deps.workItems, scope, { now: now(), thresholds: deps.healthThresholds }),\n }),\n\n factory_inspect_work_item: createTool({\n id: 'factory_inspect_work_item',\n description:\n 'Everything the Factory knows about one card: row, stage history, seats (run bindings), decisions with errors, recent audit events, recent feed comments, linked parent/children and last observed labels. Use for \"why is #N in this state\".',\n inputSchema: itemRefSchema,\n execute: async ref => {\n const item = await findItem(deps, ref);\n if (!item) throw new Error(`No work item matches ${ref.id ?? `#${ref.number}`} in this Factory.`);\n const [decisions, bindings, audit, feed, all] = await Promise.all([\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId, item.id),\n deps.audit.list({ orgId: scope.orgId, factoryProjectId: scope.factoryProjectId, limit: 200 }),\n deps.comments.listRecent({ ...scope, workItemId: item.id, limit: 10 }),\n deps.workItems.list(scope),\n ]);\n const parent = item.parentWorkItemId\n ? all.find(candidate => candidate.id === item.parentWorkItemId)\n : undefined;\n const children = all.filter(candidate => candidate.parentWorkItemId === item.id);\n return {\n item: summarizeItem(item),\n labels: itemLabels(item),\n stageHistory: item.stageHistory.map(entry => ({\n stage: entry.stage,\n enteredAt: entry.enteredAt,\n exitedAt: entry.exitedAt ?? null,\n by: entry.by,\n })),\n sessions: Object.entries(item.sessions).map(([role, ref]) => ({\n role,\n sessionId: ref.sessionId,\n threadId: ref.threadId,\n branch: ref.branch,\n startedBy: ref.startedBy,\n })),\n seats: bindings.map(binding => ({\n id: binding.id,\n role: binding.role,\n status: binding.status,\n sessionId: binding.sessionId,\n threadId: binding.threadId,\n createdAt: iso(binding.createdAt),\n revokedAt: iso(binding.revokedAt),\n })),\n decisions: decisions\n .filter(decision => decision.workItemId === item.id)\n .map(summarizeDecision)\n .slice(-MAX_LIST),\n audit: audit.events\n .filter(event => event.targets.some(target => target.type === 'work_item' && target.id === item.id))\n .slice(0, 25)\n .map(event => ({\n id: event.id,\n action: event.action,\n actorId: event.actorId,\n actorType: event.actorType,\n occurredAt: iso(event.occurredAt),\n metadata: event.metadata,\n })),\n feed: feed.map(comment => ({\n id: comment.id,\n kind: comment.kind,\n author: comment.author,\n occurredAt: iso(comment.occurredAt),\n body: truncate(comment.body, MAX_TEXT),\n })),\n parent: parent ? summarizeItem(parent) : null,\n children: children.map(summarizeItem),\n };\n },\n }),\n\n factory_list_attention: createTool({\n id: 'factory_list_attention',\n description:\n 'Failed decisions grouped by failure code and error text, so a batch of cards that broke the same way reads as one incident. Also lists proposals waiting on a person and terminal cards that still hold seats.',\n inputSchema: z.object({ limit: z.number().int().min(1).max(MAX_LIST).default(25) }).strict(),\n execute: async ({ limit }) => {\n const [decisions, items] = await Promise.all([\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.list(scope),\n ]);\n const itemsById = new Map(items.map(item => [item.id, item]));\n const groups = new Map<\n string,\n { failureCode: string; error: string; decisions: FactoryDeferredDecisionRecord[] }\n >();\n for (const decision of decisions) {\n if (decision.status !== 'failed') continue;\n const error = truncate(decision.lastError ?? '', 160);\n const key = `${decision.failureCode ?? 'unknown'}|${error}`;\n const group = groups.get(key) ?? { failureCode: decision.failureCode ?? 'unknown', error, decisions: [] };\n group.decisions.push(decision);\n groups.set(key, group);\n }\n const incidents = [...groups.values()]\n .sort((a, b) => b.decisions.length - a.decisions.length)\n .slice(0, limit)\n .map(group => ({\n failureCode: group.failureCode,\n failureLabel: factoryDispatchFailureMetadata(group.decisions[0]!.failureCode).label,\n error: group.error,\n count: group.decisions.length,\n firstFailedAt: iso(\n new Date(\n group.decisions.reduce(\n (earliest, decision) => Math.min(earliest, decision.updatedAt.getTime()),\n Infinity,\n ),\n ),\n ),\n lastFailedAt: iso(\n new Date(\n group.decisions.reduce((latest, decision) => Math.max(latest, decision.updatedAt.getTime()), -Infinity),\n ),\n ),\n decisions: group.decisions.slice(0, limit).map(decision => {\n const item = decision.workItemId ? itemsById.get(decision.workItemId) : undefined;\n return {\n id: decision.id,\n workItemId: decision.workItemId,\n number: item ? itemNumber(item) : null,\n title: item?.title ?? null,\n attempts: decision.attempts,\n };\n }),\n }));\n return {\n incidents,\n proposals: decisions\n .filter(d => d.status === 'proposed')\n .slice(0, limit)\n .map(summarizeDecision),\n };\n },\n }),\n\n factory_read_session: createTool({\n id: 'factory_read_session',\n description:\n \"The most recent turns of a card's shared agent thread: who spoke, when, the text (truncated) and which tools were called. Use to see what a worker actually did. Give a work item (id or number) or a threadId.\",\n inputSchema: z\n .object({\n id: z.string().uuid().optional(),\n number: z.number().int().positive().optional(),\n threadId: z.string().min(1).optional(),\n limit: z.number().int().min(1).max(40).default(20),\n })\n .strict()\n .refine(ref => ref.id !== undefined || ref.number !== undefined || ref.threadId !== undefined, {\n message: 'Provide a work item id, a number, or a threadId.',\n }),\n execute: async ({ limit, ...ref }) => {\n if (!deps.messageReader) throw new Error('Session transcripts are not available on this deployment.');\n let threadId = ref.threadId;\n let resourceId: string | undefined;\n if (!threadId) {\n const item = await findItem(deps, ref);\n if (!item) throw new Error(`No work item matches ${ref.id ?? `#${ref.number}`} in this Factory.`);\n const session = Object.values(item.sessions)[0];\n if (!session) return { threadId: null, item: summarizeItem(item), turns: [] };\n threadId = session.threadId;\n resourceId = session.sessionId;\n } else {\n // A thread the caller named must still belong to a card in this Factory.\n const bindings = await deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId);\n const binding = bindings.find(candidate => candidate.threadId === threadId);\n if (!binding) throw new Error(`Thread ${threadId} is not bound to a card in this Factory.`);\n resourceId = binding.resourceId;\n }\n const page = await deps.messageReader.listMessages({\n threadId,\n ...(resourceId ? { resourceId } : {}),\n page: 0,\n perPage: limit,\n orderBy: { field: 'createdAt', direction: 'DESC' },\n });\n const turns = [...page.messages].reverse().map(message => ({\n id: message.id,\n role: message.role,\n createdAt: iso(message.createdAt),\n text: truncate(messageText(message), MAX_TEXT),\n tools: messageToolCalls(message),\n }));\n return { threadId, hasOlder: page.hasMore, turns };\n },\n }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"read-tools.js","names":[],"sources":["../../src/supervisor/read-tools.ts"],"sourcesContent":["/**\n * Supervisor read surface. Every tool is bounded (hard row caps, truncated\n * text) and answers with ids a person can click on the board, so the model\n * can explain a card's state without ever touching the database itself.\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { IntegrationTools } from '../integrations/base.js';\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport { FACTORY_RULE_STAGES, factoryRuleStage } from '../rules/types.js';\nimport type { FactoryRuleStage } from '../rules/types.js';\nimport type { AuditStorage } from '../storage/domains/audit/base.js';\nimport type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';\nimport type {\n FactoryDeferredDecisionRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport type { FactoryHealthReport, FactoryHealthThresholds } from './health.js';\nimport { runFactoryHealthCheck } from './health.js';\n\nexport interface SupervisorScope {\n orgId: string;\n factoryProjectId: string;\n}\n\nexport interface SupervisorMessageReader {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n orderBy: { field: 'createdAt'; direction: 'ASC' | 'DESC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n}\n\nexport interface SupervisorReadDependencies {\n scope: SupervisorScope;\n workItems: WorkItemsStorage;\n boards: BoardRegistry;\n comments: WorkItemCommentsStorage;\n audit: AuditStorage;\n messageReader?: SupervisorMessageReader;\n healthThresholds?: FactoryHealthThresholds;\n now?: () => Date;\n}\n\nconst MAX_TEXT = 600;\nconst MAX_ERROR = 400;\nconst MAX_LIST = 50;\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\nfunction iso(value: Date | string | null | undefined): string | null {\n if (!value) return null;\n const date = value instanceof Date ? value : new Date(value);\n return Number.isFinite(date.getTime()) ? date.toISOString() : null;\n}\n\nfunction itemNumber(item: WorkItemRow): number | null {\n const number = item.metadata?.number ?? item.metadata?.githubIssueNumber ?? item.metadata?.githubPullRequestNumber;\n return typeof number === 'number' ? number : null;\n}\n\nfunction itemLabels(item: WorkItemRow): string[] {\n const labels = item.metadata?.labels;\n return Array.isArray(labels) ? labels.filter((label): label is string => typeof label === 'string') : [];\n}\n\nfunction summarizeItem(item: WorkItemRow) {\n return {\n id: item.id,\n number: itemNumber(item),\n title: item.title,\n stage: factoryRuleStage(item.stages) ?? item.stages.join('+'),\n source: item.externalSource ? `${item.externalSource.integrationId}:${item.externalSource.type}` : 'manual',\n url: typeof item.metadata?.url === 'string' ? item.metadata.url : null,\n triageType: item.triageType,\n acceptedAt: iso(item.acceptedAt),\n autonomyArmedAt: iso(item.autonomyArmedAt),\n parentWorkItemId: item.parentWorkItemId,\n revision: item.revision,\n updatedAt: iso(item.updatedAt),\n };\n}\n\nfunction summarizeDecision(decision: FactoryDeferredDecisionRecord) {\n const type = typeof decision.decision.type === 'string' ? decision.decision.type : 'decision';\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : null;\n const skill = typeof decision.decision.skillName === 'string' ? decision.decision.skillName : null;\n return {\n id: decision.id,\n workItemId: decision.workItemId,\n type,\n role,\n skill,\n status: decision.status,\n attempts: decision.attempts,\n availableAt: iso(decision.availableAt),\n leaseOwner: decision.leaseOwner,\n leaseExpiresAt: iso(decision.leaseExpiresAt),\n failureCode: decision.failureCode,\n failureLabel: decision.failureCode ? factoryDispatchFailureMetadata(decision.failureCode).label : null,\n canRetry: decision.status === 'failed' ? factoryDispatchFailureMetadata(decision.failureCode).canRetry : false,\n lastError: decision.lastError ? truncate(decision.lastError, MAX_ERROR) : null,\n approvedBy: decision.approvedBy,\n createdAt: iso(decision.createdAt),\n updatedAt: iso(decision.updatedAt),\n completedAt: iso(decision.completedAt),\n };\n}\n\nfunction messageParts(content: unknown): unknown[] {\n if (Array.isArray(content)) return content;\n if (content && typeof content === 'object') {\n const parts = (content as { parts?: unknown }).parts;\n if (Array.isArray(parts)) return parts;\n }\n return [];\n}\n\nfunction messageText(message: MastraDBMessage): string {\n const content: unknown = message.content;\n const parts = messageParts(content);\n const text: string[] = [];\n for (const part of parts) {\n if (part && typeof part === 'object' && (part as { type?: unknown }).type === 'text') {\n const value = (part as { text?: unknown }).text;\n if (typeof value === 'string') text.push(value);\n }\n }\n if (text.length === 0 && typeof content === 'string') text.push(content);\n return text.join('\\n');\n}\n\nfunction messageToolCalls(message: MastraDBMessage): Array<{ tool: string; state: string | null }> {\n const parts = messageParts(message.content);\n const calls: Array<{ tool: string; state: string | null }> = [];\n for (const rawPart of parts) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : typeof part.type === 'string' && part.type.startsWith('tool-')\n ? part\n : undefined;\n if (!invocation) continue;\n const name = invocation.toolName ?? invocation.name ?? (typeof part.type === 'string' ? part.type.slice(5) : null);\n if (typeof name !== 'string') continue;\n calls.push({ tool: name, state: typeof invocation.state === 'string' ? invocation.state : null });\n }\n return calls;\n}\n\nasync function findItem(\n deps: SupervisorReadDependencies,\n ref: { id?: string; number?: number },\n): Promise<WorkItemRow | null> {\n if (ref.id) return deps.workItems.getForProject(deps.scope.orgId, deps.scope.factoryProjectId, ref.id);\n if (ref.number === undefined) return null;\n const items = await deps.workItems.list(deps.scope);\n const matches = items.filter(item => itemNumber(item) === ref.number);\n // Issue and PR numbers share a space on GitHub; prefer the Work-board card.\n return matches.find(item => item.externalSource?.type !== 'pull-request') ?? matches[0] ?? null;\n}\n\nconst itemRefSchema = z\n .object({\n id: z.string().uuid().optional().describe('Work item id.'),\n number: z.number().int().positive().optional().describe('Card number as shown on the board, e.g. 22874.'),\n })\n .refine(ref => ref.id !== undefined || ref.number !== undefined, { message: 'Provide an id or a number.' });\n\nexport function createFactorySupervisorReadTools(deps: SupervisorReadDependencies): IntegrationTools {\n const now = deps.now ?? (() => new Date());\n const scope = deps.scope;\n\n return {\n factory_overview: createTool({\n id: 'factory_overview',\n description:\n 'Counts per pipeline stage, decisions by status, active seats, open proposals and held cards for this Factory. Start here for \"what needs me\" questions.',\n inputSchema: z.object({}).strict(),\n execute: async () => {\n const [items, decisions, bindings, pendingStarts] = await Promise.all([\n deps.workItems.list(scope),\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId),\n deps.workItems.listPendingStarts(scope.orgId, scope.factoryProjectId),\n ]);\n const stages = Object.fromEntries(FACTORY_RULE_STAGES.map(stage => [stage, 0])) as Record<\n FactoryRuleStage,\n number\n >;\n let held = 0;\n for (const item of items) {\n const stage = factoryRuleStage(item.stages);\n if (stage) stages[stage] += 1;\n if (stage === 'triage' && item.triageType && item.triageType !== 'bug' && !item.acceptedAt) held += 1;\n }\n const decisionsByStatus: Record<string, number> = {};\n for (const decision of decisions) {\n decisionsByStatus[decision.status] = (decisionsByStatus[decision.status] ?? 0) + 1;\n }\n const activeSeats = bindings.filter(binding => binding.status === 'active');\n const seatsByRole: Record<string, number> = {};\n for (const seat of activeSeats) seatsByRole[seat.role] = (seatsByRole[seat.role] ?? 0) + 1;\n return {\n checkedAt: now().toISOString(),\n workItems: { total: items.length, byStage: stages, heldForDecision: held },\n decisions: decisionsByStatus,\n openProposals: decisions\n .filter(d => d.status === 'proposed')\n .map(summarizeDecision)\n .slice(0, MAX_LIST),\n failedDecisions: decisions\n .filter(d => d.status === 'failed')\n .map(summarizeDecision)\n .slice(0, MAX_LIST),\n activeSeats: { total: activeSeats.length, byRole: seatsByRole },\n pendingStarts: pendingStarts.filter(start => start.status !== 'sent').length,\n };\n },\n }),\n\n factory_health_check: createTool({\n id: 'factory_health_check',\n description:\n 'Deterministic list of things wrong with this Factory right now (stuck decisions, stalled starts, orphaned or missing seats, held cards waiting on a person, label drift). Each finding carries evidence and the standard repair. Explain these; do not invent findings that are not listed.',\n inputSchema: z.object({}).strict(),\n execute: async (): Promise<FactoryHealthReport> =>\n runFactoryHealthCheck(deps.workItems, deps.boards, scope, { now: now(), thresholds: deps.healthThresholds }),\n }),\n\n factory_inspect_work_item: createTool({\n id: 'factory_inspect_work_item',\n description:\n 'Everything the Factory knows about one card: row, stage history, seats (run bindings), decisions with errors, recent audit events, recent feed comments, linked parent/children and last observed labels. Use for \"why is #N in this state\".',\n inputSchema: itemRefSchema,\n execute: async ref => {\n const item = await findItem(deps, ref);\n if (!item) throw new Error(`No work item matches ${ref.id ?? `#${ref.number}`} in this Factory.`);\n const [decisions, bindings, audit, feed, all] = await Promise.all([\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId, item.id),\n deps.audit.list({ orgId: scope.orgId, factoryProjectId: scope.factoryProjectId, limit: 200 }),\n deps.comments.listRecent({ ...scope, workItemId: item.id, limit: 10 }),\n deps.workItems.list(scope),\n ]);\n const parent = item.parentWorkItemId\n ? all.find(candidate => candidate.id === item.parentWorkItemId)\n : undefined;\n const children = all.filter(candidate => candidate.parentWorkItemId === item.id);\n return {\n item: summarizeItem(item),\n labels: itemLabels(item),\n stageHistory: item.stageHistory.map(entry => ({\n stage: entry.stage,\n enteredAt: entry.enteredAt,\n exitedAt: entry.exitedAt ?? null,\n by: entry.by,\n })),\n sessions: Object.entries(item.sessions).map(([role, ref]) => ({\n role,\n sessionId: ref.sessionId,\n threadId: ref.threadId,\n branch: ref.branch,\n startedBy: ref.startedBy,\n })),\n seats: bindings.map(binding => ({\n id: binding.id,\n role: binding.role,\n status: binding.status,\n sessionId: binding.sessionId,\n threadId: binding.threadId,\n createdAt: iso(binding.createdAt),\n revokedAt: iso(binding.revokedAt),\n })),\n decisions: decisions\n .filter(decision => decision.workItemId === item.id)\n .map(summarizeDecision)\n .slice(-MAX_LIST),\n audit: audit.events\n .filter(event => event.targets.some(target => target.type === 'work_item' && target.id === item.id))\n .slice(0, 25)\n .map(event => ({\n id: event.id,\n action: event.action,\n actorId: event.actorId,\n actorType: event.actorType,\n occurredAt: iso(event.occurredAt),\n metadata: event.metadata,\n })),\n feed: feed.map(comment => ({\n id: comment.id,\n kind: comment.kind,\n author: comment.author,\n occurredAt: iso(comment.occurredAt),\n body: truncate(comment.body, MAX_TEXT),\n })),\n parent: parent ? summarizeItem(parent) : null,\n children: children.map(summarizeItem),\n };\n },\n }),\n\n factory_list_attention: createTool({\n id: 'factory_list_attention',\n description:\n 'Failed decisions grouped by failure code and error text, so a batch of cards that broke the same way reads as one incident. Also lists proposals waiting on a person and terminal cards that still hold seats.',\n inputSchema: z.object({ limit: z.number().int().min(1).max(MAX_LIST).default(25) }).strict(),\n execute: async ({ limit }) => {\n const [decisions, items] = await Promise.all([\n deps.workItems.listDeferredDecisions(scope.orgId, scope.factoryProjectId),\n deps.workItems.list(scope),\n ]);\n const itemsById = new Map(items.map(item => [item.id, item]));\n const groups = new Map<\n string,\n { failureCode: string; error: string; decisions: FactoryDeferredDecisionRecord[] }\n >();\n for (const decision of decisions) {\n if (decision.status !== 'failed') continue;\n const error = truncate(decision.lastError ?? '', 160);\n const key = `${decision.failureCode ?? 'unknown'}|${error}`;\n const group = groups.get(key) ?? { failureCode: decision.failureCode ?? 'unknown', error, decisions: [] };\n group.decisions.push(decision);\n groups.set(key, group);\n }\n const incidents = [...groups.values()]\n .sort((a, b) => b.decisions.length - a.decisions.length)\n .slice(0, limit)\n .map(group => ({\n failureCode: group.failureCode,\n failureLabel: factoryDispatchFailureMetadata(group.decisions[0]!.failureCode).label,\n error: group.error,\n count: group.decisions.length,\n firstFailedAt: iso(\n new Date(\n group.decisions.reduce(\n (earliest, decision) => Math.min(earliest, decision.updatedAt.getTime()),\n Infinity,\n ),\n ),\n ),\n lastFailedAt: iso(\n new Date(\n group.decisions.reduce((latest, decision) => Math.max(latest, decision.updatedAt.getTime()), -Infinity),\n ),\n ),\n decisions: group.decisions.slice(0, limit).map(decision => {\n const item = decision.workItemId ? itemsById.get(decision.workItemId) : undefined;\n return {\n id: decision.id,\n workItemId: decision.workItemId,\n number: item ? itemNumber(item) : null,\n title: item?.title ?? null,\n attempts: decision.attempts,\n };\n }),\n }));\n return {\n incidents,\n proposals: decisions\n .filter(d => d.status === 'proposed')\n .slice(0, limit)\n .map(summarizeDecision),\n };\n },\n }),\n\n factory_read_session: createTool({\n id: 'factory_read_session',\n description:\n \"The most recent turns of a card's shared agent thread: who spoke, when, the text (truncated) and which tools were called. Use to see what a worker actually did. Give a work item (id or number) or a threadId.\",\n inputSchema: z\n .object({\n id: z.string().uuid().optional(),\n number: z.number().int().positive().optional(),\n threadId: z.string().min(1).optional(),\n limit: z.number().int().min(1).max(40).default(20),\n })\n .strict()\n .refine(ref => ref.id !== undefined || ref.number !== undefined || ref.threadId !== undefined, {\n message: 'Provide a work item id, a number, or a threadId.',\n }),\n execute: async ({ limit, ...ref }) => {\n if (!deps.messageReader) throw new Error('Session transcripts are not available on this deployment.');\n let threadId = ref.threadId;\n let resourceId: string | undefined;\n if (!threadId) {\n const item = await findItem(deps, ref);\n if (!item) throw new Error(`No work item matches ${ref.id ?? `#${ref.number}`} in this Factory.`);\n const session = Object.values(item.sessions)[0];\n if (!session) return { threadId: null, item: summarizeItem(item), turns: [] };\n threadId = session.threadId;\n resourceId = session.sessionId;\n } else {\n // A thread the caller named must still belong to a card in this Factory.\n const bindings = await deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId);\n const binding = bindings.find(candidate => candidate.threadId === threadId);\n if (!binding) throw new Error(`Thread ${threadId} is not bound to a card in this Factory.`);\n resourceId = binding.resourceId;\n }\n const page = await deps.messageReader.listMessages({\n threadId,\n ...(resourceId ? { resourceId } : {}),\n page: 0,\n perPage: limit,\n orderBy: { field: 'createdAt', direction: 'DESC' },\n });\n const turns = [...page.messages].reverse().map(message => ({\n id: message.id,\n role: message.role,\n createdAt: iso(message.createdAt),\n text: truncate(messageText(message), MAX_TEXT),\n tools: messageToolCalls(message),\n }));\n return { threadId, hasOlder: page.hasMore, turns };\n },\n }),\n };\n}\n"],"mappings":";;;;;;AAmDA,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,WAAW;AAEjB,SAAS,SAAS,MAAc,KAAqB;CACnD,OAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK;AAC5D;AAEA,SAAS,IAAI,OAAwD;CACnE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,OAAO,iBAAiB,OAAO,QAAQ,IAAI,KAAK,KAAK;CAC3D,OAAO,OAAO,SAAS,KAAK,QAAQ,CAAC,IAAI,KAAK,YAAY,IAAI;AAChE;AAEA,SAAS,WAAW,MAAkC;CACpD,MAAM,SAAS,KAAK,UAAU,UAAU,KAAK,UAAU,qBAAqB,KAAK,UAAU;CAC3F,OAAO,OAAO,WAAW,WAAW,SAAS;AAC/C;AAEA,SAAS,WAAW,MAA6B;CAC/C,MAAM,SAAS,KAAK,UAAU;CAC9B,OAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IAAI,CAAC;AACzG;AAEA,SAAS,cAAc,MAAmB;CACxC,OAAO;EACL,IAAI,KAAK;EACT,QAAQ,WAAW,IAAI;EACvB,OAAO,KAAK;EACZ,OAAO,iBAAiB,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,GAAG;EAC5D,QAAQ,KAAK,iBAAiB,GAAG,KAAK,eAAe,cAAc,GAAG,KAAK,eAAe,SAAS;EACnG,KAAK,OAAO,KAAK,UAAU,QAAQ,WAAW,KAAK,SAAS,MAAM;EAClE,YAAY,KAAK;EACjB,YAAY,IAAI,KAAK,UAAU;EAC/B,iBAAiB,IAAI,KAAK,eAAe;EACzC,kBAAkB,KAAK;EACvB,UAAU,KAAK;EACf,WAAW,IAAI,KAAK,SAAS;CAC/B;AACF;AAEA,SAAS,kBAAkB,UAAyC;CAClE,MAAM,OAAO,OAAO,SAAS,SAAS,SAAS,WAAW,SAAS,SAAS,OAAO;CACnF,MAAM,OAAO,OAAO,SAAS,SAAS,SAAS,WAAW,SAAS,SAAS,OAAO;CACnF,MAAM,QAAQ,OAAO,SAAS,SAAS,cAAc,WAAW,SAAS,SAAS,YAAY;CAC9F,OAAO;EACL,IAAI,SAAS;EACb,YAAY,SAAS;EACrB;EACA;EACA;EACA,QAAQ,SAAS;EACjB,UAAU,SAAS;EACnB,aAAa,IAAI,SAAS,WAAW;EACrC,YAAY,SAAS;EACrB,gBAAgB,IAAI,SAAS,cAAc;EAC3C,aAAa,SAAS;EACtB,cAAc,SAAS,cAAc,+BAA+B,SAAS,WAAW,CAAC,CAAC,QAAQ;EAClG,UAAU,SAAS,WAAW,WAAW,+BAA+B,SAAS,WAAW,CAAC,CAAC,WAAW;EACzG,WAAW,SAAS,YAAY,SAAS,SAAS,WAAW,SAAS,IAAI;EAC1E,YAAY,SAAS;EACrB,WAAW,IAAI,SAAS,SAAS;EACjC,WAAW,IAAI,SAAS,SAAS;EACjC,aAAa,IAAI,SAAS,WAAW;CACvC;AACF;AAEA,SAAS,aAAa,SAA6B;CACjD,IAAI,MAAM,QAAQ,OAAO,GAAG,OAAO;CACnC,IAAI,WAAW,OAAO,YAAY,UAAU;EAC1C,MAAM,QAAS,QAAgC;EAC/C,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO;CACnC;CACA,OAAO,CAAC;AACV;AAEA,SAAS,YAAY,SAAkC;CACrD,MAAM,UAAmB,QAAQ;CACjC,MAAM,QAAQ,aAAa,OAAO;CAClC,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,QAAQ,OACjB,IAAI,QAAQ,OAAO,SAAS,YAAa,KAA4B,SAAS,QAAQ;EACpF,MAAM,QAAS,KAA4B;EAC3C,IAAI,OAAO,UAAU,UAAU,KAAK,KAAK,KAAK;CAChD;CAEF,IAAI,KAAK,WAAW,KAAK,OAAO,YAAY,UAAU,KAAK,KAAK,OAAO;CACvE,OAAO,KAAK,KAAK,IAAI;AACvB;AAEA,SAAS,iBAAiB,SAAyE;CACjG,MAAM,QAAQ,aAAa,QAAQ,OAAO;CAC1C,MAAM,QAAuD,CAAC;CAC9D,KAAK,MAAM,WAAW,OAAO;EAC3B,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;EAC7C,MAAM,OAAO;EACb,MAAM,aACJ,KAAK,SAAS,qBAAqB,KAAK,kBAAkB,OAAO,KAAK,mBAAmB,WACpF,KAAK,iBACN,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,WAAW,OAAO,IAC3D,OACA,KAAA;EACR,IAAI,CAAC,YAAY;EACjB,MAAM,OAAO,WAAW,YAAY,WAAW,SAAS,OAAO,KAAK,SAAS,WAAW,KAAK,KAAK,MAAM,CAAC,IAAI;EAC7G,IAAI,OAAO,SAAS,UAAU;EAC9B,MAAM,KAAK;GAAE,MAAM;GAAM,OAAO,OAAO,WAAW,UAAU,WAAW,WAAW,QAAQ;EAAK,CAAC;CAClG;CACA,OAAO;AACT;AAEA,eAAe,SACb,MACA,KAC6B;CAC7B,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,cAAc,KAAK,MAAM,OAAO,KAAK,MAAM,kBAAkB,IAAI,EAAE;CACrG,IAAI,IAAI,WAAW,KAAA,GAAW,OAAO;CAErC,MAAM,WAAU,MADI,KAAK,UAAU,KAAK,KAAK,KAAK,EAAA,CAC5B,QAAO,SAAQ,WAAW,IAAI,MAAM,IAAI,MAAM;CAEpE,OAAO,QAAQ,MAAK,SAAQ,KAAK,gBAAgB,SAAS,cAAc,KAAK,QAAQ,MAAM;AAC7F;AAEA,MAAM,gBAAgB,EACnB,OAAO;CACN,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe;CACzD,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gDAAgD;AAC1G,CAAC,CAAC,CACD,QAAO,QAAO,IAAI,OAAO,KAAA,KAAa,IAAI,WAAW,KAAA,GAAW,EAAE,SAAS,6BAA6B,CAAC;AAE5G,SAAgB,iCAAiC,MAAoD;CACnG,MAAM,MAAM,KAAK,8BAAc,IAAI,KAAK;CACxC,MAAM,QAAQ,KAAK;CAEnB,OAAO;EACL,kBAAkB,WAAW;GAC3B,IAAI;GACJ,aACE;GACF,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;GACjC,SAAS,YAAY;IACnB,MAAM,CAAC,OAAO,WAAW,UAAU,iBAAiB,MAAM,QAAQ,IAAI;KACpE,KAAK,UAAU,KAAK,KAAK;KACzB,KAAK,UAAU,sBAAsB,MAAM,OAAO,MAAM,gBAAgB;KACxE,KAAK,UAAU,gBAAgB,MAAM,OAAO,MAAM,gBAAgB;KAClE,KAAK,UAAU,kBAAkB,MAAM,OAAO,MAAM,gBAAgB;IACtE,CAAC;IACD,MAAM,SAAS,OAAO,YAAY,oBAAoB,KAAI,UAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAI9E,IAAI,OAAO;IACX,KAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,QAAQ,iBAAiB,KAAK,MAAM;KAC1C,IAAI,OAAO,OAAO,UAAU;KAC5B,IAAI,UAAU,YAAY,KAAK,cAAc,KAAK,eAAe,SAAS,CAAC,KAAK,YAAY,QAAQ;IACtG;IACA,MAAM,oBAA4C,CAAC;IACnD,KAAK,MAAM,YAAY,WACrB,kBAAkB,SAAS,WAAW,kBAAkB,SAAS,WAAW,KAAK;IAEnF,MAAM,cAAc,SAAS,QAAO,YAAW,QAAQ,WAAW,QAAQ;IAC1E,MAAM,cAAsC,CAAC;IAC7C,KAAK,MAAM,QAAQ,aAAa,YAAY,KAAK,SAAS,YAAY,KAAK,SAAS,KAAK;IACzF,OAAO;KACL,WAAW,IAAI,CAAC,CAAC,YAAY;KAC7B,WAAW;MAAE,OAAO,MAAM;MAAQ,SAAS;MAAQ,iBAAiB;KAAK;KACzE,WAAW;KACX,eAAe,UACZ,QAAO,MAAK,EAAE,WAAW,UAAU,CAAC,CACpC,IAAI,iBAAiB,CAAC,CACtB,MAAM,GAAG,QAAQ;KACpB,iBAAiB,UACd,QAAO,MAAK,EAAE,WAAW,QAAQ,CAAC,CAClC,IAAI,iBAAiB,CAAC,CACtB,MAAM,GAAG,QAAQ;KACpB,aAAa;MAAE,OAAO,YAAY;MAAQ,QAAQ;KAAY;KAC9D,eAAe,cAAc,QAAO,UAAS,MAAM,WAAW,MAAM,CAAC,CAAC;IACxE;GACF;EACF,CAAC;EAED,sBAAsB,WAAW;GAC/B,IAAI;GACJ,aACE;GACF,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;GACjC,SAAS,YACP,sBAAsB,KAAK,WAAW,KAAK,QAAQ,OAAO;IAAE,KAAK,IAAI;IAAG,YAAY,KAAK;GAAiB,CAAC;EAC/G,CAAC;EAED,2BAA2B,WAAW;GACpC,IAAI;GACJ,aACE;GACF,aAAa;GACb,SAAS,OAAM,QAAO;IACpB,MAAM,OAAO,MAAM,SAAS,MAAM,GAAG;IACrC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,wBAAwB,IAAI,MAAM,IAAI,IAAI,SAAS,kBAAkB;IAChG,MAAM,CAAC,WAAW,UAAU,OAAO,MAAM,OAAO,MAAM,QAAQ,IAAI;KAChE,KAAK,UAAU,sBAAsB,MAAM,OAAO,MAAM,gBAAgB;KACxE,KAAK,UAAU,gBAAgB,MAAM,OAAO,MAAM,kBAAkB,KAAK,EAAE;KAC3E,KAAK,MAAM,KAAK;MAAE,OAAO,MAAM;MAAO,kBAAkB,MAAM;MAAkB,OAAO;KAAI,CAAC;KAC5F,KAAK,SAAS,WAAW;MAAE,GAAG;MAAO,YAAY,KAAK;MAAI,OAAO;KAAG,CAAC;KACrE,KAAK,UAAU,KAAK,KAAK;IAC3B,CAAC;IACD,MAAM,SAAS,KAAK,mBAChB,IAAI,MAAK,cAAa,UAAU,OAAO,KAAK,gBAAgB,IAC5D,KAAA;IACJ,MAAM,WAAW,IAAI,QAAO,cAAa,UAAU,qBAAqB,KAAK,EAAE;IAC/E,OAAO;KACL,MAAM,cAAc,IAAI;KACxB,QAAQ,WAAW,IAAI;KACvB,cAAc,KAAK,aAAa,KAAI,WAAU;MAC5C,OAAO,MAAM;MACb,WAAW,MAAM;MACjB,UAAU,MAAM,YAAY;MAC5B,IAAI,MAAM;KACZ,EAAE;KACF,UAAU,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU;MAC5D;MACA,WAAW,IAAI;MACf,UAAU,IAAI;MACd,QAAQ,IAAI;MACZ,WAAW,IAAI;KACjB,EAAE;KACF,OAAO,SAAS,KAAI,aAAY;MAC9B,IAAI,QAAQ;MACZ,MAAM,QAAQ;MACd,QAAQ,QAAQ;MAChB,WAAW,QAAQ;MACnB,UAAU,QAAQ;MAClB,WAAW,IAAI,QAAQ,SAAS;MAChC,WAAW,IAAI,QAAQ,SAAS;KAClC,EAAE;KACF,WAAW,UACR,QAAO,aAAY,SAAS,eAAe,KAAK,EAAE,CAAC,CACnD,IAAI,iBAAiB,CAAC,CACtB,MAAM,GAAS;KAClB,OAAO,MAAM,OACV,QAAO,UAAS,MAAM,QAAQ,MAAK,WAAU,OAAO,SAAS,eAAe,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CACnG,MAAM,GAAG,EAAE,CAAC,CACZ,KAAI,WAAU;MACb,IAAI,MAAM;MACV,QAAQ,MAAM;MACd,SAAS,MAAM;MACf,WAAW,MAAM;MACjB,YAAY,IAAI,MAAM,UAAU;MAChC,UAAU,MAAM;KAClB,EAAE;KACJ,MAAM,KAAK,KAAI,aAAY;MACzB,IAAI,QAAQ;MACZ,MAAM,QAAQ;MACd,QAAQ,QAAQ;MAChB,YAAY,IAAI,QAAQ,UAAU;MAClC,MAAM,SAAS,QAAQ,MAAM,QAAQ;KACvC,EAAE;KACF,QAAQ,SAAS,cAAc,MAAM,IAAI;KACzC,UAAU,SAAS,IAAI,aAAa;IACtC;GACF;EACF,CAAC;EAED,wBAAwB,WAAW;GACjC,IAAI;GACJ,aACE;GACF,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO;GAC3F,SAAS,OAAO,EAAE,YAAY;IAC5B,MAAM,CAAC,WAAW,SAAS,MAAM,QAAQ,IAAI,CAC3C,KAAK,UAAU,sBAAsB,MAAM,OAAO,MAAM,gBAAgB,GACxE,KAAK,UAAU,KAAK,KAAK,CAC3B,CAAC;IACD,MAAM,YAAY,IAAI,IAAI,MAAM,KAAI,SAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAC5D,MAAM,yBAAS,IAAI,IAGjB;IACF,KAAK,MAAM,YAAY,WAAW;KAChC,IAAI,SAAS,WAAW,UAAU;KAClC,MAAM,QAAQ,SAAS,SAAS,aAAa,IAAI,GAAG;KACpD,MAAM,MAAM,GAAG,SAAS,eAAe,UAAU,GAAG;KACpD,MAAM,QAAQ,OAAO,IAAI,GAAG,KAAK;MAAE,aAAa,SAAS,eAAe;MAAW;MAAO,WAAW,CAAC;KAAE;KACxG,MAAM,UAAU,KAAK,QAAQ;KAC7B,OAAO,IAAI,KAAK,KAAK;IACvB;IAiCA,OAAO;KACL,WAjCgB,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CACnC,MAAM,GAAG,MAAM,EAAE,UAAU,SAAS,EAAE,UAAU,MAAM,CAAC,CACvD,MAAM,GAAG,KAAK,CAAC,CACf,KAAI,WAAU;MACb,aAAa,MAAM;MACnB,cAAc,+BAA+B,MAAM,UAAU,EAAE,CAAE,WAAW,CAAC,CAAC;MAC9E,OAAO,MAAM;MACb,OAAO,MAAM,UAAU;MACvB,eAAe,IACb,IAAI,KACF,MAAM,UAAU,QACb,UAAU,aAAa,KAAK,IAAI,UAAU,SAAS,UAAU,QAAQ,CAAC,GACvE,QACF,CACF,CACF;MACA,cAAc,IACZ,IAAI,KACF,MAAM,UAAU,QAAQ,QAAQ,aAAa,KAAK,IAAI,QAAQ,SAAS,UAAU,QAAQ,CAAC,GAAG,SAAS,CACxG,CACF;MACA,WAAW,MAAM,UAAU,MAAM,GAAG,KAAK,CAAC,CAAC,KAAI,aAAY;OACzD,MAAM,OAAO,SAAS,aAAa,UAAU,IAAI,SAAS,UAAU,IAAI,KAAA;OACxE,OAAO;QACL,IAAI,SAAS;QACb,YAAY,SAAS;QACrB,QAAQ,OAAO,WAAW,IAAI,IAAI;QAClC,OAAO,MAAM,SAAS;QACtB,UAAU,SAAS;OACrB;MACF,CAAC;KACH,EAEQ;KACR,WAAW,UACR,QAAO,MAAK,EAAE,WAAW,UAAU,CAAC,CACpC,MAAM,GAAG,KAAK,CAAC,CACf,IAAI,iBAAiB;IAC1B;GACF;EACF,CAAC;EAED,sBAAsB,WAAW;GAC/B,IAAI;GACJ,aACE;GACF,aAAa,EACV,OAAO;IACN,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS;IAC/B,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;IAC7C,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;IACrC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE;GACnD,CAAC,CAAC,CACD,OAAO,CAAC,CACR,QAAO,QAAO,IAAI,OAAO,KAAA,KAAa,IAAI,WAAW,KAAA,KAAa,IAAI,aAAa,KAAA,GAAW,EAC7F,SAAS,mDACX,CAAC;GACH,SAAS,OAAO,EAAE,OAAO,GAAG,UAAU;IACpC,IAAI,CAAC,KAAK,eAAe,MAAM,IAAI,MAAM,2DAA2D;IACpG,IAAI,WAAW,IAAI;IACnB,IAAI;IACJ,IAAI,CAAC,UAAU;KACb,MAAM,OAAO,MAAM,SAAS,MAAM,GAAG;KACrC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,wBAAwB,IAAI,MAAM,IAAI,IAAI,SAAS,kBAAkB;KAChG,MAAM,UAAU,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC;KAC7C,IAAI,CAAC,SAAS,OAAO;MAAE,UAAU;MAAM,MAAM,cAAc,IAAI;MAAG,OAAO,CAAC;KAAE;KAC5E,WAAW,QAAQ;KACnB,aAAa,QAAQ;IACvB,OAAO;KAGL,MAAM,WAAU,MADO,KAAK,UAAU,gBAAgB,MAAM,OAAO,MAAM,gBAAgB,EAAA,CAChE,MAAK,cAAa,UAAU,aAAa,QAAQ;KAC1E,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,UAAU,SAAS,yCAAyC;KAC1F,aAAa,QAAQ;IACvB;IACA,MAAM,OAAO,MAAM,KAAK,cAAc,aAAa;KACjD;KACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;KACnC,MAAM;KACN,SAAS;KACT,SAAS;MAAE,OAAO;MAAa,WAAW;KAAO;IACnD,CAAC;IACD,MAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAI,aAAY;KACzD,IAAI,QAAQ;KACZ,MAAM,QAAQ;KACd,WAAW,IAAI,QAAQ,SAAS;KAChC,MAAM,SAAS,YAAY,OAAO,GAAG,QAAQ;KAC7C,OAAO,iBAAiB,OAAO;IACjC,EAAE;IACF,OAAO;KAAE;KAAU,UAAU,KAAK;KAAS;IAAM;GACnD;EACF,CAAC;CACH;AACF"}
|
|
@@ -26,4 +26,6 @@ export declare function workItemThreadTitle(item: Pick<WorkItemBranchInput, 'sou
|
|
|
26
26
|
* lost their identifier fall back to the id-derived branch.
|
|
27
27
|
*/
|
|
28
28
|
export declare function workItemBranch(item: WorkItemBranchInput): string;
|
|
29
|
+
/** The pull request a `factory/pr-<number>` branch was named after, the inverse of {@link workItemBranch}. */
|
|
30
|
+
export declare function pullRequestNumberFromBranch(branch: string): number | undefined;
|
|
29
31
|
//# sourceMappingURL=work-item-branch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-item-branch.d.ts","sourceRoot":"","sources":["../src/work-item-branch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,CAOpH;AAOD,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CAKzG;AAED,yHAAyH;AACzH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACzE,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAWhE"}
|
|
1
|
+
{"version":3,"file":"work-item-branch.d.ts","sourceRoot":"","sources":["../src/work-item-branch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,CAOpH;AAOD,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CAKzG;AAED,yHAAyH;AACzH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACzE,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAWhE;AAED,8GAA8G;AAC9G,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG9E"}
|
package/dist/work-item-branch.js
CHANGED
|
@@ -39,7 +39,12 @@ function workItemBranch(item) {
|
|
|
39
39
|
}
|
|
40
40
|
return `factory/item-${item.id}`;
|
|
41
41
|
}
|
|
42
|
+
/** The pull request a `factory/pr-<number>` branch was named after, the inverse of {@link workItemBranch}. */
|
|
43
|
+
function pullRequestNumberFromBranch(branch) {
|
|
44
|
+
const match = /^factory\/pr-([1-9]\d*)$/.exec(branch);
|
|
45
|
+
return match ? Number(match[1]) : void 0;
|
|
46
|
+
}
|
|
42
47
|
//#endregion
|
|
43
|
-
export { workItemBranch, workItemBranchSource, workItemNumber, workItemThreadTitle };
|
|
48
|
+
export { pullRequestNumberFromBranch, workItemBranch, workItemBranchSource, workItemNumber, workItemThreadTitle };
|
|
44
49
|
|
|
45
50
|
//# sourceMappingURL=work-item-branch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-item-branch.js","names":[],"sources":["../src/work-item-branch.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from './storage/domains/work-items/base.js';\n\n/**\n * Where a card came from, in the vocabulary branch naming reads. Server rows\n * map their `externalSource` into this with {@link workItemBranchSource}; the\n * board's own `WorkItem['source']` is already this union.\n */\nexport type WorkItemBranchSource = 'github-issue' | 'github-pr' | 'linear-issue' | 'slack-thread' | 'manual';\n\nexport interface WorkItemBranchInput {\n id: string;\n source: WorkItemBranchSource;\n metadata?: Record<string, unknown> | null;\n}\n\n/** Map a stored item's provenance onto the source vocabulary branch naming reads. */\nexport function workItemBranchSource(externalSource: ExternalWorkItemSource | null | undefined): WorkItemBranchSource {\n if (!externalSource) return 'manual';\n if (externalSource.integrationId === 'linear') return 'linear-issue';\n // Only GitHub and Linear carry provider identities; anything else (a Slack\n // thread, say) is a plain work item rather than a mislabeled GitHub issue.\n if (externalSource.integrationId !== 'github') return 'manual';\n return externalSource.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\nfunction branchNumber(metadata: Record<string, unknown>, key: string): number | undefined {\n const value = metadata[key] ?? metadata.number;\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\n/** The provider number a GitHub card carries — the `#12` its runs and thread titles name it by. */\nexport function workItemNumber(item: Pick<WorkItemBranchInput, 'source' | 'metadata'>): number | undefined {\n const metadata = item.metadata ?? {};\n if (item.source === 'github-issue') return branchNumber(metadata, 'githubIssueNumber');\n if (item.source === 'github-pr') return branchNumber(metadata, 'githubPullRequestNumber');\n return;\n}\n\n/** How a card names its thread: a Linear title already opens with its identifier, a GitHub card gets its number here. */\nexport function workItemThreadTitle(\n item: Pick<WorkItemBranchInput, 'source' | 'metadata'> & { title: string },\n): string {\n const number = workItemNumber(item);\n if (number === undefined) return item.title;\n return `${item.source === 'github-pr' ? 'PR' : 'Issue'} #${number}: ${item.title}`;\n}\n\n/**\n * The git branch an item's runs and sessions share, one grammar for both sides\n * of the wire: the dispatcher names autonomous run branches with it and the\n * board opens card sessions on it, so both converge on one checkout per item.\n * Cards without a provider identity (manual, Slack) and cards whose metadata\n * lost their identifier fall back to the id-derived branch.\n */\nexport function workItemBranch(item: WorkItemBranchInput): string {\n const metadata = item.metadata ?? {};\n const githubNumber = workItemNumber(item);\n if (githubNumber !== undefined) {\n return item.source === 'github-issue' ? `factory/issue-${githubNumber}` : `factory/pr-${githubNumber}`;\n }\n if (item.source === 'linear-issue' && typeof metadata.identifier === 'string') {\n const identifier = metadata.identifier.trim();\n if (identifier) return `factory/linear-${identifier.toLowerCase()}`;\n }\n return `factory/item-${item.id}`;\n}\n"],"mappings":";;AAgBA,SAAgB,qBAAqB,gBAAiF;CACpH,IAAI,CAAC,gBAAgB,OAAO;CAC5B,IAAI,eAAe,kBAAkB,UAAU,OAAO;CAGtD,IAAI,eAAe,kBAAkB,UAAU,OAAO;CACtD,OAAO,eAAe,SAAS,iBAAiB,cAAc;AAChE;AAEA,SAAS,aAAa,UAAmC,KAAiC;CACxF,MAAM,QAAQ,SAAS,QAAQ,SAAS;CACxC,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAA;AACrF;;AAGA,SAAgB,eAAe,MAA4E;CACzG,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,IAAI,KAAK,WAAW,gBAAgB,OAAO,aAAa,UAAU,mBAAmB;CACrF,IAAI,KAAK,WAAW,aAAa,OAAO,aAAa,UAAU,yBAAyB;AAE1F;;AAGA,SAAgB,oBACd,MACQ;CACR,MAAM,SAAS,eAAe,IAAI;CAClC,IAAI,WAAW,KAAA,GAAW,OAAO,KAAK;CACtC,OAAO,GAAG,KAAK,WAAW,cAAc,OAAO,QAAQ,IAAI,OAAO,IAAI,KAAK;AAC7E;;;;;;;;AASA,SAAgB,eAAe,MAAmC;CAChE,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,MAAM,eAAe,eAAe,IAAI;CACxC,IAAI,iBAAiB,KAAA,GACnB,OAAO,KAAK,WAAW,iBAAiB,iBAAiB,iBAAiB,cAAc;CAE1F,IAAI,KAAK,WAAW,kBAAkB,OAAO,SAAS,eAAe,UAAU;EAC7E,MAAM,aAAa,SAAS,WAAW,KAAK;EAC5C,IAAI,YAAY,OAAO,kBAAkB,WAAW,YAAY;CAClE;CACA,OAAO,gBAAgB,KAAK;AAC9B"}
|
|
1
|
+
{"version":3,"file":"work-item-branch.js","names":[],"sources":["../src/work-item-branch.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from './storage/domains/work-items/base.js';\n\n/**\n * Where a card came from, in the vocabulary branch naming reads. Server rows\n * map their `externalSource` into this with {@link workItemBranchSource}; the\n * board's own `WorkItem['source']` is already this union.\n */\nexport type WorkItemBranchSource = 'github-issue' | 'github-pr' | 'linear-issue' | 'slack-thread' | 'manual';\n\nexport interface WorkItemBranchInput {\n id: string;\n source: WorkItemBranchSource;\n metadata?: Record<string, unknown> | null;\n}\n\n/** Map a stored item's provenance onto the source vocabulary branch naming reads. */\nexport function workItemBranchSource(externalSource: ExternalWorkItemSource | null | undefined): WorkItemBranchSource {\n if (!externalSource) return 'manual';\n if (externalSource.integrationId === 'linear') return 'linear-issue';\n // Only GitHub and Linear carry provider identities; anything else (a Slack\n // thread, say) is a plain work item rather than a mislabeled GitHub issue.\n if (externalSource.integrationId !== 'github') return 'manual';\n return externalSource.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\nfunction branchNumber(metadata: Record<string, unknown>, key: string): number | undefined {\n const value = metadata[key] ?? metadata.number;\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\n/** The provider number a GitHub card carries — the `#12` its runs and thread titles name it by. */\nexport function workItemNumber(item: Pick<WorkItemBranchInput, 'source' | 'metadata'>): number | undefined {\n const metadata = item.metadata ?? {};\n if (item.source === 'github-issue') return branchNumber(metadata, 'githubIssueNumber');\n if (item.source === 'github-pr') return branchNumber(metadata, 'githubPullRequestNumber');\n return;\n}\n\n/** How a card names its thread: a Linear title already opens with its identifier, a GitHub card gets its number here. */\nexport function workItemThreadTitle(\n item: Pick<WorkItemBranchInput, 'source' | 'metadata'> & { title: string },\n): string {\n const number = workItemNumber(item);\n if (number === undefined) return item.title;\n return `${item.source === 'github-pr' ? 'PR' : 'Issue'} #${number}: ${item.title}`;\n}\n\n/**\n * The git branch an item's runs and sessions share, one grammar for both sides\n * of the wire: the dispatcher names autonomous run branches with it and the\n * board opens card sessions on it, so both converge on one checkout per item.\n * Cards without a provider identity (manual, Slack) and cards whose metadata\n * lost their identifier fall back to the id-derived branch.\n */\nexport function workItemBranch(item: WorkItemBranchInput): string {\n const metadata = item.metadata ?? {};\n const githubNumber = workItemNumber(item);\n if (githubNumber !== undefined) {\n return item.source === 'github-issue' ? `factory/issue-${githubNumber}` : `factory/pr-${githubNumber}`;\n }\n if (item.source === 'linear-issue' && typeof metadata.identifier === 'string') {\n const identifier = metadata.identifier.trim();\n if (identifier) return `factory/linear-${identifier.toLowerCase()}`;\n }\n return `factory/item-${item.id}`;\n}\n\n/** The pull request a `factory/pr-<number>` branch was named after, the inverse of {@link workItemBranch}. */\nexport function pullRequestNumberFromBranch(branch: string): number | undefined {\n const match = /^factory\\/pr-([1-9]\\d*)$/.exec(branch);\n return match ? Number(match[1]) : undefined;\n}\n"],"mappings":";;AAgBA,SAAgB,qBAAqB,gBAAiF;CACpH,IAAI,CAAC,gBAAgB,OAAO;CAC5B,IAAI,eAAe,kBAAkB,UAAU,OAAO;CAGtD,IAAI,eAAe,kBAAkB,UAAU,OAAO;CACtD,OAAO,eAAe,SAAS,iBAAiB,cAAc;AAChE;AAEA,SAAS,aAAa,UAAmC,KAAiC;CACxF,MAAM,QAAQ,SAAS,QAAQ,SAAS;CACxC,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAA;AACrF;;AAGA,SAAgB,eAAe,MAA4E;CACzG,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,IAAI,KAAK,WAAW,gBAAgB,OAAO,aAAa,UAAU,mBAAmB;CACrF,IAAI,KAAK,WAAW,aAAa,OAAO,aAAa,UAAU,yBAAyB;AAE1F;;AAGA,SAAgB,oBACd,MACQ;CACR,MAAM,SAAS,eAAe,IAAI;CAClC,IAAI,WAAW,KAAA,GAAW,OAAO,KAAK;CACtC,OAAO,GAAG,KAAK,WAAW,cAAc,OAAO,QAAQ,IAAI,OAAO,IAAI,KAAK;AAC7E;;;;;;;;AASA,SAAgB,eAAe,MAAmC;CAChE,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,MAAM,eAAe,eAAe,IAAI;CACxC,IAAI,iBAAiB,KAAA,GACnB,OAAO,KAAK,WAAW,iBAAiB,iBAAiB,iBAAiB,cAAc;CAE1F,IAAI,KAAK,WAAW,kBAAkB,OAAO,SAAS,eAAe,UAAU;EAC7E,MAAM,aAAa,SAAS,WAAW,KAAK;EAC5C,IAAI,YAAY,OAAO,kBAAkB,WAAW,YAAY;CAClE;CACA,OAAO,gBAAgB,KAAK;AAC9B;;AAGA,SAAgB,4BAA4B,QAAoC;CAC9E,MAAM,QAAQ,2BAA2B,KAAK,MAAM;CACpD,OAAO,QAAQ,OAAO,MAAM,EAAE,IAAI,KAAA;AACpC"}
|
package/dist/workspace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAA2B,MAAM,mCAAmC,CAAC;AAItG,OAAO,EAAoB,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAEV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAwB9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAA2B,MAAM,mCAAmC,CAAC;AAItG,OAAO,EAAoB,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAEV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAwB9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAQ7E,eAAO,MAAM,2BAA2B,QAQQ,CAAC;AAEjD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,SAAS,CAS7F;AAED,eAAO,MAAM,mBAAmB,aAO9B,CAAC;AAEH,qBAAa,kBAAmB,YAAW,WAAW;;IAMlD,QAAQ,CAAC,QAAQ,EAAE,WAAW;gBAArB,QAAQ,EAAE,WAAW,EAC9B,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,GAAE,MAAM,GAAG,SAA2C;IAqBjE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO3C,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAMjD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAMrD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyB7D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAI7C;AAuDD,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,6BAA6B;IAC5C,iEAAiE;IACjE,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,4BAA4B;IAC5B,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,gFAAgF;IAChF,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;iEAE6D;IAC7D,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,yBAAyB,CAAC,CAAC;IAC9D,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;IAC/C,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED,KAAK,mBAAmB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEtD,gFAAgF;AAChF,qBAAa,wBAAwB;;IAInC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI/B,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAAC,OAAO,CAAC;IAWb,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAS1D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,6BAAkC,IAuBlE,4CAA4C,uBAAuB,2MAkdlF"}
|
package/dist/workspace.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { timedPhase } from "./timing.js";
|
|
2
2
|
import { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from "./auth.js";
|
|
3
|
+
import { pullRequestNumberFromBranch } from "./work-item-branch.js";
|
|
3
4
|
import { getGithubPat } from "./integrations/github/pat.js";
|
|
4
5
|
import { registerGithubPatKind, registerGithubTokenInjector } from "./integrations/github/token-refresh.js";
|
|
5
6
|
import { DEFAULT_COMMAND_TIMEOUT_MS, SetupCommandError, checkoutSessionBranch, materializeRepo, runSetupCommand, runTeardownCommand } from "./integrations/github/sandbox.js";
|
|
@@ -412,7 +413,8 @@ function createWorkspaceFactory(options = {}) {
|
|
|
412
413
|
branch: session.branch,
|
|
413
414
|
baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,
|
|
414
415
|
token,
|
|
415
|
-
repoFullName
|
|
416
|
+
repoFullName,
|
|
417
|
+
pullRequestNumber: pullRequestNumberFromBranch(session.branch)
|
|
416
418
|
});
|
|
417
419
|
if (projectRepository.setupCommand && !gate.setupDone) {
|
|
418
420
|
if (hasFailedSetupCommand(session.id, projectRepository.setupCommand)) {
|
package/dist/workspace.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","names":["#bundledSource","#localSource","#fallbackSkillRoots","#layerFor","#isFactoryPath","#factoryPath","#entries","#generations"],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { SandboxFilesystem } from '@mastra/code-sdk/agents/sandbox-filesystem';\nimport { MASTRACODE_WORKSPACE_TOOLS } from '@mastra/code-sdk/agents/tool-availability';\nimport type { getDynamicWorkspace, WorkspaceSkillExtension } from '@mastra/code-sdk/agents/workspace';\nimport { DEFAULT_CONFIG_DIR } from '@mastra/code-sdk/constants';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { LocalSkillSource, Workspace } from '@mastra/core/workspace';\nimport type {\n SandboxStartHook,\n SkillSource,\n SkillSourceEntry,\n SkillSourceStat,\n WorkspaceSandbox,\n} from '@mastra/core/workspace';\nimport { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from './auth.js';\nimport type { MastraFactorySandboxConfig } from './factory.js';\nimport type { GithubIntegration } from './integrations/github/integration.js';\nimport { getGithubPat } from './integrations/github/pat.js';\nimport type { GithubPatKind } from './integrations/github/pat.js';\nimport {\n checkoutSessionBranch,\n DEFAULT_COMMAND_TIMEOUT_MS,\n materializeRepo,\n runSetupCommand,\n runTeardownCommand,\n SetupCommandError,\n} from './integrations/github/sandbox.js';\nimport { registerGithubPatKind, registerGithubTokenInjector } from './integrations/github/token-refresh.js';\nimport { getFactorySessionAddress } from './rules/binding-context.js';\nimport { requireExec } from './sandbox/materialization.js';\nimport type { ExecutableSandbox } from './sandbox/materialization.js';\nimport {\n createSessionSetupHook,\n evictSessionSandbox,\n getSessionSandbox,\n hasFailedSetupCommand,\n recordFailedSetupCommand,\n resolveSessionWorkdir,\n} from './sandbox/session-sandbox.js';\nimport type { SessionSetupGate } from './sandbox/session-sandbox.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 { timedPhase } from './timing.js';\n\nconst WORKSPACE_ID_PREFIX = 'mfw';\nconst bundleDirectory = dirname(fileURLToPath(import.meta.url));\nconst bundledFactorySkillsPath = join(bundleDirectory, 'factory-skills');\nexport const BUNDLED_FACTORY_SKILLS_PATH =\n [\n // Deploy bundle: the consumer copies `factory-skills/` next to the built\n // server module (e.g. via its public/ dir).\n bundledFactorySkillsPath,\n // Package layout: `dist/../factory-skills` (also `src/../factory-skills`\n // when running tests against sources).\n join(bundleDirectory, '..', 'factory-skills'),\n ].find(existsSync) ?? bundledFactorySkillsPath;\n\n/**\n * Resolve the consumer repo's local Factory skills root, if any. Checked in\n * addition to the bundled skills so projects can add (or override) Factory\n * skills without patching the installed package. Candidates cover the cwd\n * variants the dev server runs with (`repo root`, `--dir src/mastra` which\n * runs with cwd `src/mastra/public`).\n */\nexport function resolveLocalFactorySkillsPath(cwd: string = process.cwd()): string | undefined {\n const candidates = [\n join(cwd, 'src', 'mastra', 'public', 'factory-skills'),\n join(cwd, 'public', 'factory-skills'),\n join(cwd, 'factory-skills'),\n ];\n return candidates.find(\n candidate => path.normalize(candidate) !== path.normalize(BUNDLED_FACTORY_SKILLS_PATH) && existsSync(candidate),\n );\n}\nconst FACTORY_SKILLS_MOUNT = path.resolve(path.parse(process.cwd()).root, '__mastracode_factory_skills__');\nexport const FACTORY_SKILL_NAMES = new Set([\n 'configure-factory-rules',\n 'factory-complete-issue',\n 'factory-plan',\n 'factory-rereview',\n 'factory-review',\n 'factory-triage',\n]);\n\nexport class FactorySkillSource implements SkillSource {\n readonly #bundledSource = new LocalSkillSource({ basePath: BUNDLED_FACTORY_SKILLS_PATH });\n readonly #localSource: LocalSkillSource | undefined;\n readonly #fallbackSkillRoots: Set<string>;\n\n constructor(\n readonly fallback: SkillSource,\n fallbackSkillRoots: string[],\n localSkillsPath: string | undefined = resolveLocalFactorySkillsPath(),\n ) {\n this.#localSource = localSkillsPath ? new LocalSkillSource({ basePath: localSkillsPath }) : undefined;\n this.#fallbackSkillRoots = new Set(fallbackSkillRoots.map(skillPath => path.normalize(skillPath)));\n }\n\n #isFactoryPath(skillPath: string): boolean {\n const normalized = path.normalize(skillPath);\n return normalized === FACTORY_SKILLS_MOUNT || normalized.startsWith(`${FACTORY_SKILLS_MOUNT}${path.sep}`);\n }\n\n #factoryPath(skillPath: string): string {\n return path.relative(FACTORY_SKILLS_MOUNT, path.normalize(skillPath));\n }\n\n /** Pick the layer serving this mount-relative path: local wins when it has the entry. */\n async #layerFor(relativePath: string): Promise<LocalSkillSource> {\n if (this.#localSource && (await this.#localSource.exists(relativePath))) return this.#localSource;\n return this.#bundledSource;\n }\n\n async exists(skillPath: string): Promise<boolean> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.exists(skillPath);\n const relative = this.#factoryPath(skillPath);\n if (this.#localSource && (await this.#localSource.exists(relative))) return true;\n return this.#bundledSource.exists(relative);\n }\n\n async stat(skillPath: string): Promise<SkillSourceStat> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.stat(skillPath);\n const relative = this.#factoryPath(skillPath);\n return (await this.#layerFor(relative)).stat(relative);\n }\n\n async readFile(skillPath: string): Promise<string | Buffer> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.readFile(skillPath);\n const relative = this.#factoryPath(skillPath);\n return (await this.#layerFor(relative)).readFile(relative);\n }\n\n async readdir(skillPath: string): Promise<SkillSourceEntry[]> {\n if (this.#isFactoryPath(skillPath)) {\n const relative = this.#factoryPath(skillPath);\n const [bundledExists, localExists] = await Promise.all([\n this.#bundledSource.exists(relative),\n this.#localSource?.exists(relative) ?? Promise.resolve(false),\n ]);\n if (!bundledExists && !localExists) throw skillSourceEnoent(skillPath);\n const [bundledEntries, localEntries] = await Promise.all([\n bundledExists ? this.#bundledSource.readdir(relative) : [],\n localExists ? this.#localSource!.readdir(relative) : [],\n ]);\n const merged = new Map<string, SkillSourceEntry>();\n for (const entry of bundledEntries) merged.set(entry.name, entry);\n // Local entries override bundled names.\n for (const entry of localEntries) merged.set(entry.name, entry);\n return [...merged.values()];\n }\n const entries = await this.fallback.readdir(skillPath);\n if (this.#fallbackSkillRoots.has(path.normalize(skillPath))) {\n return entries.filter(entry => !FACTORY_SKILL_NAMES.has(entry.name));\n }\n return entries;\n }\n\n realpath(skillPath: string): Promise<string> {\n if (this.#isFactoryPath(skillPath)) return Promise.resolve(path.normalize(skillPath));\n return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);\n }\n}\n\n/** Build a Node-style ENOENT error so callers can treat missing skills like fs misses. */\nfunction skillSourceEnoent(skillPath: string): Error {\n const error = new Error(`ENOENT: no such file or directory, '${skillPath}'`) as Error & { code: string };\n error.code = 'ENOENT';\n return error;\n}\n\n/**\n * Sandbox-backed skill fallback that stays inert until the session sandbox is\n * actually materialized. Skill discovery runs on latency-sensitive paths (the\n * Factory start coordinator resolves the kickoff skill before the start route\n * responds); without this guard the first project-root read would hit the lazy\n * sandbox handle and force full provisioning + repo materialization. While the\n * sandbox is unmaterialized, project skill roots simply appear empty — bundled\n * Factory skills resolve from local disk via `FactorySkillSource`. Once the\n * sandbox exists, every call delegates straight through.\n */\nclass UnmaterializedAwareSkillSource implements SkillSource {\n constructor(\n readonly fallback: SkillSource,\n readonly isMaterialized: () => boolean,\n ) {}\n\n async exists(skillPath: string): Promise<boolean> {\n return this.isMaterialized() ? this.fallback.exists(skillPath) : false;\n }\n\n async stat(skillPath: string): Promise<SkillSourceStat> {\n if (!this.isMaterialized()) throw skillSourceEnoent(skillPath);\n return this.fallback.stat(skillPath);\n }\n\n async readFile(skillPath: string): Promise<string | Buffer> {\n if (!this.isMaterialized()) throw skillSourceEnoent(skillPath);\n return this.fallback.readFile(skillPath);\n }\n\n async readdir(skillPath: string): Promise<SkillSourceEntry[]> {\n return this.isMaterialized() ? this.fallback.readdir(skillPath) : [];\n }\n\n realpath(skillPath: string): Promise<string> {\n if (!this.isMaterialized()) return Promise.resolve(skillPath);\n return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);\n }\n}\n\nconst factorySkillExtension: WorkspaceSkillExtension = {\n id: 'web-factory',\n paths: [FACTORY_SKILLS_MOUNT],\n createSource: (fallback, fallbackSkillRoots) => new FactorySkillSource(fallback, fallbackSkillRoots),\n};\n\ntype DynamicWorkspaceContext = Parameters<typeof getDynamicWorkspace>[0];\n\n/**\n * When a session's sandbox boots: on the agent's first command (`'lazy'`, the\n * default) or as soon as the session's workspace is first resolved (`'eager'`).\n * An eager start is fire-and-forget; if it fails, the lazy path still runs.\n */\nexport type FactorySandboxStart = 'lazy' | 'eager';\n\nexport interface CreateWorkspaceFactoryOptions {\n /** Factory sandbox runtime config (session sandbox callback). */\n sandbox?: MastraFactorySandboxConfig;\n /** Defaults to `'lazy'`. */\n sandboxStart?: FactorySandboxStart;\n /** GitHub integration used to resolve Factory sessions and mint repo tokens. */\n github?: GithubIntegration;\n /** Work-items storage used to resolve the session's run-binding role, so\n * review-board sessions get the reviewer PAT as `GH_TOKEN`. Optional —\n * without it every session uses the default (worker) PAT. */\n workItems?: Pick<WorkItemsStorage, 'findRunBindingBySession'>;\n /** Projects storage used to authorize workspace-free supervisor sessions. */\n projects?: Pick<FactoryProjectsStorage, 'get'>;\n /** Runtime workspace/token registrations invalidated when a session retires. */\n workspaceRegistry?: FactoryWorkspaceRegistry;\n}\n\ntype WorkspaceUnregister = () => Promise<void> | void;\n\n/** Tracks dynamic Factory workspaces by persisted session id for retirement. */\nexport class FactoryWorkspaceRegistry {\n readonly #entries = new Map<string, Map<string, WorkspaceUnregister>>();\n readonly #generations = new Map<string, number>();\n\n generation(sessionId: string): number {\n return this.#generations.get(sessionId) ?? 0;\n }\n\n async register(\n sessionId: string,\n workspaceId: string,\n generation: number,\n unregister: WorkspaceUnregister,\n ): Promise<boolean> {\n if (generation !== this.generation(sessionId)) {\n await unregister();\n return false;\n }\n const entries = this.#entries.get(sessionId) ?? new Map<string, WorkspaceUnregister>();\n entries.set(workspaceId, unregister);\n this.#entries.set(sessionId, entries);\n return true;\n }\n\n async invalidateSession(sessionId: string): Promise<void> {\n this.#generations.set(sessionId, this.generation(sessionId) + 1);\n const entries = this.#entries.get(sessionId);\n if (!entries) return;\n this.#entries.delete(sessionId);\n const results = await Promise.allSettled([...entries.values()].map(unregister => unregister()));\n const failure = results.find(result => result.status === 'rejected');\n if (failure?.status === 'rejected') throw failure.reason;\n }\n}\n\nexport function createWorkspaceFactory(options: CreateWorkspaceFactoryOptions = {}) {\n const { sandbox: sandboxConfig, github, projects, workItems } = options;\n const eagerSandboxStart = options.sandboxStart === 'eager';\n const workspaceRegistry = options.workspaceRegistry ?? new FactoryWorkspaceRegistry();\n type GithubTokenRegistration = {\n inject: (token: string) => void;\n patKind: GithubPatKind;\n ghToken: string;\n generation: number;\n tokenReplacementPending: boolean;\n };\n // The session setup path runs commands and installs credentials, so it\n // needs `executeCommand` (required by `ExecutableSandbox`) plus core's\n // optional `setEnv`, which stays optional here because the token-refresh\n // path checks for it and reports its absence.\n type SessionSandbox = ExecutableSandbox & { setEnv?: WorkspaceSandbox['setEnv'] };\n const githubTokenInjectors = new Map<string, GithubTokenRegistration>();\n const githubTokenReconciliations = new Map<string, Promise<void>>();\n // Workspace identity cache: concurrent resolutions of the same session must\n // observe the same Workspace object even when no Mastra registry is wired\n // (the registry stays the source of truth when present).\n const constructedWorkspaces = new Map<string, Workspace>();\n\n return async ({ requestContext, mastra, skillExtension }: DynamicWorkspaceContext) => {\n const effectiveSkillExtension = skillExtension ?? factorySkillExtension;\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const supervisorProjectId = parseSupervisorResourceId(ctx?.resourceId);\n if (supervisorProjectId) {\n const orgId = getFactoryAuthOrgId(getFactoryAuthUserFromContext(requestContext));\n const project = orgId && projects ? await projects.get({ orgId, id: supervisorProjectId }) : null;\n if (!project) throw new Error(`Factory supervisor ${supervisorProjectId} is not available to the current user`);\n return undefined;\n }\n const session =\n ctx?.resourceId && github ? await github.sourceControlStorage.sessions.getBySessionId(ctx.resourceId) : null;\n\n if (!session) {\n // No factory session, no workspace. Chat still works; workspace tools\n // are simply not registered. Host-cwd behavior is opt-in via a\n // LocalSandbox callback rooted wherever the deployer wants — the\n // resolver never hands out the server host's own filesystem.\n return undefined;\n }\n\n const user = getFactoryAuthUserFromContext(requestContext);\n const userId = getFactoryAuthUserId(user);\n // No identity at all is a server-side caller that forgot to seed one\n // (webhook, cron), not someone reaching for another user's session.\n if (!user?.organizationId || !userId) {\n throw new Error(`Factory session ${session.sessionId} was resolved without a caller identity`);\n }\n // Org-visible sessions open to any member of the owning organization;\n // only private sessions stay owner-only. Cross-org access never passes.\n if (user.organizationId !== session.orgId || (session.visibility === 'private' && userId !== session.userId)) {\n throw new Error(`Factory session ${session.sessionId} is not available to the current user`);\n }\n if (!sandboxConfig || !github) {\n throw new Error('GitHub and a sandbox callback are required to create a Factory session workspace');\n }\n const createSessionSandboxInstance = sandboxConfig;\n\n const storage = github.sourceControlStorage;\n const projectRepository = await storage.projectRepositories.get({\n orgId: session.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);\n // The remaining reads only depend on the repository link — issue them in\n // parallel instead of paying four sequential storage round-trips.\n const [connection, repository] = await Promise.all([\n storage.connections.get({ orgId: session.orgId, id: projectRepository.connectionId }),\n storage.repositories.get({ orgId: session.orgId, id: projectRepository.repositoryId }),\n ]);\n if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);\n const installation = await storage.installations.get({ orgId: session.orgId, id: connection.installationId });\n if (!installation) throw new Error(`GitHub installation ${connection.installationId} was not found`);\n const repoFullName = repository.slug;\n\n // Construct (or fetch) the session's memoized sandbox instance.\n // Construction is cheap and side-effect-free by the callback contract —\n // the VM is provisioned on `start()`, which only the materialization\n // pipeline calls. The workdir is never persisted or trusted from storage\n // or client input (the stale-workdir incident class came from reusing\n // `session.sandboxWorkdir` written under a different provider): local\n // sandboxes derive it at construction, remote sandboxes clone into the\n // VM's own home so it resolves lazily at first start.\n // `runSetupOn` references `runSessionSetup`, defined below — it is only\n // invoked during start, long after this closure fully initializes.\n const runSetupOn = (target: unknown, workdir: string, gate: SessionSetupGate) =>\n runSessionSetup(requireExec(target as WorkspaceSandbox), workdir, gate);\n const guardedSetup = createSessionSetupHook(\n runSetupOn,\n session.id,\n repoFullName,\n projectRepository.setupCommand ?? undefined,\n );\n // Composed start hook: marker-guarded repo setup, then per-start\n // credential install. It runs inside the provider's start lifecycle on\n // EVERY start (create or reconnect) — providers own lazy start\n // (`ensureRunning()` on first command) and dead-VM self-healing (E2B\n // `retryOnDead`, Platform status reset on destroy), so a replacement VM\n // re-enters this hook and heals itself with credentials at least as\n // fresh as the start that installed them.\n const setupHook: SandboxStartHook = async args => {\n // A session retired before its first start must not set anything up.\n if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) {\n throw retiredError();\n }\n await guardedSetup(args);\n // Re-check after the (long) setup: a session retired mid-setup must not\n // register credentials for a workspace whose retirement teardown has\n // already run — the entry would leak forever. The VM itself is left to\n // the provider's idle timeout (accepted).\n if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) {\n throw retiredError();\n }\n const target: SessionSandbox = requireExec(args.sandbox);\n // The `gh` CLI needs a PAT when the org configured one (installation\n // tokens 403 on integration-restricted endpoints); git clone/checkout\n // keep using the minted installation token. Resolved per start so the\n // installed credential never outlives rotation.\n const patKind = await resolveGithubPatKind('default');\n const ghCliToken =\n (await getGithubPat(() => github.integrationStorage, session.orgId, patKind)) ?? (await getRepositoryToken());\n target.setEnv?.(env => ({ ...env, GH_TOKEN: ghCliToken }));\n // Observability only — nothing reads these columns for decisions. The\n // workdir was resolved (and memoized on the entry) by the guarded setup.\n void storage.sessions\n .setSandbox({ id: session.id, sandboxId: target.id, sandboxWorkdir: sessionEntry.workdir ?? '' })\n .catch(() => {});\n const tokenRegistration: GithubTokenRegistration = {\n inject: freshToken => {\n if (!target.setEnv) {\n throw new Error('The active sandbox provider does not support runtime GitHub token refresh.');\n }\n target.setEnv(env => ({ ...env, GH_TOKEN: freshToken }));\n tokenRegistration.ghToken = freshToken;\n },\n patKind,\n ghToken: ghCliToken,\n generation: 0,\n tokenReplacementPending: false,\n };\n githubTokenInjectors.set(workspaceId, tokenRegistration);\n registerGithubTokenContext(tokenRegistration);\n // Project skill roots were reported empty by the unmaterialized-source\n // guard before the checkout existed; rescan now. Fire-and-forget.\n void constructedWorkspaces\n .get(workspaceId)\n ?.skills?.refresh()\n .catch(() => {});\n };\n const constructSessionEntry = () =>\n getSessionSandbox(session.id, repoFullName, () => {\n const sandbox = createSessionSandboxInstance({\n sessionId: session.id,\n repoFullName,\n // Stored nullable; the context speaks `undefined` for absent.\n setupCommand: projectRepository.setupCommand ?? undefined,\n // Deferred call — only dereferenced when a provider needs the repo\n // outside the VM (template build time).\n getRepositoryAccess: () =>\n github.versionControl.getRepositoryAccess({ orgId: session.orgId, repositoryId: repository.id }),\n });\n // Attached inside the construction closure, so exactly once per\n // instance — `constructSessionEntry` runs on every open and would\n // stack a wrapper per call. Factory's setup runs first: a hook the\n // callback installed itself expects a prepared workspace.\n sandbox.setOnStart(previous => async args => {\n await timedPhase(`workspace.onStart(${args.outcome})`, async () => {\n await setupHook(args);\n });\n await previous?.(args);\n });\n // Only a freshly constructed instance starts eagerly; the start itself\n // waits for this resolver to finish because the start hook reads\n // bindings declared further down.\n startEagerly = eagerSandboxStart;\n return sandbox;\n });\n let startEagerly = false;\n const fireEagerStart = () => {\n if (!startEagerly) return;\n startEagerly = false;\n Promise.resolve()\n .then(() => sessionEntry.sandbox.start?.())\n .catch(error => {\n console.warn(`[factory] Eager sandbox start for session ${session.id} failed:`, error);\n });\n };\n const sessionEntry = constructSessionEntry();\n const workdir = sessionEntry.workdir;\n const isLocalSandbox = sessionEntry.sandbox.provider === 'local';\n // The system prompt derives its working directory from `state.projectPath`\n // and falls back to the server's own process.cwd() when unset — which\n // points the agent at the host checkout (and lets it run `git checkout`\n // there instead of in its session workdir). Pin it to the session workdir\n // once known. A remote workdir resolves at the sandbox's first start, so\n // the pin self-heals on the next resolution after the VM has run.\n if (ctx && workdir && ctx.getState()?.projectPath !== workdir) {\n await ctx.setState({ projectPath: workdir, projectName: repoFullName });\n }\n\n const extensionId = effectiveSkillExtension ? `-${effectiveSkillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectRepository.id}-${session.id}${extensionId}`;\n const workspaceGeneration = workspaceRegistry.generation(session.sessionId);\n const configDir = DEFAULT_CONFIG_DIR;\n\n const getRepositoryToken = async (): Promise<string> => {\n const access = await github.versionControl.getRepositoryAccess({\n orgId: session.orgId,\n repositoryId: repository.id,\n });\n const token = access.authorization?.token;\n if (!token) throw new Error('Repository access did not include a bearer token for the Factory session');\n return token;\n };\n const resolveGithubPatKind = async (fallback: GithubPatKind): Promise<GithubPatKind> => {\n if (!workItems) return 'default';\n try {\n const address = getFactorySessionAddress(requestContext);\n const runBinding = address ? await workItems.findRunBindingBySession(address) : null;\n return runBinding?.role === 'review' && runBinding.status === 'active' && runBinding.orgId === session.orgId\n ? 'reviewer'\n : 'default';\n } catch {\n // Preserve the installed role when binding storage is temporarily unavailable.\n return fallback;\n }\n };\n const registerGithubTokenContext = (registered: GithubTokenRegistration): void => {\n const generation = registered.generation;\n registerGithubTokenInjector(requestContext, token => {\n if (githubTokenInjectors.get(workspaceId) !== registered || registered.generation !== generation) {\n throw new Error('GitHub token refresh no longer matches the active Factory workspace role.');\n }\n registered.inject(token);\n });\n registerGithubPatKind(requestContext, registered.patKind);\n };\n const reconcileGithubToken = async (): Promise<void> => {\n const previous = githubTokenReconciliations.get(workspaceId) ?? Promise.resolve();\n const reconciliation = previous\n .catch(() => {})\n .then(async () => {\n const registered = githubTokenInjectors.get(workspaceId);\n if (!registered) return;\n\n const previousPatKind = registered.patKind;\n const patKind = await resolveGithubPatKind(previousPatKind);\n if (githubTokenInjectors.get(workspaceId) !== registered) return;\n\n if (patKind !== previousPatKind) {\n registered.patKind = patKind;\n registered.generation += 1;\n }\n if (patKind === 'reviewer') registered.tokenReplacementPending = false;\n if (previousPatKind === 'reviewer' && patKind === 'default') {\n // Invalidate reviewer refresh contexts before replacement I/O so\n // they cannot restore reviewer credentials after a failed downgrade.\n registered.tokenReplacementPending = true;\n }\n\n let token = await getGithubPat(() => github.integrationStorage, session.orgId, patKind);\n if (!token && registered.tokenReplacementPending) token = await getRepositoryToken();\n if (githubTokenInjectors.get(workspaceId) !== registered) return;\n\n if (token && token !== registered.ghToken) {\n try {\n registered.inject(token);\n } catch (error) {\n if (registered.tokenReplacementPending) throw error;\n // Same-role rotations and reviewer upgrades remain best-effort.\n }\n }\n if (token && token === registered.ghToken) registered.tokenReplacementPending = false;\n registerGithubTokenContext(registered);\n });\n githubTokenReconciliations.set(workspaceId, reconciliation);\n try {\n await reconciliation;\n } finally {\n if (githubTokenReconciliations.get(workspaceId) === reconciliation) {\n githubTokenReconciliations.delete(workspaceId);\n }\n }\n };\n const reconcileRegisteredWorkspace = async (workspace: Workspace): Promise<Workspace> => {\n const registered = githubTokenInjectors.get(workspaceId);\n try {\n await reconcileGithubToken();\n } catch (error) {\n if (registered?.tokenReplacementPending && githubTokenInjectors.get(workspaceId) === registered) {\n // The role generation already invalidated reviewer refresh contexts.\n // Keep the pending registration so failed eviction cannot make a\n // still-live reviewer workspace look safe on the next reuse.\n let evicted = false;\n try {\n evicted = (await mastra?.removeWorkspace?.(workspaceId)) === true;\n } catch {\n // Preserve the credential-replacement error and retry on the next reuse.\n }\n try {\n await workspace.destroy();\n evicted = true;\n } catch {\n // The pending registration keeps the workspace quarantined if cleanup also fails.\n }\n if (evicted && githubTokenInjectors.get(workspaceId) === registered) {\n githubTokenInjectors.delete(workspaceId);\n constructedWorkspaces.delete(workspaceId);\n }\n }\n throw error;\n }\n if (registered && githubTokenInjectors.get(workspaceId) !== registered) {\n throw new Error('Factory workspace GitHub credential registration is no longer active.');\n }\n return workspace;\n };\n\n let existing: Workspace | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace | undefined;\n } catch {\n // Not registered yet.\n existing = undefined;\n }\n existing ??= constructedWorkspaces.get(workspaceId);\n if (existing) {\n existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);\n // A materialization kicked off by another caller may still be running.\n // Deliberately do NOT wait for it: a metadata-only resolution (thread\n // list, messages, activity) must not block on the clone/setup that lazy\n // materialization exists to avoid. Token reconciliation below no-ops\n // until the leader registers the injector, and the next reuse after\n // materialization completes reconciles against the live sandbox.\n return reconcileRegisteredWorkspace(existing);\n }\n\n const retiredError = () =>\n new Error(`Factory session ${session.sessionId} was retired during workspace materialization`);\n\n // The session's setup work: materialize the repo (disk-truth idempotent),\n // check out the session branch, run the configured setup command. Minted\n // tokens are fetched inside the run so a replacement VM healed mid-session\n // gets fresh credentials, not ones captured at workspace construction.\n const runSessionSetup = async (target: SessionSandbox, workdir: string, gate: SessionSetupGate): Promise<void> => {\n const token = await getRepositoryToken();\n // The configured setup command may shell out to `gh`/https fetches, so\n // GH_TOKEN must exist before setup runs — and it must be the same\n // gh-capable credential the session gets after start (installation\n // tokens 403 on integration-restricted endpoints when the org\n // configured a PAT).\n const setupPatKind = await resolveGithubPatKind('default');\n const setupGhToken = (await getGithubPat(() => github.integrationStorage, session.orgId, setupPatKind)) ?? token;\n target.setEnv?.(env => ({ ...env, GH_TOKEN: setupGhToken }));\n await materializeRepo({\n row: { id: session.id, sandboxWorkdir: workdir, materializedAt: session.materializedAt },\n repoInfo: { repoFullName: repoFullName, defaultBranch: repository.defaultBranch },\n sandbox: target,\n token,\n storage: storage.sessions,\n });\n await checkoutSessionBranch(target, workdir, {\n branch: session.branch,\n baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,\n token,\n repoFullName: repoFullName,\n });\n if (projectRepository.setupCommand && !gate.setupDone) {\n // A setup command that already failed this session is skipped rather\n // than failing every start: the first failure surfaced loudly in the\n // tool result that triggered it, and a permanently failing onStart\n // would wedge the session — the agent could never get a shell to fix\n // the problem. Clone and checkout above still ran, so the tree is\n // real; the agent (or an edited setup command) takes it from here.\n if (hasFailedSetupCommand(session.id, projectRepository.setupCommand)) {\n console.warn('[Mastra Factory] Skipping setup command that already failed this session', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n });\n return;\n }\n try {\n await timedPhase('workspace.setup', () => runSetupCommand(target, workdir, projectRepository.setupCommand!));\n await gate.markSetupDone();\n } catch (setupError) {\n if (projectRepository.teardownCommand) {\n try {\n await runTeardownCommand(target, workdir, projectRepository.teardownCommand, {\n timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,\n });\n } catch (teardownError) {\n console.warn('[Mastra Factory] Worktree teardown after setup failure failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: teardownError instanceof Error ? teardownError.message.slice(-2000) : String(teardownError),\n });\n }\n }\n if (setupError instanceof SetupCommandError) {\n // The command ran and exited non-zero — a config problem, not an\n // infra one. Remember it so the next start recovers, and tell the\n // agent what happens next. Infra failures (transport, clone)\n // rethrow untouched and retry in full.\n recordFailedSetupCommand(session.id, projectRepository.setupCommand);\n throw new SetupCommandError(\n `${setupError.message}. The sandbox stays usable: this setup command is skipped for the rest of the session — retry your command, then fix the setup command in the repository settings or run it manually.`,\n setupError.code,\n );\n }\n throw setupError;\n }\n }\n };\n // The session's real sandbox goes straight onto the Workspace. Providers\n // own lazy start (`ensureRunning()` inside the first command/process op)\n // and dead-VM self-healing, and the composed `onStart` hook runs the repo\n // setup + credential install inside that lifecycle. Metadata-only\n // resolutions (thread-list polling) construct but never start.\n const sessionSandbox: SessionSandbox = requireExec(sessionEntry.sandbox);\n\n const filesystem = new SandboxFilesystem({\n id: `sandbox-fs:${workspaceId}`,\n sandbox: sessionSandbox,\n // Lazy: a remote workdir is only knowable once a VM runs. The first\n // file operation resolves it (starting the VM — which materializes the\n // repo via the onStart hook — when needed) and memoizes it.\n workdir: () => resolveSessionWorkdir(session.id, sessionEntry.sandbox, repoFullName),\n });\n const projectSkillPaths = [path.join(configDir, 'skills'), '.claude/skills', '.agents/skills'];\n const guardedSkillFallback = new UnmaterializedAwareSkillSource(\n filesystem,\n () => sessionEntry.sandbox.status === 'running',\n );\n const skillPaths = [...(effectiveSkillExtension?.paths ?? []), ...projectSkillPaths];\n const workspace = new Workspace({\n id: workspaceId,\n name: 'Mastra Code Factory Session Workspace',\n filesystem,\n sandbox: sessionSandbox as unknown as ConstructorParameters<typeof Workspace>[0]['sandbox'],\n tools: MASTRACODE_WORKSPACE_TOOLS,\n skills: skillPaths,\n // Project skill roots live in the sandbox checkout; guard them so skill\n // discovery before materialization (e.g. kickoff skill resolution in the\n // start coordinator) never forces sandbox provisioning.\n skillSource:\n effectiveSkillExtension?.createSource(guardedSkillFallback, projectSkillPaths) ?? guardedSkillFallback,\n });\n // Register with the Mastra instance so sync HTTP handlers that resolve\n // the workspace via `mastra.getWorkspaceById(id)` (file tree, permissions\n // probe, MCP/tool routes) find it instead of throwing\n // `MASTRA_GET_WORKSPACE_BY_ID_NOT_FOUND`. `addWorkspace` is idempotent on\n // key collision, so concurrent first resolutions stay race-safe (start\n // itself is coalesced by the sandbox base class + the session memo).\n mastra?.addWorkspace(workspace, workspaceId, { source: 'mastra' });\n // Cache synchronously with construction: the `await` below is a suspension\n // point, and a concurrent resolution for the same session must observe this\n // workspace rather than build a second one.\n constructedWorkspaces.set(workspaceId, workspace);\n // Retirement is registered against the workspace itself rather than the\n // sandbox: construction is eager while the VM start is lazy, so a session\n // retired before its first tool call still has a workspace (and possibly a\n // token injector) that must be torn down.\n const registered = await workspaceRegistry.register(\n session.sessionId,\n workspaceId,\n workspaceGeneration,\n async () => {\n githubTokenInjectors.delete(workspaceId);\n constructedWorkspaces.delete(workspaceId);\n // Retirement drops the memoized session sandbox so a later re-open\n // constructs (and the provider resolves) fresh instead of reusing an\n // instance whose VM the retirement path may stop or destroy.\n evictSessionSandbox(session.id);\n await mastra?.removeWorkspace?.(workspaceId);\n },\n );\n if (!registered) {\n throw new Error(`Factory session ${session.sessionId} was retired during workspace materialization`);\n }\n\n fireEagerStart();\n return workspace;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgDA,MAAM,sBAAsB;AAC5B,MAAM,kBAAkB,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AAC9D,MAAM,2BAA2B,KAAK,iBAAiB,gBAAgB;AACvE,MAAa,8BACX,CAGE,0BAGA,KAAK,iBAAiB,MAAM,gBAAgB,CAC9C,CAAC,CAAC,KAAK,UAAU,KAAK;;;;;;;;AASxB,SAAgB,8BAA8B,MAAc,QAAQ,IAAI,GAAuB;CAM7F,OAAO;EAJL,KAAK,KAAK,OAAO,UAAU,UAAU,gBAAgB;EACrD,KAAK,KAAK,UAAU,gBAAgB;EACpC,KAAK,KAAK,gBAAgB;CAEZ,CAAC,CAAC,MAChB,cAAa,KAAK,UAAU,SAAS,MAAM,KAAK,UAAU,2BAA2B,KAAK,WAAW,SAAS,CAChH;AACF;AACA,MAAM,uBAAuB,KAAK,QAAQ,KAAK,MAAM,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,+BAA+B;AACzG,MAAa,sCAAsB,IAAI,IAAI;CACzC;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,IAAa,qBAAb,MAAuD;CAM1C;CALX,iBAA0B,IAAI,iBAAiB,EAAE,UAAU,4BAA4B,CAAC;CACxF;CACA;CAEA,YACE,UACA,oBACA,kBAAsC,8BAA8B,GACpE;EAHS,KAAA,WAAA;EAIT,KAAKC,eAAe,kBAAkB,IAAI,iBAAiB,EAAE,UAAU,gBAAgB,CAAC,IAAI,KAAA;EAC5F,KAAKC,sBAAsB,IAAI,IAAI,mBAAmB,KAAI,cAAa,KAAK,UAAU,SAAS,CAAC,CAAC;CACnG;CAEA,eAAe,WAA4B;EACzC,MAAM,aAAa,KAAK,UAAU,SAAS;EAC3C,OAAO,eAAe,wBAAwB,WAAW,WAAW,GAAG,uBAAuB,KAAK,KAAK;CAC1G;CAEA,aAAa,WAA2B;EACtC,OAAO,KAAK,SAAS,sBAAsB,KAAK,UAAU,SAAS,CAAC;CACtE;;CAGA,MAAMC,UAAU,cAAiD;EAC/D,IAAI,KAAKF,gBAAiB,MAAM,KAAKA,aAAa,OAAO,YAAY,GAAI,OAAO,KAAKA;EACrF,OAAO,KAAKD;CACd;CAEA,MAAM,OAAO,WAAqC;EAChD,IAAI,CAAC,KAAKI,eAAe,SAAS,GAAG,OAAO,KAAK,SAAS,OAAO,SAAS;EAC1E,MAAM,WAAW,KAAKC,aAAa,SAAS;EAC5C,IAAI,KAAKJ,gBAAiB,MAAM,KAAKA,aAAa,OAAO,QAAQ,GAAI,OAAO;EAC5E,OAAO,KAAKD,eAAe,OAAO,QAAQ;CAC5C;CAEA,MAAM,KAAK,WAA6C;EACtD,IAAI,CAAC,KAAKI,eAAe,SAAS,GAAG,OAAO,KAAK,SAAS,KAAK,SAAS;EACxE,MAAM,WAAW,KAAKC,aAAa,SAAS;EAC5C,QAAQ,MAAM,KAAKF,UAAU,QAAQ,EAAA,CAAG,KAAK,QAAQ;CACvD;CAEA,MAAM,SAAS,WAA6C;EAC1D,IAAI,CAAC,KAAKC,eAAe,SAAS,GAAG,OAAO,KAAK,SAAS,SAAS,SAAS;EAC5E,MAAM,WAAW,KAAKC,aAAa,SAAS;EAC5C,QAAQ,MAAM,KAAKF,UAAU,QAAQ,EAAA,CAAG,SAAS,QAAQ;CAC3D;CAEA,MAAM,QAAQ,WAAgD;EAC5D,IAAI,KAAKC,eAAe,SAAS,GAAG;GAClC,MAAM,WAAW,KAAKC,aAAa,SAAS;GAC5C,MAAM,CAAC,eAAe,eAAe,MAAM,QAAQ,IAAI,CACrD,KAAKL,eAAe,OAAO,QAAQ,GACnC,KAAKC,cAAc,OAAO,QAAQ,KAAK,QAAQ,QAAQ,KAAK,CAC9D,CAAC;GACD,IAAI,CAAC,iBAAiB,CAAC,aAAa,MAAM,kBAAkB,SAAS;GACrE,MAAM,CAAC,gBAAgB,gBAAgB,MAAM,QAAQ,IAAI,CACvD,gBAAgB,KAAKD,eAAe,QAAQ,QAAQ,IAAI,CAAC,GACzD,cAAc,KAAKC,aAAc,QAAQ,QAAQ,IAAI,CAAC,CACxD,CAAC;GACD,MAAM,yBAAS,IAAI,IAA8B;GACjD,KAAK,MAAM,SAAS,gBAAgB,OAAO,IAAI,MAAM,MAAM,KAAK;GAEhE,KAAK,MAAM,SAAS,cAAc,OAAO,IAAI,MAAM,MAAM,KAAK;GAC9D,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC;EAC5B;EACA,MAAM,UAAU,MAAM,KAAK,SAAS,QAAQ,SAAS;EACrD,IAAI,KAAKC,oBAAoB,IAAI,KAAK,UAAU,SAAS,CAAC,GACxD,OAAO,QAAQ,QAAO,UAAS,CAAC,oBAAoB,IAAI,MAAM,IAAI,CAAC;EAErE,OAAO;CACT;CAEA,SAAS,WAAoC;EAC3C,IAAI,KAAKE,eAAe,SAAS,GAAG,OAAO,QAAQ,QAAQ,KAAK,UAAU,SAAS,CAAC;EACpF,OAAO,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS,SAAS,IAAI,QAAQ,QAAQ,SAAS;CAC/F;AACF;;AAGA,SAAS,kBAAkB,WAA0B;CACnD,MAAM,wBAAQ,IAAI,MAAM,uCAAuC,UAAU,EAAE;CAC3E,MAAM,OAAO;CACb,OAAO;AACT;;;;;;;;;;;AAYA,IAAM,iCAAN,MAA4D;CAE/C;CACA;CAFX,YACE,UACA,gBACA;EAFS,KAAA,WAAA;EACA,KAAA,iBAAA;CACR;CAEH,MAAM,OAAO,WAAqC;EAChD,OAAO,KAAK,eAAe,IAAI,KAAK,SAAS,OAAO,SAAS,IAAI;CACnE;CAEA,MAAM,KAAK,WAA6C;EACtD,IAAI,CAAC,KAAK,eAAe,GAAG,MAAM,kBAAkB,SAAS;EAC7D,OAAO,KAAK,SAAS,KAAK,SAAS;CACrC;CAEA,MAAM,SAAS,WAA6C;EAC1D,IAAI,CAAC,KAAK,eAAe,GAAG,MAAM,kBAAkB,SAAS;EAC7D,OAAO,KAAK,SAAS,SAAS,SAAS;CACzC;CAEA,MAAM,QAAQ,WAAgD;EAC5D,OAAO,KAAK,eAAe,IAAI,KAAK,SAAS,QAAQ,SAAS,IAAI,CAAC;CACrE;CAEA,SAAS,WAAoC;EAC3C,IAAI,CAAC,KAAK,eAAe,GAAG,OAAO,QAAQ,QAAQ,SAAS;EAC5D,OAAO,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS,SAAS,IAAI,QAAQ,QAAQ,SAAS;CAC/F;AACF;AAEA,MAAM,wBAAiD;CACrD,IAAI;CACJ,OAAO,CAAC,oBAAoB;CAC5B,eAAe,UAAU,uBAAuB,IAAI,mBAAmB,UAAU,kBAAkB;AACrG;;AA+BA,IAAa,2BAAb,MAAsC;CACpC,2BAAoB,IAAI,IAA8C;CACtE,+BAAwB,IAAI,IAAoB;CAEhD,WAAW,WAA2B;EACpC,OAAO,KAAKG,aAAa,IAAI,SAAS,KAAK;CAC7C;CAEA,MAAM,SACJ,WACA,aACA,YACA,YACkB;EAClB,IAAI,eAAe,KAAK,WAAW,SAAS,GAAG;GAC7C,MAAM,WAAW;GACjB,OAAO;EACT;EACA,MAAM,UAAU,KAAKD,SAAS,IAAI,SAAS,qBAAK,IAAI,IAAiC;EACrF,QAAQ,IAAI,aAAa,UAAU;EACnC,KAAKA,SAAS,IAAI,WAAW,OAAO;EACpC,OAAO;CACT;CAEA,MAAM,kBAAkB,WAAkC;EACxD,KAAKC,aAAa,IAAI,WAAW,KAAK,WAAW,SAAS,IAAI,CAAC;EAC/D,MAAM,UAAU,KAAKD,SAAS,IAAI,SAAS;EAC3C,IAAI,CAAC,SAAS;EACd,KAAKA,SAAS,OAAO,SAAS;EAE9B,MAAM,WAAU,MADM,QAAQ,WAAW,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAI,eAAc,WAAW,CAAC,CAAC,EAAA,CACtE,MAAK,WAAU,OAAO,WAAW,UAAU;EACnE,IAAI,SAAS,WAAW,YAAY,MAAM,QAAQ;CACpD;AACF;AAEA,SAAgB,uBAAuB,UAAyC,CAAC,GAAG;CAClF,MAAM,EAAE,SAAS,eAAe,QAAQ,UAAU,cAAc;CAChE,MAAM,oBAAoB,QAAQ,iBAAiB;CACnD,MAAM,oBAAoB,QAAQ,qBAAqB,IAAI,yBAAyB;CAapF,MAAM,uCAAuB,IAAI,IAAqC;CACtE,MAAM,6CAA6B,IAAI,IAA2B;CAIlE,MAAM,wCAAwB,IAAI,IAAuB;CAEzD,OAAO,OAAO,EAAE,gBAAgB,QAAQ,qBAA8C;EACpF,MAAM,0BAA0B,kBAAkB;EAClD,MAAM,MAAM,eAAe,IAAI,YAAY;EAC3C,MAAM,sBAAsB,0BAA0B,KAAK,UAAU;EACrE,IAAI,qBAAqB;GACvB,MAAM,QAAQ,oBAAoB,8BAA8B,cAAc,CAAC;GAE/E,IAAI,EADY,SAAS,WAAW,MAAM,SAAS,IAAI;IAAE;IAAO,IAAI;GAAoB,CAAC,IAAI,OAC/E,MAAM,IAAI,MAAM,sBAAsB,oBAAoB,sCAAsC;GAC9G;EACF;EACA,MAAM,UACJ,KAAK,cAAc,SAAS,MAAM,OAAO,qBAAqB,SAAS,eAAe,IAAI,UAAU,IAAI;EAE1G,IAAI,CAAC,SAKH;EAGF,MAAM,OAAO,8BAA8B,cAAc;EACzD,MAAM,SAAS,qBAAqB,IAAI;EAGxC,IAAI,CAAC,MAAM,kBAAkB,CAAC,QAC5B,MAAM,IAAI,MAAM,mBAAmB,QAAQ,UAAU,wCAAwC;EAI/F,IAAI,KAAK,mBAAmB,QAAQ,SAAU,QAAQ,eAAe,aAAa,WAAW,QAAQ,QACnG,MAAM,IAAI,MAAM,mBAAmB,QAAQ,UAAU,sCAAsC;EAE7F,IAAI,CAAC,iBAAiB,CAAC,QACrB,MAAM,IAAI,MAAM,kFAAkF;EAEpG,MAAM,+BAA+B;EAErC,MAAM,UAAU,OAAO;EACvB,MAAM,oBAAoB,MAAM,QAAQ,oBAAoB,IAAI;GAC9D,OAAO,QAAQ;GACf,IAAI,QAAQ;EACd,CAAC;EACD,IAAI,CAAC,mBAAmB,MAAM,IAAI,MAAM,mBAAmB,QAAQ,oBAAoB,eAAe;EAGtG,MAAM,CAAC,YAAY,cAAc,MAAM,QAAQ,IAAI,CACjD,QAAQ,YAAY,IAAI;GAAE,OAAO,QAAQ;GAAO,IAAI,kBAAkB;EAAa,CAAC,GACpF,QAAQ,aAAa,IAAI;GAAE,OAAO,QAAQ;GAAO,IAAI,kBAAkB;EAAa,CAAC,CACvF,CAAC;EACD,IAAI,CAAC,cAAc,CAAC,YAAY,MAAM,IAAI,MAAM,mBAAmB,QAAQ,oBAAoB,eAAe;EAE9G,IAAI,CAAC,MADsB,QAAQ,cAAc,IAAI;GAAE,OAAO,QAAQ;GAAO,IAAI,WAAW;EAAe,CAAC,GACzF,MAAM,IAAI,MAAM,uBAAuB,WAAW,eAAe,eAAe;EACnG,MAAM,eAAe,WAAW;EAYhC,MAAM,cAAc,QAAiB,SAAiB,SACpD,gBAAgB,YAAY,MAA0B,GAAG,SAAS,IAAI;EACxE,MAAM,eAAe,uBACnB,YACA,QAAQ,IACR,cACA,kBAAkB,gBAAgB,KAAA,CACpC;EAQA,MAAM,YAA8B,OAAM,SAAQ;GAEhD,IAAI,kBAAkB,WAAW,QAAQ,SAAS,MAAM,qBACtD,MAAM,aAAa;GAErB,MAAM,aAAa,IAAI;GAKvB,IAAI,kBAAkB,WAAW,QAAQ,SAAS,MAAM,qBACtD,MAAM,aAAa;GAErB,MAAM,SAAyB,YAAY,KAAK,OAAO;GAKvD,MAAM,UAAU,MAAM,qBAAqB,SAAS;GACpD,MAAM,aACH,MAAM,mBAAmB,OAAO,oBAAoB,QAAQ,OAAO,OAAO,KAAO,MAAM,mBAAmB;GAC7G,OAAO,UAAS,SAAQ;IAAE,GAAG;IAAK,UAAU;GAAW,EAAE;GAGzD,QAAa,SACV,WAAW;IAAE,IAAI,QAAQ;IAAI,WAAW,OAAO;IAAI,gBAAgB,aAAa,WAAW;GAAG,CAAC,CAAC,CAChG,YAAY,CAAC,CAAC;GACjB,MAAM,oBAA6C;IACjD,SAAQ,eAAc;KACpB,IAAI,CAAC,OAAO,QACV,MAAM,IAAI,MAAM,4EAA4E;KAE9F,OAAO,QAAO,SAAQ;MAAE,GAAG;MAAK,UAAU;KAAW,EAAE;KACvD,kBAAkB,UAAU;IAC9B;IACA;IACA,SAAS;IACT,YAAY;IACZ,yBAAyB;GAC3B;GACA,qBAAqB,IAAI,aAAa,iBAAiB;GACvD,2BAA2B,iBAAiB;GAG5C,sBACG,IAAI,WAAW,CAAC,EACf,QAAQ,QAAQ,CAAC,CAClB,YAAY,CAAC,CAAC;EACnB;EACA,MAAM,8BACJ,kBAAkB,QAAQ,IAAI,oBAAoB;GAChD,MAAM,UAAU,6BAA6B;IAC3C,WAAW,QAAQ;IACnB;IAEA,cAAc,kBAAkB,gBAAgB,KAAA;IAGhD,2BACE,OAAO,eAAe,oBAAoB;KAAE,OAAO,QAAQ;KAAO,cAAc,WAAW;IAAG,CAAC;GACnG,CAAC;GAKD,QAAQ,YAAW,aAAY,OAAM,SAAQ;IAC3C,MAAM,WAAW,qBAAqB,KAAK,QAAQ,IAAI,YAAY;KACjE,MAAM,UAAU,IAAI;IACtB,CAAC;IACD,MAAM,WAAW,IAAI;GACvB,CAAC;GAID,eAAe;GACf,OAAO;EACT,CAAC;EACH,IAAI,eAAe;EACnB,MAAM,uBAAuB;GAC3B,IAAI,CAAC,cAAc;GACnB,eAAe;GACf,QAAQ,QAAQ,CAAC,CACd,WAAW,aAAa,QAAQ,QAAQ,CAAC,CAAC,CAC1C,OAAM,UAAS;IACd,QAAQ,KAAK,6CAA6C,QAAQ,GAAG,WAAW,KAAK;GACvF,CAAC;EACL;EACA,MAAM,eAAe,sBAAsB;EAC3C,MAAM,UAAU,aAAa;EACN,aAAa,QAAQ;EAO5C,IAAI,OAAO,WAAW,IAAI,SAAS,CAAC,EAAE,gBAAgB,SACpD,MAAM,IAAI,SAAS;GAAE,aAAa;GAAS,aAAa;EAAa,CAAC;EAGxE,MAAM,cAAc,0BAA0B,IAAI,wBAAwB,OAAO;EACjF,MAAM,cAAc,GAAG,oBAAoB,GAAG,kBAAkB,GAAG,GAAG,QAAQ,KAAK;EACnF,MAAM,sBAAsB,kBAAkB,WAAW,QAAQ,SAAS;EAC1E,MAAM,YAAY;EAElB,MAAM,qBAAqB,YAA6B;GAKtD,MAAM,SAAQ,MAJO,OAAO,eAAe,oBAAoB;IAC7D,OAAO,QAAQ;IACf,cAAc,WAAW;GAC3B,CAAC,EAAA,CACoB,eAAe;GACpC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,0EAA0E;GACtG,OAAO;EACT;EACA,MAAM,uBAAuB,OAAO,aAAoD;GACtF,IAAI,CAAC,WAAW,OAAO;GACvB,IAAI;IACF,MAAM,UAAU,yBAAyB,cAAc;IACvD,MAAM,aAAa,UAAU,MAAM,UAAU,wBAAwB,OAAO,IAAI;IAChF,OAAO,YAAY,SAAS,YAAY,WAAW,WAAW,YAAY,WAAW,UAAU,QAAQ,QACnG,aACA;GACN,QAAQ;IAEN,OAAO;GACT;EACF;EACA,MAAM,8BAA8B,eAA8C;GAChF,MAAM,aAAa,WAAW;GAC9B,4BAA4B,iBAAgB,UAAS;IACnD,IAAI,qBAAqB,IAAI,WAAW,MAAM,cAAc,WAAW,eAAe,YACpF,MAAM,IAAI,MAAM,2EAA2E;IAE7F,WAAW,OAAO,KAAK;GACzB,CAAC;GACD,sBAAsB,gBAAgB,WAAW,OAAO;EAC1D;EACA,MAAM,uBAAuB,YAA2B;GAEtD,MAAM,kBADW,2BAA2B,IAAI,WAAW,KAAK,QAAQ,QAAQ,EAAA,CAE7E,YAAY,CAAC,CAAC,CAAC,CACf,KAAK,YAAY;IAChB,MAAM,aAAa,qBAAqB,IAAI,WAAW;IACvD,IAAI,CAAC,YAAY;IAEjB,MAAM,kBAAkB,WAAW;IACnC,MAAM,UAAU,MAAM,qBAAqB,eAAe;IAC1D,IAAI,qBAAqB,IAAI,WAAW,MAAM,YAAY;IAE1D,IAAI,YAAY,iBAAiB;KAC/B,WAAW,UAAU;KACrB,WAAW,cAAc;IAC3B;IACA,IAAI,YAAY,YAAY,WAAW,0BAA0B;IACjE,IAAI,oBAAoB,cAAc,YAAY,WAGhD,WAAW,0BAA0B;IAGvC,IAAI,QAAQ,MAAM,mBAAmB,OAAO,oBAAoB,QAAQ,OAAO,OAAO;IACtF,IAAI,CAAC,SAAS,WAAW,yBAAyB,QAAQ,MAAM,mBAAmB;IACnF,IAAI,qBAAqB,IAAI,WAAW,MAAM,YAAY;IAE1D,IAAI,SAAS,UAAU,WAAW,SAChC,IAAI;KACF,WAAW,OAAO,KAAK;IACzB,SAAS,OAAO;KACd,IAAI,WAAW,yBAAyB,MAAM;IAEhD;IAEF,IAAI,SAAS,UAAU,WAAW,SAAS,WAAW,0BAA0B;IAChF,2BAA2B,UAAU;GACvC,CAAC;GACH,2BAA2B,IAAI,aAAa,cAAc;GAC1D,IAAI;IACF,MAAM;GACR,UAAU;IACR,IAAI,2BAA2B,IAAI,WAAW,MAAM,gBAClD,2BAA2B,OAAO,WAAW;GAEjD;EACF;EACA,MAAM,+BAA+B,OAAO,cAA6C;GACvF,MAAM,aAAa,qBAAqB,IAAI,WAAW;GACvD,IAAI;IACF,MAAM,qBAAqB;GAC7B,SAAS,OAAO;IACd,IAAI,YAAY,2BAA2B,qBAAqB,IAAI,WAAW,MAAM,YAAY;KAI/F,IAAI,UAAU;KACd,IAAI;MACF,UAAW,MAAM,QAAQ,kBAAkB,WAAW,MAAO;KAC/D,QAAQ,CAER;KACA,IAAI;MACF,MAAM,UAAU,QAAQ;MACxB,UAAU;KACZ,QAAQ,CAER;KACA,IAAI,WAAW,qBAAqB,IAAI,WAAW,MAAM,YAAY;MACnE,qBAAqB,OAAO,WAAW;MACvC,sBAAsB,OAAO,WAAW;KAC1C;IACF;IACA,MAAM;GACR;GACA,IAAI,cAAc,qBAAqB,IAAI,WAAW,MAAM,YAC1D,MAAM,IAAI,MAAM,uEAAuE;GAEzF,OAAO;EACT;EAEA,IAAI;EACJ,IAAI;GACF,WAAW,QAAQ,iBAAiB,WAAW;EACjD,QAAQ;GAEN,WAAW,KAAA;EACb;EACA,aAAa,sBAAsB,IAAI,WAAW;EAClD,IAAI,UAAU;GACZ,SAAS,eAAe,0BAA0B;GAOlD,OAAO,6BAA6B,QAAQ;EAC9C;EAEA,MAAM,qCACJ,IAAI,MAAM,mBAAmB,QAAQ,UAAU,8CAA8C;EAM/F,MAAM,kBAAkB,OAAO,QAAwB,SAAiB,SAA0C;GAChH,MAAM,QAAQ,MAAM,mBAAmB;GAMvC,MAAM,eAAe,MAAM,qBAAqB,SAAS;GACzD,MAAM,eAAgB,MAAM,mBAAmB,OAAO,oBAAoB,QAAQ,OAAO,YAAY,KAAM;GAC3G,OAAO,UAAS,SAAQ;IAAE,GAAG;IAAK,UAAU;GAAa,EAAE;GAC3D,MAAM,gBAAgB;IACpB,KAAK;KAAE,IAAI,QAAQ;KAAI,gBAAgB;KAAS,gBAAgB,QAAQ;IAAe;IACvF,UAAU;KAAgB;KAAc,eAAe,WAAW;IAAc;IAChF,SAAS;IACT;IACA,SAAS,QAAQ;GACnB,CAAC;GACD,MAAM,sBAAsB,QAAQ,SAAS;IAC3C,QAAQ,QAAQ;IAChB,YAAY,QAAQ,cAAc,kBAAkB,UAAU,WAAW;IACzE;IACc;GAChB,CAAC;GACD,IAAI,kBAAkB,gBAAgB,CAAC,KAAK,WAAW;IAOrD,IAAI,sBAAsB,QAAQ,IAAI,kBAAkB,YAAY,GAAG;KACrE,QAAQ,KAAK,4EAA4E;MACvF,OAAO,QAAQ;MACf,WAAW,QAAQ;MACnB,qBAAqB,QAAQ;KAC/B,CAAC;KACD;IACF;IACA,IAAI;KACF,MAAM,WAAW,yBAAyB,gBAAgB,QAAQ,SAAS,kBAAkB,YAAa,CAAC;KAC3G,MAAM,KAAK,cAAc;IAC3B,SAAS,YAAY;KACnB,IAAI,kBAAkB,iBACpB,IAAI;MACF,MAAM,mBAAmB,QAAQ,SAAS,kBAAkB,iBAAiB,EAC3E,WAAW,2BACb,CAAC;KACH,SAAS,eAAe;MACtB,QAAQ,KAAK,iEAAiE;OAC5E,OAAO,QAAQ;OACf,WAAW,QAAQ;OACnB,qBAAqB,QAAQ;OAC7B,OAAO,yBAAyB,QAAQ,cAAc,QAAQ,MAAM,IAAK,IAAI,OAAO,aAAa;MACnG,CAAC;KACH;KAEF,IAAI,sBAAsB,mBAAmB;MAK3C,yBAAyB,QAAQ,IAAI,kBAAkB,YAAY;MACnE,MAAM,IAAI,kBACR,GAAG,WAAW,QAAQ,wLACtB,WAAW,IACb;KACF;KACA,MAAM;IACR;GACF;EACF;EAMA,MAAM,iBAAiC,YAAY,aAAa,OAAO;EAEvE,MAAM,aAAa,IAAI,kBAAkB;GACvC,IAAI,cAAc;GAClB,SAAS;GAIT,eAAe,sBAAsB,QAAQ,IAAI,aAAa,SAAS,YAAY;EACrF,CAAC;EACD,MAAM,oBAAoB;GAAC,KAAK,KAAK,WAAW,QAAQ;GAAG;GAAkB;EAAgB;EAC7F,MAAM,uBAAuB,IAAI,+BAC/B,kBACM,aAAa,QAAQ,WAAW,SACxC;EAEA,MAAM,YAAY,IAAI,UAAU;GAC9B,IAAI;GACJ,MAAM;GACN;GACA,SAAS;GACT,OAAO;GACP,QAAQ,CAPU,GAAI,yBAAyB,SAAS,CAAC,GAAI,GAAG,iBAO/C;GAIjB,aACE,yBAAyB,aAAa,sBAAsB,iBAAiB,KAAK;EACtF,CAAC;EAOD,QAAQ,aAAa,WAAW,aAAa,EAAE,QAAQ,SAAS,CAAC;EAIjE,sBAAsB,IAAI,aAAa,SAAS;EAmBhD,IAAI,CAAC,MAdoB,kBAAkB,SACzC,QAAQ,WACR,aACA,qBACA,YAAY;GACV,qBAAqB,OAAO,WAAW;GACvC,sBAAsB,OAAO,WAAW;GAIxC,oBAAoB,QAAQ,EAAE;GAC9B,MAAM,QAAQ,kBAAkB,WAAW;EAC7C,CACF,GAEE,MAAM,IAAI,MAAM,mBAAmB,QAAQ,UAAU,8CAA8C;EAGrG,eAAe;EACf,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"workspace.js","names":["#bundledSource","#localSource","#fallbackSkillRoots","#layerFor","#isFactoryPath","#factoryPath","#entries","#generations"],"sources":["../src/workspace.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { SandboxFilesystem } from '@mastra/code-sdk/agents/sandbox-filesystem';\nimport { MASTRACODE_WORKSPACE_TOOLS } from '@mastra/code-sdk/agents/tool-availability';\nimport type { getDynamicWorkspace, WorkspaceSkillExtension } from '@mastra/code-sdk/agents/workspace';\nimport { DEFAULT_CONFIG_DIR } from '@mastra/code-sdk/constants';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { LocalSkillSource, Workspace } from '@mastra/core/workspace';\nimport type {\n SandboxStartHook,\n SkillSource,\n SkillSourceEntry,\n SkillSourceStat,\n WorkspaceSandbox,\n} from '@mastra/core/workspace';\nimport { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from './auth.js';\nimport type { MastraFactorySandboxConfig } from './factory.js';\nimport type { GithubIntegration } from './integrations/github/integration.js';\nimport { getGithubPat } from './integrations/github/pat.js';\nimport type { GithubPatKind } from './integrations/github/pat.js';\nimport {\n checkoutSessionBranch,\n DEFAULT_COMMAND_TIMEOUT_MS,\n materializeRepo,\n runSetupCommand,\n runTeardownCommand,\n SetupCommandError,\n} from './integrations/github/sandbox.js';\nimport { registerGithubPatKind, registerGithubTokenInjector } from './integrations/github/token-refresh.js';\nimport { getFactorySessionAddress } from './rules/binding-context.js';\nimport { requireExec } from './sandbox/materialization.js';\nimport type { ExecutableSandbox } from './sandbox/materialization.js';\nimport {\n createSessionSetupHook,\n evictSessionSandbox,\n getSessionSandbox,\n hasFailedSetupCommand,\n recordFailedSetupCommand,\n resolveSessionWorkdir,\n} from './sandbox/session-sandbox.js';\nimport type { SessionSetupGate } from './sandbox/session-sandbox.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 { timedPhase } from './timing.js';\nimport { pullRequestNumberFromBranch } from './work-item-branch.js';\n\nconst WORKSPACE_ID_PREFIX = 'mfw';\nconst bundleDirectory = dirname(fileURLToPath(import.meta.url));\nconst bundledFactorySkillsPath = join(bundleDirectory, 'factory-skills');\nexport const BUNDLED_FACTORY_SKILLS_PATH =\n [\n // Deploy bundle: the consumer copies `factory-skills/` next to the built\n // server module (e.g. via its public/ dir).\n bundledFactorySkillsPath,\n // Package layout: `dist/../factory-skills` (also `src/../factory-skills`\n // when running tests against sources).\n join(bundleDirectory, '..', 'factory-skills'),\n ].find(existsSync) ?? bundledFactorySkillsPath;\n\n/**\n * Resolve the consumer repo's local Factory skills root, if any. Checked in\n * addition to the bundled skills so projects can add (or override) Factory\n * skills without patching the installed package. Candidates cover the cwd\n * variants the dev server runs with (`repo root`, `--dir src/mastra` which\n * runs with cwd `src/mastra/public`).\n */\nexport function resolveLocalFactorySkillsPath(cwd: string = process.cwd()): string | undefined {\n const candidates = [\n join(cwd, 'src', 'mastra', 'public', 'factory-skills'),\n join(cwd, 'public', 'factory-skills'),\n join(cwd, 'factory-skills'),\n ];\n return candidates.find(\n candidate => path.normalize(candidate) !== path.normalize(BUNDLED_FACTORY_SKILLS_PATH) && existsSync(candidate),\n );\n}\nconst FACTORY_SKILLS_MOUNT = path.resolve(path.parse(process.cwd()).root, '__mastracode_factory_skills__');\nexport const FACTORY_SKILL_NAMES = new Set([\n 'configure-factory-rules',\n 'factory-complete-issue',\n 'factory-plan',\n 'factory-rereview',\n 'factory-review',\n 'factory-triage',\n]);\n\nexport class FactorySkillSource implements SkillSource {\n readonly #bundledSource = new LocalSkillSource({ basePath: BUNDLED_FACTORY_SKILLS_PATH });\n readonly #localSource: LocalSkillSource | undefined;\n readonly #fallbackSkillRoots: Set<string>;\n\n constructor(\n readonly fallback: SkillSource,\n fallbackSkillRoots: string[],\n localSkillsPath: string | undefined = resolveLocalFactorySkillsPath(),\n ) {\n this.#localSource = localSkillsPath ? new LocalSkillSource({ basePath: localSkillsPath }) : undefined;\n this.#fallbackSkillRoots = new Set(fallbackSkillRoots.map(skillPath => path.normalize(skillPath)));\n }\n\n #isFactoryPath(skillPath: string): boolean {\n const normalized = path.normalize(skillPath);\n return normalized === FACTORY_SKILLS_MOUNT || normalized.startsWith(`${FACTORY_SKILLS_MOUNT}${path.sep}`);\n }\n\n #factoryPath(skillPath: string): string {\n return path.relative(FACTORY_SKILLS_MOUNT, path.normalize(skillPath));\n }\n\n /** Pick the layer serving this mount-relative path: local wins when it has the entry. */\n async #layerFor(relativePath: string): Promise<LocalSkillSource> {\n if (this.#localSource && (await this.#localSource.exists(relativePath))) return this.#localSource;\n return this.#bundledSource;\n }\n\n async exists(skillPath: string): Promise<boolean> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.exists(skillPath);\n const relative = this.#factoryPath(skillPath);\n if (this.#localSource && (await this.#localSource.exists(relative))) return true;\n return this.#bundledSource.exists(relative);\n }\n\n async stat(skillPath: string): Promise<SkillSourceStat> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.stat(skillPath);\n const relative = this.#factoryPath(skillPath);\n return (await this.#layerFor(relative)).stat(relative);\n }\n\n async readFile(skillPath: string): Promise<string | Buffer> {\n if (!this.#isFactoryPath(skillPath)) return this.fallback.readFile(skillPath);\n const relative = this.#factoryPath(skillPath);\n return (await this.#layerFor(relative)).readFile(relative);\n }\n\n async readdir(skillPath: string): Promise<SkillSourceEntry[]> {\n if (this.#isFactoryPath(skillPath)) {\n const relative = this.#factoryPath(skillPath);\n const [bundledExists, localExists] = await Promise.all([\n this.#bundledSource.exists(relative),\n this.#localSource?.exists(relative) ?? Promise.resolve(false),\n ]);\n if (!bundledExists && !localExists) throw skillSourceEnoent(skillPath);\n const [bundledEntries, localEntries] = await Promise.all([\n bundledExists ? this.#bundledSource.readdir(relative) : [],\n localExists ? this.#localSource!.readdir(relative) : [],\n ]);\n const merged = new Map<string, SkillSourceEntry>();\n for (const entry of bundledEntries) merged.set(entry.name, entry);\n // Local entries override bundled names.\n for (const entry of localEntries) merged.set(entry.name, entry);\n return [...merged.values()];\n }\n const entries = await this.fallback.readdir(skillPath);\n if (this.#fallbackSkillRoots.has(path.normalize(skillPath))) {\n return entries.filter(entry => !FACTORY_SKILL_NAMES.has(entry.name));\n }\n return entries;\n }\n\n realpath(skillPath: string): Promise<string> {\n if (this.#isFactoryPath(skillPath)) return Promise.resolve(path.normalize(skillPath));\n return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);\n }\n}\n\n/** Build a Node-style ENOENT error so callers can treat missing skills like fs misses. */\nfunction skillSourceEnoent(skillPath: string): Error {\n const error = new Error(`ENOENT: no such file or directory, '${skillPath}'`) as Error & { code: string };\n error.code = 'ENOENT';\n return error;\n}\n\n/**\n * Sandbox-backed skill fallback that stays inert until the session sandbox is\n * actually materialized. Skill discovery runs on latency-sensitive paths (the\n * Factory start coordinator resolves the kickoff skill before the start route\n * responds); without this guard the first project-root read would hit the lazy\n * sandbox handle and force full provisioning + repo materialization. While the\n * sandbox is unmaterialized, project skill roots simply appear empty — bundled\n * Factory skills resolve from local disk via `FactorySkillSource`. Once the\n * sandbox exists, every call delegates straight through.\n */\nclass UnmaterializedAwareSkillSource implements SkillSource {\n constructor(\n readonly fallback: SkillSource,\n readonly isMaterialized: () => boolean,\n ) {}\n\n async exists(skillPath: string): Promise<boolean> {\n return this.isMaterialized() ? this.fallback.exists(skillPath) : false;\n }\n\n async stat(skillPath: string): Promise<SkillSourceStat> {\n if (!this.isMaterialized()) throw skillSourceEnoent(skillPath);\n return this.fallback.stat(skillPath);\n }\n\n async readFile(skillPath: string): Promise<string | Buffer> {\n if (!this.isMaterialized()) throw skillSourceEnoent(skillPath);\n return this.fallback.readFile(skillPath);\n }\n\n async readdir(skillPath: string): Promise<SkillSourceEntry[]> {\n return this.isMaterialized() ? this.fallback.readdir(skillPath) : [];\n }\n\n realpath(skillPath: string): Promise<string> {\n if (!this.isMaterialized()) return Promise.resolve(skillPath);\n return this.fallback.realpath ? this.fallback.realpath(skillPath) : Promise.resolve(skillPath);\n }\n}\n\nconst factorySkillExtension: WorkspaceSkillExtension = {\n id: 'web-factory',\n paths: [FACTORY_SKILLS_MOUNT],\n createSource: (fallback, fallbackSkillRoots) => new FactorySkillSource(fallback, fallbackSkillRoots),\n};\n\ntype DynamicWorkspaceContext = Parameters<typeof getDynamicWorkspace>[0];\n\n/**\n * When a session's sandbox boots: on the agent's first command (`'lazy'`, the\n * default) or as soon as the session's workspace is first resolved (`'eager'`).\n * An eager start is fire-and-forget; if it fails, the lazy path still runs.\n */\nexport type FactorySandboxStart = 'lazy' | 'eager';\n\nexport interface CreateWorkspaceFactoryOptions {\n /** Factory sandbox runtime config (session sandbox callback). */\n sandbox?: MastraFactorySandboxConfig;\n /** Defaults to `'lazy'`. */\n sandboxStart?: FactorySandboxStart;\n /** GitHub integration used to resolve Factory sessions and mint repo tokens. */\n github?: GithubIntegration;\n /** Work-items storage used to resolve the session's run-binding role, so\n * review-board sessions get the reviewer PAT as `GH_TOKEN`. Optional —\n * without it every session uses the default (worker) PAT. */\n workItems?: Pick<WorkItemsStorage, 'findRunBindingBySession'>;\n /** Projects storage used to authorize workspace-free supervisor sessions. */\n projects?: Pick<FactoryProjectsStorage, 'get'>;\n /** Runtime workspace/token registrations invalidated when a session retires. */\n workspaceRegistry?: FactoryWorkspaceRegistry;\n}\n\ntype WorkspaceUnregister = () => Promise<void> | void;\n\n/** Tracks dynamic Factory workspaces by persisted session id for retirement. */\nexport class FactoryWorkspaceRegistry {\n readonly #entries = new Map<string, Map<string, WorkspaceUnregister>>();\n readonly #generations = new Map<string, number>();\n\n generation(sessionId: string): number {\n return this.#generations.get(sessionId) ?? 0;\n }\n\n async register(\n sessionId: string,\n workspaceId: string,\n generation: number,\n unregister: WorkspaceUnregister,\n ): Promise<boolean> {\n if (generation !== this.generation(sessionId)) {\n await unregister();\n return false;\n }\n const entries = this.#entries.get(sessionId) ?? new Map<string, WorkspaceUnregister>();\n entries.set(workspaceId, unregister);\n this.#entries.set(sessionId, entries);\n return true;\n }\n\n async invalidateSession(sessionId: string): Promise<void> {\n this.#generations.set(sessionId, this.generation(sessionId) + 1);\n const entries = this.#entries.get(sessionId);\n if (!entries) return;\n this.#entries.delete(sessionId);\n const results = await Promise.allSettled([...entries.values()].map(unregister => unregister()));\n const failure = results.find(result => result.status === 'rejected');\n if (failure?.status === 'rejected') throw failure.reason;\n }\n}\n\nexport function createWorkspaceFactory(options: CreateWorkspaceFactoryOptions = {}) {\n const { sandbox: sandboxConfig, github, projects, workItems } = options;\n const eagerSandboxStart = options.sandboxStart === 'eager';\n const workspaceRegistry = options.workspaceRegistry ?? new FactoryWorkspaceRegistry();\n type GithubTokenRegistration = {\n inject: (token: string) => void;\n patKind: GithubPatKind;\n ghToken: string;\n generation: number;\n tokenReplacementPending: boolean;\n };\n // The session setup path runs commands and installs credentials, so it\n // needs `executeCommand` (required by `ExecutableSandbox`) plus core's\n // optional `setEnv`, which stays optional here because the token-refresh\n // path checks for it and reports its absence.\n type SessionSandbox = ExecutableSandbox & { setEnv?: WorkspaceSandbox['setEnv'] };\n const githubTokenInjectors = new Map<string, GithubTokenRegistration>();\n const githubTokenReconciliations = new Map<string, Promise<void>>();\n // Workspace identity cache: concurrent resolutions of the same session must\n // observe the same Workspace object even when no Mastra registry is wired\n // (the registry stays the source of truth when present).\n const constructedWorkspaces = new Map<string, Workspace>();\n\n return async ({ requestContext, mastra, skillExtension }: DynamicWorkspaceContext) => {\n const effectiveSkillExtension = skillExtension ?? factorySkillExtension;\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const supervisorProjectId = parseSupervisorResourceId(ctx?.resourceId);\n if (supervisorProjectId) {\n const orgId = getFactoryAuthOrgId(getFactoryAuthUserFromContext(requestContext));\n const project = orgId && projects ? await projects.get({ orgId, id: supervisorProjectId }) : null;\n if (!project) throw new Error(`Factory supervisor ${supervisorProjectId} is not available to the current user`);\n return undefined;\n }\n const session =\n ctx?.resourceId && github ? await github.sourceControlStorage.sessions.getBySessionId(ctx.resourceId) : null;\n\n if (!session) {\n // No factory session, no workspace. Chat still works; workspace tools\n // are simply not registered. Host-cwd behavior is opt-in via a\n // LocalSandbox callback rooted wherever the deployer wants — the\n // resolver never hands out the server host's own filesystem.\n return undefined;\n }\n\n const user = getFactoryAuthUserFromContext(requestContext);\n const userId = getFactoryAuthUserId(user);\n // No identity at all is a server-side caller that forgot to seed one\n // (webhook, cron), not someone reaching for another user's session.\n if (!user?.organizationId || !userId) {\n throw new Error(`Factory session ${session.sessionId} was resolved without a caller identity`);\n }\n // Org-visible sessions open to any member of the owning organization;\n // only private sessions stay owner-only. Cross-org access never passes.\n if (user.organizationId !== session.orgId || (session.visibility === 'private' && userId !== session.userId)) {\n throw new Error(`Factory session ${session.sessionId} is not available to the current user`);\n }\n if (!sandboxConfig || !github) {\n throw new Error('GitHub and a sandbox callback are required to create a Factory session workspace');\n }\n const createSessionSandboxInstance = sandboxConfig;\n\n const storage = github.sourceControlStorage;\n const projectRepository = await storage.projectRepositories.get({\n orgId: session.orgId,\n id: session.projectRepositoryId,\n });\n if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);\n // The remaining reads only depend on the repository link — issue them in\n // parallel instead of paying four sequential storage round-trips.\n const [connection, repository] = await Promise.all([\n storage.connections.get({ orgId: session.orgId, id: projectRepository.connectionId }),\n storage.repositories.get({ orgId: session.orgId, id: projectRepository.repositoryId }),\n ]);\n if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);\n const installation = await storage.installations.get({ orgId: session.orgId, id: connection.installationId });\n if (!installation) throw new Error(`GitHub installation ${connection.installationId} was not found`);\n const repoFullName = repository.slug;\n\n // Construct (or fetch) the session's memoized sandbox instance.\n // Construction is cheap and side-effect-free by the callback contract —\n // the VM is provisioned on `start()`, which only the materialization\n // pipeline calls. The workdir is never persisted or trusted from storage\n // or client input (the stale-workdir incident class came from reusing\n // `session.sandboxWorkdir` written under a different provider): local\n // sandboxes derive it at construction, remote sandboxes clone into the\n // VM's own home so it resolves lazily at first start.\n // `runSetupOn` references `runSessionSetup`, defined below — it is only\n // invoked during start, long after this closure fully initializes.\n const runSetupOn = (target: unknown, workdir: string, gate: SessionSetupGate) =>\n runSessionSetup(requireExec(target as WorkspaceSandbox), workdir, gate);\n const guardedSetup = createSessionSetupHook(\n runSetupOn,\n session.id,\n repoFullName,\n projectRepository.setupCommand ?? undefined,\n );\n // Composed start hook: marker-guarded repo setup, then per-start\n // credential install. It runs inside the provider's start lifecycle on\n // EVERY start (create or reconnect) — providers own lazy start\n // (`ensureRunning()` on first command) and dead-VM self-healing (E2B\n // `retryOnDead`, Platform status reset on destroy), so a replacement VM\n // re-enters this hook and heals itself with credentials at least as\n // fresh as the start that installed them.\n const setupHook: SandboxStartHook = async args => {\n // A session retired before its first start must not set anything up.\n if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) {\n throw retiredError();\n }\n await guardedSetup(args);\n // Re-check after the (long) setup: a session retired mid-setup must not\n // register credentials for a workspace whose retirement teardown has\n // already run — the entry would leak forever. The VM itself is left to\n // the provider's idle timeout (accepted).\n if (workspaceRegistry.generation(session.sessionId) !== workspaceGeneration) {\n throw retiredError();\n }\n const target: SessionSandbox = requireExec(args.sandbox);\n // The `gh` CLI needs a PAT when the org configured one (installation\n // tokens 403 on integration-restricted endpoints); git clone/checkout\n // keep using the minted installation token. Resolved per start so the\n // installed credential never outlives rotation.\n const patKind = await resolveGithubPatKind('default');\n const ghCliToken =\n (await getGithubPat(() => github.integrationStorage, session.orgId, patKind)) ?? (await getRepositoryToken());\n target.setEnv?.(env => ({ ...env, GH_TOKEN: ghCliToken }));\n // Observability only — nothing reads these columns for decisions. The\n // workdir was resolved (and memoized on the entry) by the guarded setup.\n void storage.sessions\n .setSandbox({ id: session.id, sandboxId: target.id, sandboxWorkdir: sessionEntry.workdir ?? '' })\n .catch(() => {});\n const tokenRegistration: GithubTokenRegistration = {\n inject: freshToken => {\n if (!target.setEnv) {\n throw new Error('The active sandbox provider does not support runtime GitHub token refresh.');\n }\n target.setEnv(env => ({ ...env, GH_TOKEN: freshToken }));\n tokenRegistration.ghToken = freshToken;\n },\n patKind,\n ghToken: ghCliToken,\n generation: 0,\n tokenReplacementPending: false,\n };\n githubTokenInjectors.set(workspaceId, tokenRegistration);\n registerGithubTokenContext(tokenRegistration);\n // Project skill roots were reported empty by the unmaterialized-source\n // guard before the checkout existed; rescan now. Fire-and-forget.\n void constructedWorkspaces\n .get(workspaceId)\n ?.skills?.refresh()\n .catch(() => {});\n };\n const constructSessionEntry = () =>\n getSessionSandbox(session.id, repoFullName, () => {\n const sandbox = createSessionSandboxInstance({\n sessionId: session.id,\n repoFullName,\n // Stored nullable; the context speaks `undefined` for absent.\n setupCommand: projectRepository.setupCommand ?? undefined,\n // Deferred call — only dereferenced when a provider needs the repo\n // outside the VM (template build time).\n getRepositoryAccess: () =>\n github.versionControl.getRepositoryAccess({ orgId: session.orgId, repositoryId: repository.id }),\n });\n // Attached inside the construction closure, so exactly once per\n // instance — `constructSessionEntry` runs on every open and would\n // stack a wrapper per call. Factory's setup runs first: a hook the\n // callback installed itself expects a prepared workspace.\n sandbox.setOnStart(previous => async args => {\n await timedPhase(`workspace.onStart(${args.outcome})`, async () => {\n await setupHook(args);\n });\n await previous?.(args);\n });\n // Only a freshly constructed instance starts eagerly; the start itself\n // waits for this resolver to finish because the start hook reads\n // bindings declared further down.\n startEagerly = eagerSandboxStart;\n return sandbox;\n });\n let startEagerly = false;\n const fireEagerStart = () => {\n if (!startEagerly) return;\n startEagerly = false;\n Promise.resolve()\n .then(() => sessionEntry.sandbox.start?.())\n .catch(error => {\n console.warn(`[factory] Eager sandbox start for session ${session.id} failed:`, error);\n });\n };\n const sessionEntry = constructSessionEntry();\n const workdir = sessionEntry.workdir;\n const isLocalSandbox = sessionEntry.sandbox.provider === 'local';\n // The system prompt derives its working directory from `state.projectPath`\n // and falls back to the server's own process.cwd() when unset — which\n // points the agent at the host checkout (and lets it run `git checkout`\n // there instead of in its session workdir). Pin it to the session workdir\n // once known. A remote workdir resolves at the sandbox's first start, so\n // the pin self-heals on the next resolution after the VM has run.\n if (ctx && workdir && ctx.getState()?.projectPath !== workdir) {\n await ctx.setState({ projectPath: workdir, projectName: repoFullName });\n }\n\n const extensionId = effectiveSkillExtension ? `-${effectiveSkillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectRepository.id}-${session.id}${extensionId}`;\n const workspaceGeneration = workspaceRegistry.generation(session.sessionId);\n const configDir = DEFAULT_CONFIG_DIR;\n\n const getRepositoryToken = async (): Promise<string> => {\n const access = await github.versionControl.getRepositoryAccess({\n orgId: session.orgId,\n repositoryId: repository.id,\n });\n const token = access.authorization?.token;\n if (!token) throw new Error('Repository access did not include a bearer token for the Factory session');\n return token;\n };\n const resolveGithubPatKind = async (fallback: GithubPatKind): Promise<GithubPatKind> => {\n if (!workItems) return 'default';\n try {\n const address = getFactorySessionAddress(requestContext);\n const runBinding = address ? await workItems.findRunBindingBySession(address) : null;\n return runBinding?.role === 'review' && runBinding.status === 'active' && runBinding.orgId === session.orgId\n ? 'reviewer'\n : 'default';\n } catch {\n // Preserve the installed role when binding storage is temporarily unavailable.\n return fallback;\n }\n };\n const registerGithubTokenContext = (registered: GithubTokenRegistration): void => {\n const generation = registered.generation;\n registerGithubTokenInjector(requestContext, token => {\n if (githubTokenInjectors.get(workspaceId) !== registered || registered.generation !== generation) {\n throw new Error('GitHub token refresh no longer matches the active Factory workspace role.');\n }\n registered.inject(token);\n });\n registerGithubPatKind(requestContext, registered.patKind);\n };\n const reconcileGithubToken = async (): Promise<void> => {\n const previous = githubTokenReconciliations.get(workspaceId) ?? Promise.resolve();\n const reconciliation = previous\n .catch(() => {})\n .then(async () => {\n const registered = githubTokenInjectors.get(workspaceId);\n if (!registered) return;\n\n const previousPatKind = registered.patKind;\n const patKind = await resolveGithubPatKind(previousPatKind);\n if (githubTokenInjectors.get(workspaceId) !== registered) return;\n\n if (patKind !== previousPatKind) {\n registered.patKind = patKind;\n registered.generation += 1;\n }\n if (patKind === 'reviewer') registered.tokenReplacementPending = false;\n if (previousPatKind === 'reviewer' && patKind === 'default') {\n // Invalidate reviewer refresh contexts before replacement I/O so\n // they cannot restore reviewer credentials after a failed downgrade.\n registered.tokenReplacementPending = true;\n }\n\n let token = await getGithubPat(() => github.integrationStorage, session.orgId, patKind);\n if (!token && registered.tokenReplacementPending) token = await getRepositoryToken();\n if (githubTokenInjectors.get(workspaceId) !== registered) return;\n\n if (token && token !== registered.ghToken) {\n try {\n registered.inject(token);\n } catch (error) {\n if (registered.tokenReplacementPending) throw error;\n // Same-role rotations and reviewer upgrades remain best-effort.\n }\n }\n if (token && token === registered.ghToken) registered.tokenReplacementPending = false;\n registerGithubTokenContext(registered);\n });\n githubTokenReconciliations.set(workspaceId, reconciliation);\n try {\n await reconciliation;\n } finally {\n if (githubTokenReconciliations.get(workspaceId) === reconciliation) {\n githubTokenReconciliations.delete(workspaceId);\n }\n }\n };\n const reconcileRegisteredWorkspace = async (workspace: Workspace): Promise<Workspace> => {\n const registered = githubTokenInjectors.get(workspaceId);\n try {\n await reconcileGithubToken();\n } catch (error) {\n if (registered?.tokenReplacementPending && githubTokenInjectors.get(workspaceId) === registered) {\n // The role generation already invalidated reviewer refresh contexts.\n // Keep the pending registration so failed eviction cannot make a\n // still-live reviewer workspace look safe on the next reuse.\n let evicted = false;\n try {\n evicted = (await mastra?.removeWorkspace?.(workspaceId)) === true;\n } catch {\n // Preserve the credential-replacement error and retry on the next reuse.\n }\n try {\n await workspace.destroy();\n evicted = true;\n } catch {\n // The pending registration keeps the workspace quarantined if cleanup also fails.\n }\n if (evicted && githubTokenInjectors.get(workspaceId) === registered) {\n githubTokenInjectors.delete(workspaceId);\n constructedWorkspaces.delete(workspaceId);\n }\n }\n throw error;\n }\n if (registered && githubTokenInjectors.get(workspaceId) !== registered) {\n throw new Error('Factory workspace GitHub credential registration is no longer active.');\n }\n return workspace;\n };\n\n let existing: Workspace | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace | undefined;\n } catch {\n // Not registered yet.\n existing = undefined;\n }\n existing ??= constructedWorkspaces.get(workspaceId);\n if (existing) {\n existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);\n // A materialization kicked off by another caller may still be running.\n // Deliberately do NOT wait for it: a metadata-only resolution (thread\n // list, messages, activity) must not block on the clone/setup that lazy\n // materialization exists to avoid. Token reconciliation below no-ops\n // until the leader registers the injector, and the next reuse after\n // materialization completes reconciles against the live sandbox.\n return reconcileRegisteredWorkspace(existing);\n }\n\n const retiredError = () =>\n new Error(`Factory session ${session.sessionId} was retired during workspace materialization`);\n\n // The session's setup work: materialize the repo (disk-truth idempotent),\n // check out the session branch, run the configured setup command. Minted\n // tokens are fetched inside the run so a replacement VM healed mid-session\n // gets fresh credentials, not ones captured at workspace construction.\n const runSessionSetup = async (target: SessionSandbox, workdir: string, gate: SessionSetupGate): Promise<void> => {\n const token = await getRepositoryToken();\n // The configured setup command may shell out to `gh`/https fetches, so\n // GH_TOKEN must exist before setup runs — and it must be the same\n // gh-capable credential the session gets after start (installation\n // tokens 403 on integration-restricted endpoints when the org\n // configured a PAT).\n const setupPatKind = await resolveGithubPatKind('default');\n const setupGhToken = (await getGithubPat(() => github.integrationStorage, session.orgId, setupPatKind)) ?? token;\n target.setEnv?.(env => ({ ...env, GH_TOKEN: setupGhToken }));\n await materializeRepo({\n row: { id: session.id, sandboxWorkdir: workdir, materializedAt: session.materializedAt },\n repoInfo: { repoFullName: repoFullName, defaultBranch: repository.defaultBranch },\n sandbox: target,\n token,\n storage: storage.sessions,\n });\n await checkoutSessionBranch(target, workdir, {\n branch: session.branch,\n baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,\n token,\n repoFullName: repoFullName,\n pullRequestNumber: pullRequestNumberFromBranch(session.branch),\n });\n if (projectRepository.setupCommand && !gate.setupDone) {\n // A setup command that already failed this session is skipped rather\n // than failing every start: the first failure surfaced loudly in the\n // tool result that triggered it, and a permanently failing onStart\n // would wedge the session — the agent could never get a shell to fix\n // the problem. Clone and checkout above still ran, so the tree is\n // real; the agent (or an edited setup command) takes it from here.\n if (hasFailedSetupCommand(session.id, projectRepository.setupCommand)) {\n console.warn('[Mastra Factory] Skipping setup command that already failed this session', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n });\n return;\n }\n try {\n await timedPhase('workspace.setup', () => runSetupCommand(target, workdir, projectRepository.setupCommand!));\n await gate.markSetupDone();\n } catch (setupError) {\n if (projectRepository.teardownCommand) {\n try {\n await runTeardownCommand(target, workdir, projectRepository.teardownCommand, {\n timeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,\n });\n } catch (teardownError) {\n console.warn('[Mastra Factory] Worktree teardown after setup failure failed', {\n orgId: session.orgId,\n sessionId: session.sessionId,\n projectRepositoryId: session.projectRepositoryId,\n error: teardownError instanceof Error ? teardownError.message.slice(-2000) : String(teardownError),\n });\n }\n }\n if (setupError instanceof SetupCommandError) {\n // The command ran and exited non-zero — a config problem, not an\n // infra one. Remember it so the next start recovers, and tell the\n // agent what happens next. Infra failures (transport, clone)\n // rethrow untouched and retry in full.\n recordFailedSetupCommand(session.id, projectRepository.setupCommand);\n throw new SetupCommandError(\n `${setupError.message}. The sandbox stays usable: this setup command is skipped for the rest of the session — retry your command, then fix the setup command in the repository settings or run it manually.`,\n setupError.code,\n );\n }\n throw setupError;\n }\n }\n };\n // The session's real sandbox goes straight onto the Workspace. Providers\n // own lazy start (`ensureRunning()` inside the first command/process op)\n // and dead-VM self-healing, and the composed `onStart` hook runs the repo\n // setup + credential install inside that lifecycle. Metadata-only\n // resolutions (thread-list polling) construct but never start.\n const sessionSandbox: SessionSandbox = requireExec(sessionEntry.sandbox);\n\n const filesystem = new SandboxFilesystem({\n id: `sandbox-fs:${workspaceId}`,\n sandbox: sessionSandbox,\n // Lazy: a remote workdir is only knowable once a VM runs. The first\n // file operation resolves it (starting the VM — which materializes the\n // repo via the onStart hook — when needed) and memoizes it.\n workdir: () => resolveSessionWorkdir(session.id, sessionEntry.sandbox, repoFullName),\n });\n const projectSkillPaths = [path.join(configDir, 'skills'), '.claude/skills', '.agents/skills'];\n const guardedSkillFallback = new UnmaterializedAwareSkillSource(\n filesystem,\n () => sessionEntry.sandbox.status === 'running',\n );\n const skillPaths = [...(effectiveSkillExtension?.paths ?? []), ...projectSkillPaths];\n const workspace = new Workspace({\n id: workspaceId,\n name: 'Mastra Code Factory Session Workspace',\n filesystem,\n sandbox: sessionSandbox as unknown as ConstructorParameters<typeof Workspace>[0]['sandbox'],\n tools: MASTRACODE_WORKSPACE_TOOLS,\n skills: skillPaths,\n // Project skill roots live in the sandbox checkout; guard them so skill\n // discovery before materialization (e.g. kickoff skill resolution in the\n // start coordinator) never forces sandbox provisioning.\n skillSource:\n effectiveSkillExtension?.createSource(guardedSkillFallback, projectSkillPaths) ?? guardedSkillFallback,\n });\n // Register with the Mastra instance so sync HTTP handlers that resolve\n // the workspace via `mastra.getWorkspaceById(id)` (file tree, permissions\n // probe, MCP/tool routes) find it instead of throwing\n // `MASTRA_GET_WORKSPACE_BY_ID_NOT_FOUND`. `addWorkspace` is idempotent on\n // key collision, so concurrent first resolutions stay race-safe (start\n // itself is coalesced by the sandbox base class + the session memo).\n mastra?.addWorkspace(workspace, workspaceId, { source: 'mastra' });\n // Cache synchronously with construction: the `await` below is a suspension\n // point, and a concurrent resolution for the same session must observe this\n // workspace rather than build a second one.\n constructedWorkspaces.set(workspaceId, workspace);\n // Retirement is registered against the workspace itself rather than the\n // sandbox: construction is eager while the VM start is lazy, so a session\n // retired before its first tool call still has a workspace (and possibly a\n // token injector) that must be torn down.\n const registered = await workspaceRegistry.register(\n session.sessionId,\n workspaceId,\n workspaceGeneration,\n async () => {\n githubTokenInjectors.delete(workspaceId);\n constructedWorkspaces.delete(workspaceId);\n // Retirement drops the memoized session sandbox so a later re-open\n // constructs (and the provider resolves) fresh instead of reusing an\n // instance whose VM the retirement path may stop or destroy.\n evictSessionSandbox(session.id);\n await mastra?.removeWorkspace?.(workspaceId);\n },\n );\n if (!registered) {\n throw new Error(`Factory session ${session.sessionId} was retired during workspace materialization`);\n }\n\n fireEagerStart();\n return workspace;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: configure-factory-rules
|
|
3
|
-
description: Configure
|
|
3
|
+
description: Configure board-owned lifecycle, policy, phase-semantics, and tool-result rules, plus integration event rules
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Configure Factory Rules
|
|
@@ -9,10 +9,10 @@ Help the user change Factory policy in the typed deployment configuration. Facto
|
|
|
9
9
|
|
|
10
10
|
## Find the configuration
|
|
11
11
|
|
|
12
|
-
1. Search for `new MastraFactory`, its `boards` and `includeDefaultBoards` options, `defineBoard`, `
|
|
12
|
+
1. Search for `new MastraFactory`, its `boards` and `includeDefaultBoards` options, `defineBoard`, `configVersion`, and the installed `GithubIntegration`, `PlatformGithubIntegration`, `LinearIntegration`, or `PlatformLinearIntegration` constructors and their `rules` options.
|
|
13
13
|
2. Read the existing rule configuration and its tests before editing.
|
|
14
14
|
3. Import rule helpers and types from the same local Factory module used by the deployment.
|
|
15
|
-
4. For custom-board handlers, edit the installed `defineBoard()` definition. For tool rules,
|
|
15
|
+
4. For custom-board handlers or phase semantics (`kind`, `role`), edit the installed `defineBoard()` definition. For tool-result rules, add `tools: { <toolName>: { onResult } }` to the installed `defineBoard()` definition of the board whose seat produces the result. For GitHub or Linear rules, configure the installed integration constructor directly.
|
|
16
16
|
|
|
17
17
|
Do not guess a file path. Factory deployments can assemble `MastraFactory` from different entry points.
|
|
18
18
|
|
|
@@ -20,7 +20,7 @@ Do not guess a file path. Factory deployments can assemble `MastraFactory` from
|
|
|
20
20
|
|
|
21
21
|
Installed board definitions exclusively own lifecycle handlers: use `phases.<phase>.onEnter.<source>` and `phases.<phase>.onExit.<source>` in `defineBoard()`. Sources are `issue`, `pullRequest`, `linearIssue`, and `manual`. Work and Review install automatically with preferred defaults. Custom boards are installed through `boards`; `includeDefaultBoards: false` supports custom-only installations.
|
|
22
22
|
|
|
23
|
-
Remove former global `rules.work` and `rules.review` configuration. Built-in customization is deferred: do not invent a board override API, derive replacements, or use reserved IDs `work` and `review`.
|
|
23
|
+
Remove former global `rules.work` and `rules.review` configuration. Built-in customization is deferred: do not invent a board override API, derive replacements, or use reserved IDs `work` and `review`. There is no global rules tree; `new MastraFactory({ rules })` throws. Set `configVersion` on `MastraFactory` for the audit label. Tool-result rules resolve only from the item's installed board: Work declares `submit_plan`, Review declares none, and custom boards inherit nothing.
|
|
24
24
|
|
|
25
25
|
Work automatic intake requires both `linked_item_materialized` and `autoStartCandidate: true`. Do not remove these guards to reproduce the web deployment's former unconditional intake override. Noncandidate and manual arrivals stay unstarted merely from entering Intake; explicit issue triage and human-approval safeguards remain. Linear Intake and Review retain their existing defaults and guards.
|
|
26
26
|
|
|
@@ -58,15 +58,23 @@ The policy receives a deeply readonly snapshot with ISO-string dates. Return `un
|
|
|
58
58
|
|
|
59
59
|
Policies must be side-effect-free and share the lifecycle timeout budget. Initial entry, reentry, and same-stage requests evaluate policy; completed replay does not. Concurrent attempts may evaluate more than once, and timing out does not cancel work started by a callback. Do not access storage or integrations from a policy.
|
|
60
60
|
|
|
61
|
-
Policy allowance cannot bypass topology, board ownership, ingress authorization, external-author safety, revision checks, replay, or decision validation.
|
|
61
|
+
Policy allowance cannot bypass topology, board ownership, ingress authorization, external-author safety, revision checks, replay, or decision validation. Phase meaning is declared on the phase (`kind`), not in the policy. Do not invent built-in replacement APIs.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## Configure board phase semantics
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Every phase in `defineBoard()` requires `kind: 'resting' | 'working' | 'terminal'`; working phases also require `role`. Read `src/boards/define-board.ts` for validation and the derived helpers (`phaseKind`, `isWorking`, `isTerminal`, `roleForPhase`, `phaseForRole`) and `src/boards/semantics.ts` for how runtime resolves an item's board and phase. Work and Review declarations live in `src/boards/work.ts` and `src/boards/review.ts`.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Runtime reads the installed board's declarations for consent arming, the external-author guard, kickoff seating, run-start lanes, terminal cleanup, sweeps, and supervisor findings; nothing name-matches phases, and custom boards inherit nothing from Work. `initialPhase` must be resting. Unknown board or phase fails closed: consent is requested, nothing is cleaned up, no seat is started or revoked.
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
To make a custom phase terminal or seat an agent in it, change its `kind`/`role` in the definition. Do not add phase-name checks to the transition service, dispatcher, sweeps, or supervisor. Decision and tool-input validation still accept only built-in board IDs and phase names, so custom-board handlers cannot emit `transition` decisions into custom phases yet.
|
|
70
|
+
|
|
71
|
+
## Change a built-in handler
|
|
72
|
+
|
|
73
|
+
Integration overrides (`rules[event]` on the GitHub/Linear constructor) replace one event handler; they do not compose with the built-in handler, and siblings remain unchanged. There is no override mechanism for board-owned handlers, including Work's `submit_plan` tool-result rule: change the installed definition itself (`src/boards/work.ts`, handler in `src/boards/work-tool-rules.ts`) or install a custom board that declares its own `tools`.
|
|
74
|
+
|
|
75
|
+
Before changing a built-in handler:
|
|
76
|
+
|
|
77
|
+
1. Find and read it: GitHub handlers live in `src/integrations/github/default-rules.ts`, Linear handlers in `src/integrations/linear/default-rules.ts`, Work's tool-result handler in `src/boards/work-tool-rules.ts`, and Work and Review lifecycle/policy defaults in `src/boards/work.ts` and `src/boards/review.ts`. Custom handlers live in their installed definitions.
|
|
70
78
|
2. Decide whether the replacement must preserve part of that behavior explicitly.
|
|
71
79
|
3. Use only fields exposed by the typed context. Do not reach into Factory storage or raw webhook payloads.
|
|
72
80
|
4. Return `undefined` to allow the ingress with no decision, or return a typed rejection or bounded structured decision.
|
|
@@ -74,7 +82,7 @@ Before replacing a built-in leaf:
|
|
|
74
82
|
|
|
75
83
|
## Version changes
|
|
76
84
|
|
|
77
|
-
Set an explicit, deployment-owned `
|
|
85
|
+
Set an explicit, deployment-owned `configVersion` on `MastraFactory`. Change it whenever rule behavior changes. It labels persisted evaluations and audit records (stored in the `rule_set_version` column); it must not be used as event identity or added to ingress deduplication keys.
|
|
78
86
|
|
|
79
87
|
## Safety rules
|
|
80
88
|
|