@mastra/factory 0.13.0-alpha.6 → 0.13.0-alpha.7
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 +35 -0
- package/dist/boards/index.d.ts +4 -3
- 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/registry.d.ts +16 -0
- package/dist/boards/registry.d.ts.map +1 -0
- package/dist/boards/registry.js +27 -0
- package/dist/boards/registry.js.map +1 -0
- package/dist/boards/review.js +1 -1
- package/dist/boards/work.js +2 -2
- package/dist/factory.d.ts +6 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +10 -0
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/integrations/github/default-rules.d.ts +194 -0
- package/dist/integrations/github/default-rules.d.ts.map +1 -0
- package/dist/integrations/github/default-rules.js +230 -0
- package/dist/integrations/github/default-rules.js.map +1 -0
- package/dist/integrations/github/integration.d.ts +4 -0
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +6 -0
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +3 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +1 -2
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/linear/rules.js +1 -1
- package/dist/integrations/platform/github/integration.d.ts +4 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +6 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +3 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +19 -1
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/surface.d.ts +3 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +2 -1
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts +3 -0
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +48 -15
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/defaults.d.ts.map +1 -1
- package/dist/rules/defaults.js +1 -229
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/index.d.ts +2 -2
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -2
- package/dist/rules/processor.d.ts +2 -0
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +9 -7
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +4 -4
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +4 -6
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/transition-service.d.ts +2 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +16 -10
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +3 -8
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +1 -9
- package/dist/rules/validation.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +11 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +22 -2
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +12 -4
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-items.js","names":["#resolveTenant","#resolveProject","#auditWorkItemPatch","#proposalRoute"],"sources":["../../src/routes/work-items.ts"],"sourcesContent":["/**\n * Mastra `apiRoutes` for Factory work items (the kanban board).\n *\n * Registered alongside the other `/web/*` routes behind the host auth gate.\n * The board is org-wide: every route re-resolves the caller's `(orgId, userId)`\n * tenant and scopes reads/writes by `orgId`, so any org member sees and moves\n * the same cards while `created_by` / stage history record who acted.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n FactoryStartCoordinator,\n FactoryStartPreparedResult,\n FactoryStartRequest,\n} from '../rules/start-coordinator.js';\nimport { FactoryStartTransitionError } from '../rules/start-coordinator.js';\nimport { roleForStage } from '../rules/transition-service.js';\nimport type { FactoryTransitionRequest, FactoryTransitionService } from '../rules/transition-service.js';\nimport type { FactoryRuleBoard, WorkItemSource } from '../rules/types.js';\nimport { FACTORY_RULE_BOARDS, isFactoryRuleStage } from '../rules/types.js';\nimport type { LiveSessions } from '../session/live-sessions.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';\nimport { thresholdsOrDefault } from '../storage/domains/queue-health/base.js';\nimport type {\n CreateWorkItemInput,\n ExternalWorkItemSource,\n FactoryDeferredDecisionRecord,\n FactoryDispatchStatus,\n UpdateWorkItemInput,\n WorkItemPriorState,\n WorkItemRow,\n WorkItemSessionInput,\n WorkItemStage,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport {\n FACTORY_PULL_REQUEST_RECONCILIATION_KEY,\n FACTORY_RULE_MATERIALIZATION_KEY,\n WorkItemRelationError,\n} from '../storage/domains/work-items/base.js';\nimport { computeFactoryMetrics, parseMetricsRange } from '../storage/domains/work-items/metrics.js';\nimport { buildAttentionRoutes, factoryDecisionType } from './attention.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\nimport { buildSupervisorRoutes } from './supervisor.js';\n\nexport interface WorkItemRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Factory projects domain — validates the `:id` project belongs to the caller's org. */\n projects: FactoryProjectsStorage;\n /** Work-items domain backing the kanban board. */\n workItems: WorkItemsStorage;\n /** Comments domain — backs the mention attention provider. */\n comments: WorkItemCommentsStorage;\n /** Per-project queue-health threshold config. */\n queueHealth: QueueHealthStorage;\n /** Governed stage-transition service. Stage moves 503 when absent. */\n transitionService?: Pick<FactoryTransitionService, 'transition' | 'ruleSetVersion'>;\n /** Coordinator that binds a Factory run before dispatching its kickoff. */\n startCoordinator?: Pick<FactoryStartCoordinator, 'prepare'>;\n /** Materialized sessions, read to report which of the listed cards are being worked. */\n liveSessions: Pick<LiveSessions, 'isRunning'>;\n}\n\n/** The card as clients see it, without the dispatcher's internal bookkeeping. */\nfunction toWireWorkItem(item: WorkItemRow): WorkItemRow {\n if (\n !item.metadata ||\n (!(FACTORY_RULE_MATERIALIZATION_KEY in item.metadata) &&\n !(FACTORY_PULL_REQUEST_RECONCILIATION_KEY in item.metadata))\n ) {\n return item;\n }\n return { ...item, metadata: publicWorkItemMetadata(item.metadata) ?? {} };\n}\n\n/** Session ids of the listed cards whose agent run is in flight. */\nfunction runningSessionIds(items: WorkItemRow[], liveSessions: Pick<LiveSessions, 'isRunning'>): string[] {\n const running = new Set<string>();\n for (const item of items) {\n for (const { sessionId } of Object.values(item.sessions)) {\n if (liveSessions.isRunning(sessionId)) running.add(sessionId);\n }\n }\n return [...running];\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nconst MAX_STAGES = 16;\nconst MAX_STAGE_LENGTH = 64;\nconst MAX_METADATA_BYTES = 16 * 1024;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validStages(value: unknown): value is WorkItemStage[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.length <= MAX_STAGES &&\n value.every(\n stage => typeof stage === 'string' && stage.length <= MAX_STAGE_LENGTH && /^[a-z0-9][a-z0-9_-]*$/i.test(stage),\n ) &&\n new Set(value).size === value.length\n );\n}\n\nfunction validMetadata(value: unknown): value is Record<string, unknown> | null {\n if (value === null) return true;\n if (!isRecord(value)) return false;\n try {\n return JSON.stringify(value).length <= MAX_METADATA_BYTES;\n } catch {\n return false;\n }\n}\n\nfunction publicWorkItemMetadata(value: Record<string, unknown> | null): Record<string, unknown> | null {\n if (value === null) return null;\n const {\n [FACTORY_RULE_MATERIALIZATION_KEY]: _materialization,\n [FACTORY_PULL_REQUEST_RECONCILIATION_KEY]: _reconciliation,\n ...metadata\n } = value;\n return metadata;\n}\n\nfunction parseExternalSource(value: unknown): ExternalWorkItemSource | null | undefined {\n if (value === undefined || value === null) return value;\n if (!isRecord(value)) return undefined;\n const { integrationId, type, externalId, url } = value;\n if (typeof integrationId !== 'string' || integrationId.length === 0 || integrationId.length > 128) return undefined;\n if (typeof type !== 'string' || type.length === 0 || type.length > 128) return undefined;\n if (typeof externalId !== 'string' || externalId.length === 0 || externalId.length > 512) return undefined;\n if (url !== undefined && (typeof url !== 'string' || url.length > 2048)) return undefined;\n return { integrationId, type, externalId, ...(url !== undefined ? { url } : {}) };\n}\n\nfunction parseParentWorkItemId(value: unknown): string | null | undefined {\n if (value === null) return null;\n if (typeof value !== 'string' || !UUID_RE.test(value)) return undefined;\n return value;\n}\n\nfunction parseSessions(value: unknown): Record<string, WorkItemSessionInput> | undefined {\n if (!isRecord(value)) return undefined;\n const out: Record<string, WorkItemSessionInput> = {};\n for (const [role, session] of Object.entries(value)) {\n if (!role || role.length > 64 || !isRecord(session)) return undefined;\n const { sessionId, branch, threadId } = session;\n if (typeof sessionId !== 'string' || sessionId.length === 0 || sessionId.length > 512) return undefined;\n if (typeof branch !== 'string' || branch.length === 0 || branch.length > 512) return undefined;\n if (typeof threadId !== 'string' || threadId.length === 0 || threadId.length > 512) return undefined;\n out[role] = { sessionId, branch, threadId };\n }\n return out;\n}\n\n/** Validate an untrusted create body. Unknown keys are dropped. */\nexport function parseCreateWorkItem(body: unknown): CreateWorkItemInput | null {\n if (!isRecord(body)) return null;\n const { externalSource, title, stages, sessions, metadata } = body;\n if (typeof title !== 'string' || title.trim().length === 0 || title.length > 500) return null;\n\n const hasParentWorkItemId = 'parentWorkItemId' in body;\n const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : undefined;\n if (hasParentWorkItemId && parentWorkItemId === undefined) return null;\n const parsedSource = parseExternalSource(externalSource);\n if (externalSource !== undefined && parsedSource === undefined) return null;\n if (stages !== undefined && !validStages(stages)) return null;\n const parsedSessions = sessions === undefined ? undefined : parseSessions(sessions);\n if (sessions !== undefined && parsedSessions === undefined) return null;\n let parsedMetadata: Record<string, unknown> | null | undefined;\n if (metadata !== undefined) {\n if (!validMetadata(metadata)) return null;\n parsedMetadata = publicWorkItemMetadata(metadata);\n }\n\n return {\n title: title.trim(),\n ...(parsedSource !== undefined ? { externalSource: parsedSource } : {}),\n ...(hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {}),\n ...(stages !== undefined ? { stages } : {}),\n ...(parsedSessions !== undefined ? { sessions: parsedSessions } : {}),\n ...(parsedMetadata !== undefined ? { metadata: parsedMetadata } : {}),\n };\n}\n\n/** Validate an untrusted patch body. Unknown keys are dropped. */\nexport function parseUpdateWorkItem(body: unknown): UpdateWorkItemInput | null {\n if (!isRecord(body)) return null;\n const { title, stages, sessions, metadata, plansPreapproved } = body;\n const hasParentWorkItemId = 'parentWorkItemId' in body;\n if (\n title === undefined &&\n stages === undefined &&\n sessions === undefined &&\n metadata === undefined &&\n plansPreapproved === undefined &&\n !hasParentWorkItemId\n )\n return null;\n if (plansPreapproved !== undefined && plansPreapproved !== true) return null;\n const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : undefined;\n if (hasParentWorkItemId && parentWorkItemId === undefined) return null;\n if (title !== undefined && (typeof title !== 'string' || title.trim().length === 0 || title.length > 500))\n return null;\n if (stages !== undefined && !validStages(stages)) return null;\n const parsedSessions = sessions === undefined ? undefined : parseSessions(sessions);\n if (sessions !== undefined && parsedSessions === undefined) return null;\n let parsedMetadata: Record<string, unknown> | null | undefined;\n if (metadata !== undefined) {\n if (!validMetadata(metadata)) return null;\n parsedMetadata = publicWorkItemMetadata(metadata);\n }\n\n return {\n ...(hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {}),\n ...(title !== undefined ? { title: title.trim() } : {}),\n ...(stages !== undefined ? { stages } : {}),\n ...(parsedSessions !== undefined ? { sessions: parsedSessions } : {}),\n ...(parsedMetadata !== undefined ? { metadata: parsedMetadata } : {}),\n ...(plansPreapproved === true ? { plansPreapproved: true } : {}),\n };\n}\n\nasync function readJson(c: Context): Promise<unknown | undefined> {\n try {\n return await c.req.json();\n } catch {\n return undefined;\n }\n}\n\n/** Fields a PATCH touched, for the bounded `updated` event summary. */\nfunction patchedFields(patch: Record<string, unknown>): string[] {\n return Object.keys(patch).filter(key => patch[key] !== undefined);\n}\n\nfunction boundedText(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const normalized = value.trim();\n return normalized.length > 0 && normalized.length <= max ? normalized : undefined;\n}\n\nfunction parseTransitionBody(\n body: unknown,\n): Omit<FactoryTransitionRequest, 'orgId' | 'factoryProjectId' | 'workItemId' | 'actor'> | null {\n if (!isRecord(body)) return null;\n const board = FACTORY_RULE_BOARDS.includes(body.board as FactoryRuleBoard)\n ? (body.board as FactoryRuleBoard)\n : undefined;\n const stage = isFactoryRuleStage(body.stage) ? body.stage : undefined;\n const requestId = boundedText(body.requestId, 256);\n const cause = boundedText(body.cause, 256);\n if (\n !board ||\n !stage ||\n !requestId ||\n !UUID_RE.test(requestId) ||\n !cause ||\n !Number.isInteger(body.expectedRevision) ||\n Number(body.expectedRevision) < 1\n ) {\n return null;\n }\n return {\n board,\n stage,\n expectedRevision: Number(body.expectedRevision),\n ingress: { type: 'human', identity: requestId },\n cause,\n ...(body.reenter === true ? { reenter: true } : {}),\n };\n}\n\nfunction parseStartBody(\n body: unknown,\n tenant: { orgId: string; userId: string },\n factoryProjectId: string,\n): FactoryStartRequest | null {\n if (!isRecord(body) || !isRecord(body.workItem)) return null;\n const input = parseCreateWorkItem(body.workItem.input);\n const sessionId = boundedText(body.sessionId, 256);\n const threadTitle = boundedText(body.threadTitle, 512);\n const kickoffKey = boundedText(body.kickoffKey, 256);\n const role = boundedText(body.workItem.role, 32);\n const id = boundedText(body.workItem.id, 64);\n if (\n !input ||\n !sessionId ||\n !UUID_RE.test(sessionId) ||\n !threadTitle ||\n !kickoffKey ||\n !UUID_RE.test(kickoffKey) ||\n !id ||\n !UUID_RE.test(id) ||\n !role\n ) {\n return null;\n }\n return { ...tenant, factoryProjectId, sessionId, threadTitle, kickoffKey, workItem: { id, role, input } };\n}\n\nconst DECISION_STATUSES = new Set<FactoryDispatchStatus>([\n 'pending',\n 'proposed',\n 'dismissed',\n 'superseded',\n 'leased',\n 'retry',\n 'succeeded',\n 'failed',\n]);\nconst DEFAULT_DECISION_PAGE_SIZE = 25;\nconst MAX_DECISION_PAGE_SIZE = 50;\n\nfunction parseDecisionStatuses(raw: string | undefined): FactoryDispatchStatus[] | undefined {\n if (!raw) return undefined;\n const statuses = [...new Set(raw.split(',').map(status => status.trim()))].filter(\n (status): status is FactoryDispatchStatus => DECISION_STATUSES.has(status as FactoryDispatchStatus),\n );\n return statuses.length > 0 ? statuses : undefined;\n}\n\nfunction parseDecisionLimit(raw: string | undefined): number {\n const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_DECISION_PAGE_SIZE;\n if (!Number.isFinite(parsed)) return DEFAULT_DECISION_PAGE_SIZE;\n return Math.max(1, Math.min(MAX_DECISION_PAGE_SIZE, parsed));\n}\n\nfunction encodeDecisionCursor(decision: FactoryDeferredDecisionRecord): string {\n return Buffer.from(JSON.stringify([decision.createdAt.toISOString(), decision.id]), 'utf8').toString('base64url');\n}\n\nfunction parseDecisionCursor(raw: string | undefined): { createdAt: Date; id: string } | undefined {\n if (!raw) return undefined;\n try {\n const decoded = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8')) as unknown;\n if (\n !Array.isArray(decoded) ||\n decoded.length !== 2 ||\n typeof decoded[0] !== 'string' ||\n typeof decoded[1] !== 'string'\n ) {\n return undefined;\n }\n const createdAt = new Date(decoded[0]);\n if (Number.isNaN(createdAt.getTime()) || !UUID_RE.test(decoded[1])) return undefined;\n return { createdAt, id: decoded[1] };\n } catch {\n return undefined;\n }\n}\n\n/** A proposed transition names the seat its lane addresses, so the card can label what approving starts. */\nfunction summaryRole(decision: Record<string, unknown>): string | null {\n if (typeof decision.role === 'string') return decision.role.slice(0, 32);\n if (decision.type !== 'transition') return null;\n const board = decision.board;\n const stage = decision.stage;\n if ((board !== 'work' && board !== 'review') || !isFactoryRuleStage(stage)) return null;\n return roleForStage(board, stage);\n}\n\n/** A linked-card decision names where the card is synced from, so the UI can say \"GitHub\" rather than \"a linked card\". */\nfunction summarySource(decision: Record<string, unknown>): WorkItemSource | null {\n if (decision.type !== 'upsertLinkedWorkItem') return null;\n const source = decision.source;\n return source === 'github-issue' || source === 'github-pr' || source === 'linear-issue' || source === 'manual'\n ? source\n : null;\n}\n\nfunction decisionSummary(decision: FactoryDeferredDecisionRecord) {\n return {\n id: decision.id,\n evaluationId: decision.evaluationId,\n workItemId: decision.workItemId,\n type: factoryDecisionType(decision),\n role: summaryRole(decision.decision),\n source: summarySource(decision.decision),\n status: decision.status,\n attempts: decision.attempts,\n failureOccurrence: decision.failureOccurrence,\n failureCode: decision.failureCode,\n canRetry: factoryDispatchFailureMetadata(decision.failureCode).canRetry,\n lastError: decision.lastError?.slice(0, 512) ?? null,\n createdAt: decision.createdAt.toISOString(),\n updatedAt: decision.updatedAt.toISOString(),\n completedAt: decision.completedAt?.toISOString() ?? null,\n };\n}\n\nexport class WorkItemRoutes extends Route<WorkItemRoutesDeps> {\n /** Resolve the `(orgId, userId)` tenant or a ready-to-return error response. */\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'The Factory board requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n /**\n * Resolve the tenant AND the org-owned project from the `:id` param. Work\n * items hang off a project, so listing/creating requires the project to\n * exist in the caller's org.\n */\n async #resolveProject(\n c: Context,\n ): Promise<\n { orgId: string; userId: string; factoryProjectId: string; defaultModelId: string | null } | { response: Response }\n > {\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n const { projects } = this.deps;\n await projects.ensureReady();\n const project = await projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n return { ...tenant, factoryProjectId: projectId, defaultModelId: project.defaultModelId };\n }\n\n /**\n * Emit the audit events a successful work-item PATCH implies: always\n * `updated`, plus `stage_moved` when the stages actually changed and one\n * `run.started` per session role the patch introduced.\n */\n async #auditWorkItemPatch({\n context,\n item,\n previous,\n patch,\n }: {\n context: Context;\n item: WorkItemRow;\n previous: WorkItemPriorState;\n patch: Record<string, unknown>;\n }): Promise<void> {\n const { audit } = this.deps;\n const target = { type: 'work_item', id: item.id, name: item.title };\n await audit.emit({\n context,\n input: {\n action: 'factory.work_item.updated',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: { fields: patchedFields(patch) },\n },\n });\n\n const stagesChanged =\n patch.stages !== undefined &&\n (previous.stages.length !== item.stages.length || previous.stages.some((s, i) => s !== item.stages[i]));\n if (stagesChanged) {\n await audit.emit({\n context,\n input: {\n action: 'factory.work_item.stage_moved',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: { from: previous.stages, to: item.stages },\n },\n });\n }\n\n const newRoles = Object.keys(item.sessions).filter(role => !previous.sessionRoles.includes(role));\n for (const role of newRoles) {\n const session = item.sessions[role];\n await audit.emit({\n context,\n input: {\n action: 'factory.run.started',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: {\n role,\n branch: session?.branch,\n threadId: session?.threadId,\n sessionId: session?.sessionId,\n },\n },\n });\n }\n }\n\n /** Releasing a parked run and dropping it: same request, opposite outcomes, both audited as consent. */\n #proposalRoute({\n verb,\n settle,\n }: {\n verb: 'approve' | 'dismiss';\n settle: (\n orgId: string,\n factoryProjectId: string,\n decisionId: string,\n now: Date,\n userId: string,\n ) => Promise<FactoryDeferredDecisionRecord | null>;\n }): ApiRoute {\n const { audit, workItems } = this.deps;\n return registerApiRoute(`/web/factory/projects/:id/decisions/:decisionId/${verb}`, {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n if (!decisionId || !UUID_RE.test(decisionId)) return c.json({ error: 'invalid_decision_id' }, 422);\n await workItems.ensureReady();\n const now = new Date();\n const decision = await settle(resolved.orgId, resolved.factoryProjectId, decisionId, now, resolved.userId);\n if (!decision) return c.json({ error: 'decision_not_proposed' }, 409);\n // Releasing a proposal is a person taking the item on. Approval arms the\n // item's autonomy inside the same storage transaction (see\n // approveDeferredDecision), so follow-up runs no longer wait for approval.\n await audit.emit({\n context,\n input: {\n action: verb === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n factoryProjectId: resolved.factoryProjectId,\n targets: decision.workItemId\n ? [{ type: 'work_item', id: decision.workItemId }]\n : [{ type: 'rule_decision', id: decision.id }],\n metadata: { decisionId: decision.id, effect: factoryDecisionType(decision) },\n },\n });\n return c.json({ decision: decisionSummary(decision) });\n },\n });\n }\n\n /** Build the Factory work-item routes as Mastra `apiRoutes`. */\n routes(): ApiRoute[] {\n const { audit, workItems, queueHealth, transitionService, startCoordinator, liveSessions } = this.deps;\n return [\n // ── List the org's work items for a project, and which are being worked ─\n registerApiRoute('/web/factory/projects/:id/work-items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n await workItems.ensureReady();\n const items = await workItems.list({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n });\n return c.json({\n workItems: items.map(toWireWorkItem),\n runningSessionIds: runningSessionIds(items, liveSessions),\n });\n },\n }),\n\n // ── Flow metrics aggregated over the project's work items ───────────────\n registerApiRoute('/web/factory/projects/:id/metrics', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n const { windowStart, windowEnd } = parseMetricsRange(\n loose(c).req.query('from'),\n loose(c).req.query('to'),\n new Date(),\n );\n await workItems.ensureReady();\n const items = await workItems.list({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n });\n return c.json({ metrics: computeFactoryMetrics(items, { windowStart, windowEnd }) });\n },\n }),\n\n // ── Per-project queue-health age-threshold config (seconds) ─────────────\n registerApiRoute('/web/factory/projects/:id/health/thresholds', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n await queueHealth.ensureReady();\n const stored = await queueHealth.getConfig(resolved.orgId, resolved.factoryProjectId);\n // Validate at the read choke point: `getConfig` round-trips a stored\n // JSONB row, and only `saveConfig` validates on write — a corrupted or\n // hand-edited row (empty / non-ascending) would otherwise flow to the\n // chart and invert bucket colors. Fall back to the default on invalid.\n return c.json({ thresholds: thresholdsOrDefault(stored) });\n },\n }),\n\n // ── Bounded durable rule-decision status ────────────────────────────────\n registerApiRoute('/web/factory/projects/:id/decisions', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n\n const cursorRaw = context.req.query('before');\n const before = parseDecisionCursor(cursorRaw);\n if (cursorRaw && !before) return c.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n const page = await workItems.listDeferredDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: parseDecisionStatuses(context.req.query('statuses')),\n before,\n limit: parseDecisionLimit(context.req.query('limit')),\n });\n const last = page.decisions.at(-1);\n return c.json({\n decisions: page.decisions.map(decisionSummary),\n ...(page.hasMore && last ? { nextCursor: encodeDecisionCursor(last) } : {}),\n });\n },\n }),\n\n ...buildAttentionRoutes({\n workItems,\n comments: this.deps.comments,\n resolveProject: context => this.#resolveProject(loose(context)),\n }),\n\n ...buildSupervisorRoutes({\n workItems,\n resolveProject: context => this.#resolveProject(loose(context)),\n }),\n\n this.#proposalRoute({ verb: 'approve', settle: workItems.approveDeferredDecision.bind(workItems) }),\n this.#proposalRoute({ verb: 'dismiss', settle: workItems.dismissDeferredDecision.bind(workItems) }),\n\n registerApiRoute('/web/factory/projects/:id/decisions/:decisionId/retry', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n if (!decisionId || !UUID_RE.test(decisionId)) return c.json({ error: 'invalid_decision_id' }, 422);\n await workItems.ensureReady();\n const current = await workItems.getDeferredDecision(resolved.orgId, resolved.factoryProjectId, decisionId);\n if (\n !current ||\n current.status !== 'failed' ||\n !factoryDispatchFailureMetadata(current.failureCode).canRetry\n ) {\n return c.json({ error: 'decision_not_retryable' }, 409);\n }\n const decision = await workItems.retryDeferredDecision(\n resolved.orgId,\n resolved.factoryProjectId,\n decisionId,\n new Date(),\n );\n if (!decision) return c.json({ error: 'decision_not_retryable' }, 409);\n return c.json({ decision: decisionSummary(decision) });\n },\n }),\n\n // ── Create (upsert on sourceKey) a work item ─────────────────────────────\n registerApiRoute('/web/factory/projects/:id/work-items', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n\n const body = await readJson(loose(c));\n if (body === undefined) return c.json({ error: 'Invalid JSON body' }, 400);\n const input = parseCreateWorkItem(body);\n if (!input) return c.json({ error: 'invalid_work_item' }, 400);\n if ((input.stages ?? ['intake']).length !== 1 || (input.stages ?? ['intake'])[0] !== 'intake') {\n return c.json(\n { error: 'governed_transition_required', message: 'New work items must enter through Factory intake.' },\n 409,\n );\n }\n\n await workItems.ensureReady();\n try {\n const result = await workItems.upsert({\n orgId: resolved.orgId,\n userId: resolved.userId,\n factoryProjectId: resolved.factoryProjectId,\n input,\n reuseMode: 'non-stage',\n });\n let item = result.item;\n if (result.created) {\n if (!transitionService) {\n await workItems.delete({ orgId: resolved.orgId, id: item.id });\n return c.json({ error: 'factory_transitions_unavailable' }, 503);\n }\n const entered = await transitionService.transition({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n workItemId: item.id,\n board: item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage: 'intake',\n expectedRevision: item.revision,\n actor: { type: 'human', id: resolved.userId },\n ingress: { type: 'human', identity: `work-item:${item.id}:initial-entry` },\n cause: 'work_item_created',\n initialEntry: true,\n });\n if (entered.status === 'rejected') {\n await workItems.delete({ orgId: resolved.orgId, id: item.id });\n return c.json({ status: 'rejected', code: entered.code, reason: entered.reason }, 422);\n }\n item = (await workItems.getForProject(resolved.orgId, resolved.factoryProjectId, item.id)) ?? item;\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.work_item.created',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: item.id, name: item.title }],\n metadata: { externalSource: item.externalSource, stages: item.stages },\n },\n });\n } else {\n // Source-key reuse: the POST updated an existing card, so audit it\n // as an update (plus stage/run events) instead of a false creation.\n const { stages: _stages, sessions: _sessions, ...boundedPatch } = input;\n await this.#auditWorkItemPatch({\n context: loose(c),\n item,\n previous: result.previous,\n patch: boundedPatch as unknown as Record<string, unknown>,\n });\n }\n return c.json({ workItem: toWireWorkItem(item) });\n } catch (error) {\n if (error instanceof WorkItemRelationError) {\n return c.json({ error: error.code, message: error.message }, 400);\n }\n throw error;\n }\n },\n }),\n\n // ── Authoritative stage transition ──────────────────────────────────────\n registerApiRoute('/web/factory/projects/:id/work-items/:workItemId/transition', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n const workItemId = loose(c).req.param('workItemId');\n if (!workItemId || !UUID_RE.test(workItemId)) return c.json({ error: 'Work item not found' }, 404);\n const parsed = parseTransitionBody(await readJson(loose(c)));\n if (!parsed) return c.json({ error: 'invalid_transition_request' }, 400);\n if (!transitionService) {\n return c.json({ error: 'factory_transition_unavailable' }, 503);\n }\n await workItems.ensureReady();\n const result = await transitionService.transition({\n ...parsed,\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n workItemId,\n actor: { type: 'human', id: resolved.userId },\n ingress: {\n ...parsed.ingress,\n identity: `human:${resolved.userId}:${parsed.ingress.identity}`,\n },\n });\n await audit.emit({\n context: loose(c),\n input: {\n action:\n result.status === 'accepted'\n ? 'factory.work_item.stage_moved'\n : 'factory.work_item.transition_rejected',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: workItemId }],\n metadata: {\n transitionId: result.transitionId,\n ingressType: parsed.ingress.type,\n ruleSetVersion: transitionService.ruleSetVersion,\n ...(result.status === 'accepted'\n ? { to: result.stage, revision: result.revision }\n : { code: result.code, reason: result.reason }),\n },\n },\n });\n if (result.status === 'accepted') return c.json({ result });\n return c.json({ result }, result.code === 'stale' ? 409 : 422);\n },\n }),\n\n // ── Bind a Factory run before dispatching its kickoff ────────────────────\n registerApiRoute('/web/factory/projects/:id/runs/start', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n if (!startCoordinator) {\n return c.json({ error: 'factory_start_unavailable' }, 503);\n }\n const input = parseStartBody(await readJson(loose(c)), resolved, resolved.factoryProjectId);\n if (!input) return c.json({ error: 'invalid_factory_start' }, 400);\n input.requestContext = loose(c).get('requestContext');\n input.defaultModelId = resolved.defaultModelId ?? undefined;\n await workItems.ensureReady();\n let prepared: FactoryStartPreparedResult;\n try {\n prepared = await startCoordinator.prepare(input);\n } catch (error) {\n if (error instanceof FactoryStartTransitionError) {\n return c.json({ result: error.result }, error.result.code === 'stale' ? 409 : 422);\n }\n throw error;\n }\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.run.started',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: prepared.workItemId }],\n metadata: {\n role: input.workItem.role,\n branch: prepared.branch,\n threadId: prepared.threadId,\n sessionId: prepared.sessionId,\n bindingId: prepared.bindingId,\n },\n },\n });\n return c.json({ prepared }, 202);\n },\n }),\n\n // ── Patch non-stage metadata / sessions / title ──────────────────────────\n registerApiRoute('/web/factory/work-items/:id', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n const id = loose(c).req.param('id');\n if (!id || !UUID_RE.test(id)) return c.json({ error: 'Work item not found' }, 404);\n\n const body = await readJson(loose(c));\n if (body === undefined) return c.json({ error: 'Invalid JSON body' }, 400);\n const patch = parseUpdateWorkItem(body);\n if (!patch) return c.json({ error: 'invalid_work_item_patch' }, 400);\n if (patch.stages !== undefined) {\n return c.json(\n { error: 'governed_transition_required', message: 'Use the Factory transition endpoint to move stages.' },\n 409,\n );\n }\n\n await workItems.ensureReady();\n try {\n const updated = await workItems.update({ orgId: tenant.orgId, id, userId: tenant.userId, patch });\n if (!updated) return c.json({ error: 'Work item not found' }, 404);\n await this.#auditWorkItemPatch({\n context: loose(c),\n item: updated.item,\n previous: updated.previous,\n patch: patch as Record<string, unknown>,\n });\n return c.json({ workItem: toWireWorkItem(updated.item) });\n } catch (error) {\n if (error instanceof WorkItemRelationError) {\n return c.json({ error: error.code, message: error.message }, 400);\n }\n throw error;\n }\n },\n }),\n\n // ── Remove a work item ───────────────────────────────────────────────────\n registerApiRoute('/web/factory/work-items/:id', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n const id = loose(c).req.param('id');\n if (!id || !UUID_RE.test(id)) return c.json({ error: 'Work item not found' }, 404);\n\n await workItems.ensureReady();\n const deleted = await workItems.delete({ orgId: tenant.orgId, id });\n if (!deleted) return c.json({ error: 'Work item not found' }, 404);\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.work_item.deleted',\n factoryProjectId: deleted.factoryProjectId,\n targets: [{ type: 'work_item', id: deleted.id, name: deleted.title }],\n },\n });\n return c.json({ ok: true });\n },\n }),\n ];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAwEA,SAAS,eAAe,MAAgC;CACtD,IACE,CAAC,KAAK,YACL,EAAA,mCAAsC,KAAK,aAC1C,EAAA,sCAA6C,KAAK,WAEpD,OAAO;CAET,OAAO;EAAE,GAAG;EAAM,UAAU,uBAAuB,KAAK,QAAQ,KAAK,CAAC;CAAE;AAC1E;;AAGA,SAAS,kBAAkB,OAAsB,cAAyD;CACxG,MAAM,0BAAU,IAAI,IAAY;CAChC,KAAK,MAAM,QAAQ,OACjB,KAAK,MAAM,EAAE,eAAe,OAAO,OAAO,KAAK,QAAQ,GACrD,IAAI,aAAa,UAAU,SAAS,GAAG,QAAQ,IAAI,SAAS;CAGhE,OAAO,CAAC,GAAG,OAAO;AACpB;AAEA,SAAS,MAAM,GAAqB;CAClC,OAAO;AACT;AAEA,MAAM,UAAU;AAEhB,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB,KAAK;AAEhC,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,YAAY,OAA0C;CAC7D,OACE,MAAM,QAAQ,KAAK,KACnB,MAAM,SAAS,KACf,MAAM,UAAU,cAChB,MAAM,OACJ,UAAS,OAAO,UAAU,YAAY,MAAM,UAAU,oBAAoB,yBAAyB,KAAK,KAAK,CAC/G,KACA,IAAI,IAAI,KAAK,CAAC,CAAC,SAAS,MAAM;AAElC;AAEA,SAAS,cAAc,OAAyD;CAC9E,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO;CAC7B,IAAI;EACF,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,UAAU;CACzC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,uBAAuB,OAAuE;CACrG,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,GACH,mCAAmC,mBACnC,0CAA0C,iBAC3C,GAAG,aACD;CACJ,OAAO;AACT;AAEA,SAAS,oBAAoB,OAA2D;CACtF,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,KAAA;CAC7B,MAAM,EAAE,eAAe,MAAM,YAAY,QAAQ;CACjD,IAAI,OAAO,kBAAkB,YAAY,cAAc,WAAW,KAAK,cAAc,SAAS,KAAK,OAAO,KAAA;CAC1G,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,KAAK,KAAK,SAAS,KAAK,OAAO,KAAA;CAC/E,IAAI,OAAO,eAAe,YAAY,WAAW,WAAW,KAAK,WAAW,SAAS,KAAK,OAAO,KAAA;CACjG,IAAI,QAAQ,KAAA,MAAc,OAAO,QAAQ,YAAY,IAAI,SAAS,OAAO,OAAO,KAAA;CAChF,OAAO;EAAE;EAAe;EAAM;EAAY,GAAI,QAAQ,KAAA,IAAY,EAAE,IAAI,IAAI,CAAC;CAAG;AAClF;AAEA,SAAS,sBAAsB,OAA2C;CACxE,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,KAAK,KAAK,GAAG,OAAO,KAAA;CAC9D,OAAO;AACT;AAEA,SAAS,cAAc,OAAkE;CACvF,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,KAAA;CAC7B,MAAM,MAA4C,CAAC;CACnD,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,KAAK,GAAG;EACnD,IAAI,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,SAAS,OAAO,GAAG,OAAO,KAAA;EAC5D,MAAM,EAAE,WAAW,QAAQ,aAAa;EACxC,IAAI,OAAO,cAAc,YAAY,UAAU,WAAW,KAAK,UAAU,SAAS,KAAK,OAAO,KAAA;EAC9F,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,KAAK,OAAO,SAAS,KAAK,OAAO,KAAA;EACrF,IAAI,OAAO,aAAa,YAAY,SAAS,WAAW,KAAK,SAAS,SAAS,KAAK,OAAO,KAAA;EAC3F,IAAI,QAAQ;GAAE;GAAW;GAAQ;EAAS;CAC5C;CACA,OAAO;AACT;;AAGA,SAAgB,oBAAoB,MAA2C;CAC7E,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO;CAC5B,MAAM,EAAE,gBAAgB,OAAO,QAAQ,UAAU,aAAa;CAC9D,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,WAAW,KAAK,MAAM,SAAS,KAAK,OAAO;CAEzF,MAAM,sBAAsB,sBAAsB;CAClD,MAAM,mBAAmB,sBAAsB,sBAAsB,KAAK,gBAAgB,IAAI,KAAA;CAC9F,IAAI,uBAAuB,qBAAqB,KAAA,GAAW,OAAO;CAClE,MAAM,eAAe,oBAAoB,cAAc;CACvD,IAAI,mBAAmB,KAAA,KAAa,iBAAiB,KAAA,GAAW,OAAO;CACvE,IAAI,WAAW,KAAA,KAAa,CAAC,YAAY,MAAM,GAAG,OAAO;CACzD,MAAM,iBAAiB,aAAa,KAAA,IAAY,KAAA,IAAY,cAAc,QAAQ;CAClF,IAAI,aAAa,KAAA,KAAa,mBAAmB,KAAA,GAAW,OAAO;CACnE,IAAI;CACJ,IAAI,aAAa,KAAA,GAAW;EAC1B,IAAI,CAAC,cAAc,QAAQ,GAAG,OAAO;EACrC,iBAAiB,uBAAuB,QAAQ;CAClD;CAEA,OAAO;EACL,OAAO,MAAM,KAAK;EAClB,GAAI,iBAAiB,KAAA,IAAY,EAAE,gBAAgB,aAAa,IAAI,CAAC;EACrE,GAAI,sBAAsB,EAAE,kBAAkB,oBAAoB,KAAK,IAAI,CAAC;EAC5E,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EACzC,GAAI,mBAAmB,KAAA,IAAY,EAAE,UAAU,eAAe,IAAI,CAAC;EACnE,GAAI,mBAAmB,KAAA,IAAY,EAAE,UAAU,eAAe,IAAI,CAAC;CACrE;AACF;;AAGA,SAAgB,oBAAoB,MAA2C;CAC7E,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO;CAC5B,MAAM,EAAE,OAAO,QAAQ,UAAU,UAAU,qBAAqB;CAChE,MAAM,sBAAsB,sBAAsB;CAClD,IACE,UAAU,KAAA,KACV,WAAW,KAAA,KACX,aAAa,KAAA,KACb,aAAa,KAAA,KACb,qBAAqB,KAAA,KACrB,CAAC,qBAED,OAAO;CACT,IAAI,qBAAqB,KAAA,KAAa,qBAAqB,MAAM,OAAO;CACxE,MAAM,mBAAmB,sBAAsB,sBAAsB,KAAK,gBAAgB,IAAI,KAAA;CAC9F,IAAI,uBAAuB,qBAAqB,KAAA,GAAW,OAAO;CAClE,IAAI,UAAU,KAAA,MAAc,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,WAAW,KAAK,MAAM,SAAS,MACnG,OAAO;CACT,IAAI,WAAW,KAAA,KAAa,CAAC,YAAY,MAAM,GAAG,OAAO;CACzD,MAAM,iBAAiB,aAAa,KAAA,IAAY,KAAA,IAAY,cAAc,QAAQ;CAClF,IAAI,aAAa,KAAA,KAAa,mBAAmB,KAAA,GAAW,OAAO;CACnE,IAAI;CACJ,IAAI,aAAa,KAAA,GAAW;EAC1B,IAAI,CAAC,cAAc,QAAQ,GAAG,OAAO;EACrC,iBAAiB,uBAAuB,QAAQ;CAClD;CAEA,OAAO;EACL,GAAI,sBAAsB,EAAE,kBAAkB,oBAAoB,KAAK,IAAI,CAAC;EAC5E,GAAI,UAAU,KAAA,IAAY,EAAE,OAAO,MAAM,KAAK,EAAE,IAAI,CAAC;EACrD,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EACzC,GAAI,mBAAmB,KAAA,IAAY,EAAE,UAAU,eAAe,IAAI,CAAC;EACnE,GAAI,mBAAmB,KAAA,IAAY,EAAE,UAAU,eAAe,IAAI,CAAC;EACnE,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAChE;AACF;AAEA,eAAe,SAAS,GAA0C;CAChE,IAAI;EACF,OAAO,MAAM,EAAE,IAAI,KAAK;CAC1B,QAAQ;EACN;CACF;AACF;;AAGA,SAAS,cAAc,OAA0C;CAC/D,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,QAAO,QAAO,MAAM,SAAS,KAAA,CAAS;AAClE;AAEA,SAAS,YAAY,OAAgB,KAAiC;CACpE,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;CACtC,MAAM,aAAa,MAAM,KAAK;CAC9B,OAAO,WAAW,SAAS,KAAK,WAAW,UAAU,MAAM,aAAa,KAAA;AAC1E;AAEA,SAAS,oBACP,MAC8F;CAC9F,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO;CAC5B,MAAM,QAAQ,oBAAoB,SAAS,KAAK,KAAyB,IACpE,KAAK,QACN,KAAA;CACJ,MAAM,QAAQ,mBAAmB,KAAK,KAAK,IAAI,KAAK,QAAQ,KAAA;CAC5D,MAAM,YAAY,YAAY,KAAK,WAAW,GAAG;CACjD,MAAM,QAAQ,YAAY,KAAK,OAAO,GAAG;CACzC,IACE,CAAC,SACD,CAAC,SACD,CAAC,aACD,CAAC,QAAQ,KAAK,SAAS,KACvB,CAAC,SACD,CAAC,OAAO,UAAU,KAAK,gBAAgB,KACvC,OAAO,KAAK,gBAAgB,IAAI,GAEhC,OAAO;CAET,OAAO;EACL;EACA;EACA,kBAAkB,OAAO,KAAK,gBAAgB;EAC9C,SAAS;GAAE,MAAM;GAAS,UAAU;EAAU;EAC9C;EACA,GAAI,KAAK,YAAY,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;CACnD;AACF;AAEA,SAAS,eACP,MACA,QACA,kBAC4B;CAC5B,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,QAAQ,GAAG,OAAO;CACxD,MAAM,QAAQ,oBAAoB,KAAK,SAAS,KAAK;CACrD,MAAM,YAAY,YAAY,KAAK,WAAW,GAAG;CACjD,MAAM,cAAc,YAAY,KAAK,aAAa,GAAG;CACrD,MAAM,aAAa,YAAY,KAAK,YAAY,GAAG;CACnD,MAAM,OAAO,YAAY,KAAK,SAAS,MAAM,EAAE;CAC/C,MAAM,KAAK,YAAY,KAAK,SAAS,IAAI,EAAE;CAC3C,IACE,CAAC,SACD,CAAC,aACD,CAAC,QAAQ,KAAK,SAAS,KACvB,CAAC,eACD,CAAC,cACD,CAAC,QAAQ,KAAK,UAAU,KACxB,CAAC,MACD,CAAC,QAAQ,KAAK,EAAE,KAChB,CAAC,MAED,OAAO;CAET,OAAO;EAAE,GAAG;EAAQ;EAAkB;EAAW;EAAa;EAAY,UAAU;GAAE;GAAI;GAAM;EAAM;CAAE;AAC1G;AAEA,MAAM,oCAAoB,IAAI,IAA2B;CACvD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,6BAA6B;AACnC,MAAM,yBAAyB;AAE/B,SAAS,sBAAsB,KAA8D;CAC3F,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG,CAAC,CAAC,KAAI,WAAU,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QACxE,WAA4C,kBAAkB,IAAI,MAA+B,CACpG;CACA,OAAO,SAAS,SAAS,IAAI,WAAW,KAAA;AAC1C;AAEA,SAAS,mBAAmB,KAAiC;CAC3D,MAAM,SAAS,MAAM,OAAO,SAAS,KAAK,EAAE,IAAI;CAChD,IAAI,CAAC,OAAO,SAAS,MAAM,GAAG,OAAO;CACrC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,wBAAwB,MAAM,CAAC;AAC7D;AAEA,SAAS,qBAAqB,UAAiD;CAC7E,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,SAAS,UAAU,YAAY,GAAG,SAAS,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,SAAS,WAAW;AAClH;AAEA,SAAS,oBAAoB,KAAsE;CACjG,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,IAAI;EACF,MAAM,UAAU,KAAK,MAAM,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC;EACzE,IACE,CAAC,MAAM,QAAQ,OAAO,KACtB,QAAQ,WAAW,KACnB,OAAO,QAAQ,OAAO,YACtB,OAAO,QAAQ,OAAO,UAEtB;EAEF,MAAM,YAAY,IAAI,KAAK,QAAQ,EAAE;EACrC,IAAI,OAAO,MAAM,UAAU,QAAQ,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,GAAG,OAAO,KAAA;EAC3E,OAAO;GAAE;GAAW,IAAI,QAAQ;EAAG;CACrC,QAAQ;EACN;CACF;AACF;;AAGA,SAAS,YAAY,UAAkD;CACrE,IAAI,OAAO,SAAS,SAAS,UAAU,OAAO,SAAS,KAAK,MAAM,GAAG,EAAE;CACvE,IAAI,SAAS,SAAS,cAAc,OAAO;CAC3C,MAAM,QAAQ,SAAS;CACvB,MAAM,QAAQ,SAAS;CACvB,IAAK,UAAU,UAAU,UAAU,YAAa,CAAC,mBAAmB,KAAK,GAAG,OAAO;CACnF,OAAO,aAAa,OAAO,KAAK;AAClC;;AAGA,SAAS,cAAc,UAA0D;CAC/E,IAAI,SAAS,SAAS,wBAAwB,OAAO;CACrD,MAAM,SAAS,SAAS;CACxB,OAAO,WAAW,kBAAkB,WAAW,eAAe,WAAW,kBAAkB,WAAW,WAClG,SACA;AACN;AAEA,SAAS,gBAAgB,UAAyC;CAChE,OAAO;EACL,IAAI,SAAS;EACb,cAAc,SAAS;EACvB,YAAY,SAAS;EACrB,MAAM,oBAAoB,QAAQ;EAClC,MAAM,YAAY,SAAS,QAAQ;EACnC,QAAQ,cAAc,SAAS,QAAQ;EACvC,QAAQ,SAAS;EACjB,UAAU,SAAS;EACnB,mBAAmB,SAAS;EAC5B,aAAa,SAAS;EACtB,UAAU,+BAA+B,SAAS,WAAW,CAAC,CAAC;EAC/D,WAAW,SAAS,WAAW,MAAM,GAAG,GAAG,KAAK;EAChD,WAAW,SAAS,UAAU,YAAY;EAC1C,WAAW,SAAS,UAAU,YAAY;EAC1C,aAAa,SAAS,aAAa,YAAY,KAAK;CACtD;AACF;AAEA,IAAa,iBAAb,cAAoC,MAA0B;;CAE5D,MAAMA,eAAe,GAAiF;EACpG,MAAM,KAAK,KAAK,KAAK,WAAW,CAAC;EACjC,MAAM,SAAS,KAAK,KAAK,KAAK,OAAO,CAAC;EACtC,IAAI,CAAC,QAAQ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,eAAe,GAAG,GAAG,EAAE;EACvE,IAAI,CAAC,OAAO,OACV,OAAO,EACL,UAAU,EAAE,KACV;GAAE,OAAO;GAAyB,SAAS;EAA8C,GACzF,GACF,EACF;EAEF,OAAO;GAAE,OAAO,OAAO;GAAO,QAAQ,OAAO;EAAO;CACtD;;;;;;CAOA,MAAMC,gBACJ,GAGA;EACA,MAAM,SAAS,MAAM,KAAKD,eAAe,CAAC;EAC1C,IAAI,cAAc,QAAQ,OAAO;EAEjC,MAAM,YAAY,EAAE,IAAI,MAAM,IAAI;EAClC,IAAI,CAAC,aAAa,CAAC,QAAQ,KAAK,SAAS,GACvC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG,EAAE;EAEjE,MAAM,EAAE,aAAa,KAAK;EAC1B,MAAM,SAAS,YAAY;EAC3B,MAAM,UAAU,MAAM,SAAS,IAAI;GAAE,OAAO,OAAO;GAAO,IAAI;EAAU,CAAC;EACzE,IAAI,CAAC,SACH,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG,EAAE;EAEjE,OAAO;GAAE,GAAG;GAAQ,kBAAkB;GAAW,gBAAgB,QAAQ;EAAe;CAC1F;;;;;;CAOA,MAAME,oBAAoB,EACxB,SACA,MACA,UACA,SAMgB;EAChB,MAAM,EAAE,UAAU,KAAK;EACvB,MAAM,SAAS;GAAE,MAAM;GAAa,IAAI,KAAK;GAAI,MAAM,KAAK;EAAM;EAClE,MAAM,MAAM,KAAK;GACf;GACA,OAAO;IACL,QAAQ;IACR,kBAAkB,KAAK;IACvB,SAAS,CAAC,MAAM;IAChB,UAAU,EAAE,QAAQ,cAAc,KAAK,EAAE;GAC3C;EACF,CAAC;EAKD,IAFE,MAAM,WAAW,KAAA,MAChB,SAAS,OAAO,WAAW,KAAK,OAAO,UAAU,SAAS,OAAO,MAAM,GAAG,MAAM,MAAM,KAAK,OAAO,EAAE,IAErG,MAAM,MAAM,KAAK;GACf;GACA,OAAO;IACL,QAAQ;IACR,kBAAkB,KAAK;IACvB,SAAS,CAAC,MAAM;IAChB,UAAU;KAAE,MAAM,SAAS;KAAQ,IAAI,KAAK;IAAO;GACrD;EACF,CAAC;EAGH,MAAM,WAAW,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,QAAO,SAAQ,CAAC,SAAS,aAAa,SAAS,IAAI,CAAC;EAChG,KAAK,MAAM,QAAQ,UAAU;GAC3B,MAAM,UAAU,KAAK,SAAS;GAC9B,MAAM,MAAM,KAAK;IACf;IACA,OAAO;KACL,QAAQ;KACR,kBAAkB,KAAK;KACvB,SAAS,CAAC,MAAM;KAChB,UAAU;MACR;MACA,QAAQ,SAAS;MACjB,UAAU,SAAS;MACnB,WAAW,SAAS;KACtB;IACF;GACF,CAAC;EACH;CACF;;CAGA,eAAe,EACb,MACA,UAUW;EACX,MAAM,EAAE,OAAO,cAAc,KAAK;EAClC,OAAO,iBAAiB,mDAAmD,QAAQ;GACjF,QAAQ;GACR,cAAc;GACd,SAAS,OAAM,MAAK;IAClB,MAAM,UAAU,MAAM,CAAC;IACvB,MAAM,WAAW,MAAM,KAAKD,gBAAgB,OAAO;IACnD,IAAI,cAAc,UAAU,OAAO,SAAS;IAC5C,MAAM,aAAa,QAAQ,IAAI,MAAM,YAAY;IACjD,IAAI,CAAC,cAAc,CAAC,QAAQ,KAAK,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;IACjG,MAAM,UAAU,YAAY;IAC5B,MAAM,sBAAM,IAAI,KAAK;IACrB,MAAM,WAAW,MAAM,OAAO,SAAS,OAAO,SAAS,kBAAkB,YAAY,KAAK,SAAS,MAAM;IACzG,IAAI,CAAC,UAAU,OAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,GAAG,GAAG;IAIpE,MAAM,MAAM,KAAK;KACf;KACA,OAAO;MACL,QAAQ,SAAS,YAAY,yBAAyB;MACtD,kBAAkB,SAAS;MAC3B,SAAS,SAAS,aACd,CAAC;OAAE,MAAM;OAAa,IAAI,SAAS;MAAW,CAAC,IAC/C,CAAC;OAAE,MAAM;OAAiB,IAAI,SAAS;MAAG,CAAC;MAC/C,UAAU;OAAE,YAAY,SAAS;OAAI,QAAQ,oBAAoB,QAAQ;MAAE;KAC7E;IACF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,UAAU,gBAAgB,QAAQ,EAAE,CAAC;GACvD;EACF,CAAC;CACH;;CAGA,SAAqB;EACnB,MAAM,EAAE,OAAO,WAAW,aAAa,mBAAmB,kBAAkB,iBAAiB,KAAK;EAClG,OAAO;GAEL,iBAAiB,wCAAwC;IACvD,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,MAAM,UAAU,YAAY;KAC5B,MAAM,QAAQ,MAAM,UAAU,KAAK;MACjC,OAAO,SAAS;MAChB,kBAAkB,SAAS;KAC7B,CAAC;KACD,OAAO,EAAE,KAAK;MACZ,WAAW,MAAM,IAAI,cAAc;MACnC,mBAAmB,kBAAkB,OAAO,YAAY;KAC1D,CAAC;IACH;GACF,CAAC;GAGD,iBAAiB,qCAAqC;IACpD,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,MAAM,EAAE,aAAa,cAAc,kBACjC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,MAAM,GACzB,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,mBACvB,IAAI,KAAK,CACX;KACA,MAAM,UAAU,YAAY;KAC5B,MAAM,QAAQ,MAAM,UAAU,KAAK;MACjC,OAAO,SAAS;MAChB,kBAAkB,SAAS;KAC7B,CAAC;KACD,OAAO,EAAE,KAAK,EAAE,SAAS,sBAAsB,OAAO;MAAE;MAAa;KAAU,CAAC,EAAE,CAAC;IACrF;GACF,CAAC;GAGD,iBAAiB,+CAA+C;IAC9D,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,MAAM,YAAY,YAAY;KAC9B,MAAM,SAAS,MAAM,YAAY,UAAU,SAAS,OAAO,SAAS,gBAAgB;KAKpF,OAAO,EAAE,KAAK,EAAE,YAAY,oBAAoB,MAAM,EAAE,CAAC;IAC3D;GACF,CAAC;GAGD,iBAAiB,uCAAuC;IACtD,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,UAAU,MAAM,CAAC;KACvB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,OAAO;KACnD,IAAI,cAAc,UAAU,OAAO,SAAS;KAE5C,MAAM,YAAY,QAAQ,IAAI,MAAM,QAAQ;KAC5C,MAAM,SAAS,oBAAoB,SAAS;KAC5C,IAAI,aAAa,CAAC,QAAQ,OAAO,EAAE,KAAK,EAAE,OAAO,iBAAiB,GAAG,GAAG;KACxE,MAAM,UAAU,YAAY;KAC5B,MAAM,OAAO,MAAM,UAAU,yBAAyB;MACpD,OAAO,SAAS;MAChB,kBAAkB,SAAS;MAC3B,UAAU,sBAAsB,QAAQ,IAAI,MAAM,UAAU,CAAC;MAC7D;MACA,OAAO,mBAAmB,QAAQ,IAAI,MAAM,OAAO,CAAC;KACtD,CAAC;KACD,MAAM,OAAO,KAAK,UAAU,GAAG,EAAE;KACjC,OAAO,EAAE,KAAK;MACZ,WAAW,KAAK,UAAU,IAAI,eAAe;MAC7C,GAAI,KAAK,WAAW,OAAO,EAAE,YAAY,qBAAqB,IAAI,EAAE,IAAI,CAAC;KAC3E,CAAC;IACH;GACF,CAAC;GAED,GAAG,qBAAqB;IACtB;IACA,UAAU,KAAK,KAAK;IACpB,iBAAgB,YAAW,KAAKA,gBAAgB,MAAM,OAAO,CAAC;GAChE,CAAC;GAED,GAAG,sBAAsB;IACvB;IACA,iBAAgB,YAAW,KAAKA,gBAAgB,MAAM,OAAO,CAAC;GAChE,CAAC;GAED,KAAKE,eAAe;IAAE,MAAM;IAAW,QAAQ,UAAU,wBAAwB,KAAK,SAAS;GAAE,CAAC;GAClG,KAAKA,eAAe;IAAE,MAAM;IAAW,QAAQ,UAAU,wBAAwB,KAAK,SAAS;GAAE,CAAC;GAElG,iBAAiB,yDAAyD;IACxE,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,UAAU,MAAM,CAAC;KACvB,MAAM,WAAW,MAAM,KAAKF,gBAAgB,OAAO;KACnD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,MAAM,aAAa,QAAQ,IAAI,MAAM,YAAY;KACjD,IAAI,CAAC,cAAc,CAAC,QAAQ,KAAK,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;KACjG,MAAM,UAAU,YAAY;KAC5B,MAAM,UAAU,MAAM,UAAU,oBAAoB,SAAS,OAAO,SAAS,kBAAkB,UAAU;KACzG,IACE,CAAC,WACD,QAAQ,WAAW,YACnB,CAAC,+BAA+B,QAAQ,WAAW,CAAC,CAAC,UAErD,OAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,GAAG,GAAG;KAExD,MAAM,WAAW,MAAM,UAAU,sBAC/B,SAAS,OACT,SAAS,kBACT,4BACA,IAAI,KAAK,CACX;KACA,IAAI,CAAC,UAAU,OAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,GAAG,GAAG;KACrE,OAAO,EAAE,KAAK,EAAE,UAAU,gBAAgB,QAAQ,EAAE,CAAC;IACvD;GACF,CAAC;GAGD,iBAAiB,wCAAwC;IACvD,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAE5C,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC;KACpC,IAAI,SAAS,KAAA,GAAW,OAAO,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG;KACzE,MAAM,QAAQ,oBAAoB,IAAI;KACtC,IAAI,CAAC,OAAO,OAAO,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG;KAC7D,KAAK,MAAM,UAAU,CAAC,QAAQ,EAAA,CAAG,WAAW,MAAM,MAAM,UAAU,CAAC,QAAQ,EAAA,CAAG,OAAO,UACnF,OAAO,EAAE,KACP;MAAE,OAAO;MAAgC,SAAS;KAAoD,GACtG,GACF;KAGF,MAAM,UAAU,YAAY;KAC5B,IAAI;MACF,MAAM,SAAS,MAAM,UAAU,OAAO;OACpC,OAAO,SAAS;OAChB,QAAQ,SAAS;OACjB,kBAAkB,SAAS;OAC3B;OACA,WAAW;MACb,CAAC;MACD,IAAI,OAAO,OAAO;MAClB,IAAI,OAAO,SAAS;OAClB,IAAI,CAAC,mBAAmB;QACtB,MAAM,UAAU,OAAO;SAAE,OAAO,SAAS;SAAO,IAAI,KAAK;QAAG,CAAC;QAC7D,OAAO,EAAE,KAAK,EAAE,OAAO,kCAAkC,GAAG,GAAG;OACjE;OACA,MAAM,UAAU,MAAM,kBAAkB,WAAW;QACjD,OAAO,SAAS;QAChB,kBAAkB,SAAS;QAC3B,YAAY,KAAK;QACjB,OAAO,KAAK,gBAAgB,SAAS,iBAAiB,WAAW;QACjE,OAAO;QACP,kBAAkB,KAAK;QACvB,OAAO;SAAE,MAAM;SAAS,IAAI,SAAS;QAAO;QAC5C,SAAS;SAAE,MAAM;SAAS,UAAU,aAAa,KAAK,GAAG;QAAgB;QACzE,OAAO;QACP,cAAc;OAChB,CAAC;OACD,IAAI,QAAQ,WAAW,YAAY;QACjC,MAAM,UAAU,OAAO;SAAE,OAAO,SAAS;SAAO,IAAI,KAAK;QAAG,CAAC;QAC7D,OAAO,EAAE,KAAK;SAAE,QAAQ;SAAY,MAAM,QAAQ;SAAM,QAAQ,QAAQ;QAAO,GAAG,GAAG;OACvF;OACA,OAAQ,MAAM,UAAU,cAAc,SAAS,OAAO,SAAS,kBAAkB,KAAK,EAAE,KAAM;OAC9F,MAAM,MAAM,KAAK;QACf,SAAS,MAAM,CAAC;QAChB,OAAO;SACL,QAAQ;SACR,kBAAkB,SAAS;SAC3B,SAAS,CAAC;UAAE,MAAM;UAAa,IAAI,KAAK;UAAI,MAAM,KAAK;SAAM,CAAC;SAC9D,UAAU;UAAE,gBAAgB,KAAK;UAAgB,QAAQ,KAAK;SAAO;QACvE;OACF,CAAC;MACH,OAAO;OAGL,MAAM,EAAE,QAAQ,SAAS,UAAU,WAAW,GAAG,iBAAiB;OAClE,MAAM,KAAKC,oBAAoB;QAC7B,SAAS,MAAM,CAAC;QAChB;QACA,UAAU,OAAO;QACjB,OAAO;OACT,CAAC;MACH;MACA,OAAO,EAAE,KAAK,EAAE,UAAU,eAAe,IAAI,EAAE,CAAC;KAClD,SAAS,OAAO;MACd,IAAI,iBAAiB,uBACnB,OAAO,EAAE,KAAK;OAAE,OAAO,MAAM;OAAM,SAAS,MAAM;MAAQ,GAAG,GAAG;MAElE,MAAM;KACR;IACF;GACF,CAAC;GAGD,iBAAiB,+DAA+D;IAC9E,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKD,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,MAAM,aAAa,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,YAAY;KAClD,IAAI,CAAC,cAAc,CAAC,QAAQ,KAAK,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;KACjG,MAAM,SAAS,oBAAoB,MAAM,SAAS,MAAM,CAAC,CAAC,CAAC;KAC3D,IAAI,CAAC,QAAQ,OAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;KACvE,IAAI,CAAC,mBACH,OAAO,EAAE,KAAK,EAAE,OAAO,iCAAiC,GAAG,GAAG;KAEhE,MAAM,UAAU,YAAY;KAC5B,MAAM,SAAS,MAAM,kBAAkB,WAAW;MAChD,GAAG;MACH,OAAO,SAAS;MAChB,kBAAkB,SAAS;MAC3B;MACA,OAAO;OAAE,MAAM;OAAS,IAAI,SAAS;MAAO;MAC5C,SAAS;OACP,GAAG,OAAO;OACV,UAAU,SAAS,SAAS,OAAO,GAAG,OAAO,QAAQ;MACvD;KACF,CAAC;KACD,MAAM,MAAM,KAAK;MACf,SAAS,MAAM,CAAC;MAChB,OAAO;OACL,QACE,OAAO,WAAW,aACd,kCACA;OACN,kBAAkB,SAAS;OAC3B,SAAS,CAAC;QAAE,MAAM;QAAa,IAAI;OAAW,CAAC;OAC/C,UAAU;QACR,cAAc,OAAO;QACrB,aAAa,OAAO,QAAQ;QAC5B,gBAAgB,kBAAkB;QAClC,GAAI,OAAO,WAAW,aAClB;SAAE,IAAI,OAAO;SAAO,UAAU,OAAO;QAAS,IAC9C;SAAE,MAAM,OAAO;SAAM,QAAQ,OAAO;QAAO;OACjD;MACF;KACF,CAAC;KACD,IAAI,OAAO,WAAW,YAAY,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;KAC1D,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,SAAS,UAAU,MAAM,GAAG;IAC/D;GACF,CAAC;GAGD,iBAAiB,wCAAwC;IACvD,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,WAAW,MAAM,KAAKA,gBAAgB,MAAM,CAAC,CAAC;KACpD,IAAI,cAAc,UAAU,OAAO,SAAS;KAC5C,IAAI,CAAC,kBACH,OAAO,EAAE,KAAK,EAAE,OAAO,4BAA4B,GAAG,GAAG;KAE3D,MAAM,QAAQ,eAAe,MAAM,SAAS,MAAM,CAAC,CAAC,GAAG,UAAU,SAAS,gBAAgB;KAC1F,IAAI,CAAC,OAAO,OAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,GAAG,GAAG;KACjE,MAAM,iBAAiB,MAAM,CAAC,CAAC,CAAC,IAAI,gBAAgB;KACpD,MAAM,iBAAiB,SAAS,kBAAkB,KAAA;KAClD,MAAM,UAAU,YAAY;KAC5B,IAAI;KACJ,IAAI;MACF,WAAW,MAAM,iBAAiB,QAAQ,KAAK;KACjD,SAAS,OAAO;MACd,IAAI,iBAAiB,6BACnB,OAAO,EAAE,KAAK,EAAE,QAAQ,MAAM,OAAO,GAAG,MAAM,OAAO,SAAS,UAAU,MAAM,GAAG;MAEnF,MAAM;KACR;KACA,MAAM,MAAM,KAAK;MACf,SAAS,MAAM,CAAC;MAChB,OAAO;OACL,QAAQ;OACR,kBAAkB,SAAS;OAC3B,SAAS,CAAC;QAAE,MAAM;QAAa,IAAI,SAAS;OAAW,CAAC;OACxD,UAAU;QACR,MAAM,MAAM,SAAS;QACrB,QAAQ,SAAS;QACjB,UAAU,SAAS;QACnB,WAAW,SAAS;QACpB,WAAW,SAAS;OACtB;MACF;KACF,CAAC;KACD,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG;IACjC;GACF,CAAC;GAGD,iBAAiB,+BAA+B;IAC9C,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,SAAS,MAAM,KAAKD,eAAe,MAAM,CAAC,CAAC;KACjD,IAAI,cAAc,QAAQ,OAAO,OAAO;KAExC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI;KAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;KAEjF,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC;KACpC,IAAI,SAAS,KAAA,GAAW,OAAO,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG;KACzE,MAAM,QAAQ,oBAAoB,IAAI;KACtC,IAAI,CAAC,OAAO,OAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,GAAG,GAAG;KACnE,IAAI,MAAM,WAAW,KAAA,GACnB,OAAO,EAAE,KACP;MAAE,OAAO;MAAgC,SAAS;KAAsD,GACxG,GACF;KAGF,MAAM,UAAU,YAAY;KAC5B,IAAI;MACF,MAAM,UAAU,MAAM,UAAU,OAAO;OAAE,OAAO,OAAO;OAAO;OAAI,QAAQ,OAAO;OAAQ;MAAM,CAAC;MAChG,IAAI,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;MACjE,MAAM,KAAKE,oBAAoB;OAC7B,SAAS,MAAM,CAAC;OAChB,MAAM,QAAQ;OACd,UAAU,QAAQ;OACX;MACT,CAAC;MACD,OAAO,EAAE,KAAK,EAAE,UAAU,eAAe,QAAQ,IAAI,EAAE,CAAC;KAC1D,SAAS,OAAO;MACd,IAAI,iBAAiB,uBACnB,OAAO,EAAE,KAAK;OAAE,OAAO,MAAM;OAAM,SAAS,MAAM;MAAQ,GAAG,GAAG;MAElE,MAAM;KACR;IACF;GACF,CAAC;GAGD,iBAAiB,+BAA+B;IAC9C,QAAQ;IACR,cAAc;IACd,SAAS,OAAM,MAAK;KAClB,MAAM,SAAS,MAAM,KAAKF,eAAe,MAAM,CAAC,CAAC;KACjD,IAAI,cAAc,QAAQ,OAAO,OAAO;KAExC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI;KAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;KAEjF,MAAM,UAAU,YAAY;KAC5B,MAAM,UAAU,MAAM,UAAU,OAAO;MAAE,OAAO,OAAO;MAAO;KAAG,CAAC;KAClE,IAAI,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,OAAO,sBAAsB,GAAG,GAAG;KACjE,MAAM,MAAM,KAAK;MACf,SAAS,MAAM,CAAC;MAChB,OAAO;OACL,QAAQ;OACR,kBAAkB,QAAQ;OAC1B,SAAS,CAAC;QAAE,MAAM;QAAa,IAAI,QAAQ;QAAI,MAAM,QAAQ;OAAM,CAAC;MACtE;KACF,CAAC;KACD,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;IAC5B;GACF,CAAC;EACH;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"work-items.js","names":["#resolveTenant","#resolveProject","#auditWorkItemPatch","#proposalRoute"],"sources":["../../src/routes/work-items.ts"],"sourcesContent":["/**\n * Mastra `apiRoutes` for Factory work items (the kanban board).\n *\n * Registered alongside the other `/web/*` routes behind the host auth gate.\n * The board is org-wide: every route re-resolves the caller's `(orgId, userId)`\n * tenant and scopes reads/writes by `orgId`, so any org member sees and moves\n * the same cards while `created_by` / stage history record who acted.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { createBoardRegistry } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/index.js';\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n FactoryStartCoordinator,\n FactoryStartPreparedResult,\n FactoryStartRequest,\n} from '../rules/start-coordinator.js';\nimport { FactoryStartTransitionError } from '../rules/start-coordinator.js';\nimport { roleForStage } from '../rules/transition-service.js';\nimport type { FactoryTransitionRequest, FactoryTransitionService } from '../rules/transition-service.js';\nimport type { FactoryRuleBoard, FactoryRuleStage, WorkItemSource } from '../rules/types.js';\nimport { isFactoryRuleStage } from '../rules/types.js';\nimport type { LiveSessions } from '../session/live-sessions.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';\nimport { thresholdsOrDefault } from '../storage/domains/queue-health/base.js';\nimport type {\n CreateWorkItemInput,\n ExternalWorkItemSource,\n FactoryDeferredDecisionRecord,\n FactoryDispatchStatus,\n UpdateWorkItemInput,\n WorkItemPriorState,\n WorkItemRow,\n WorkItemSessionInput,\n WorkItemStage,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport {\n FACTORY_PULL_REQUEST_RECONCILIATION_KEY,\n FACTORY_RULE_MATERIALIZATION_KEY,\n WorkItemRelationError,\n WorkItemUpdateConflictError,\n} from '../storage/domains/work-items/base.js';\nimport { computeFactoryMetrics, parseMetricsRange } from '../storage/domains/work-items/metrics.js';\nimport { buildAttentionRoutes, factoryDecisionType } from './attention.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\nimport { buildSupervisorRoutes } from './supervisor.js';\n\nexport interface WorkItemRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Factory projects domain — validates the `:id` project belongs to the caller's org. */\n projects: FactoryProjectsStorage;\n /** Work-items domain backing the kanban board. */\n workItems: WorkItemsStorage;\n /** Boards installed for this Factory instance. */\n boardRegistry?: BoardRegistry;\n /** Comments domain — backs the mention attention provider. */\n comments: WorkItemCommentsStorage;\n /** Per-project queue-health threshold config. */\n queueHealth: QueueHealthStorage;\n /** Governed stage-transition service. Stage moves 503 when absent. */\n transitionService?: Pick<FactoryTransitionService, 'transition' | 'ruleSetVersion'>;\n /** Coordinator that binds a Factory run before dispatching its kickoff. */\n startCoordinator?: Pick<FactoryStartCoordinator, 'prepare'>;\n /** Materialized sessions, read to report which of the listed cards are being worked. */\n liveSessions: Pick<LiveSessions, 'isRunning'>;\n}\n\n/** The card as clients see it, without the dispatcher's internal bookkeeping. */\nfunction toWireWorkItem(item: WorkItemRow): WorkItemRow {\n if (\n !item.metadata ||\n (!(FACTORY_RULE_MATERIALIZATION_KEY in item.metadata) &&\n !(FACTORY_PULL_REQUEST_RECONCILIATION_KEY in item.metadata))\n ) {\n return item;\n }\n return { ...item, metadata: publicWorkItemMetadata(item.metadata) ?? {} };\n}\n\n/** Session ids of the listed cards whose agent run is in flight. */\nfunction runningSessionIds(items: WorkItemRow[], liveSessions: Pick<LiveSessions, 'isRunning'>): string[] {\n const running = new Set<string>();\n for (const item of items) {\n for (const { sessionId } of Object.values(item.sessions)) {\n if (liveSessions.isRunning(sessionId)) running.add(sessionId);\n }\n }\n return [...running];\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nconst MAX_STAGES = 16;\nconst MAX_STAGE_LENGTH = 64;\nconst MAX_METADATA_BYTES = 16 * 1024;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validStages(value: unknown): value is WorkItemStage[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.length <= MAX_STAGES &&\n value.every(\n stage => typeof stage === 'string' && stage.length <= MAX_STAGE_LENGTH && /^[a-z0-9][a-z0-9_-]*$/i.test(stage),\n ) &&\n new Set(value).size === value.length\n );\n}\n\nfunction validMetadata(value: unknown): value is Record<string, unknown> | null {\n if (value === null) return true;\n if (!isRecord(value)) return false;\n try {\n return JSON.stringify(value).length <= MAX_METADATA_BYTES;\n } catch {\n return false;\n }\n}\n\nfunction publicWorkItemMetadata(value: Record<string, unknown> | null): Record<string, unknown> | null {\n if (value === null) return null;\n const {\n [FACTORY_RULE_MATERIALIZATION_KEY]: _materialization,\n [FACTORY_PULL_REQUEST_RECONCILIATION_KEY]: _reconciliation,\n ...metadata\n } = value;\n return metadata;\n}\n\nfunction parseExternalSource(value: unknown): ExternalWorkItemSource | null | undefined {\n if (value === undefined || value === null) return value;\n if (!isRecord(value)) return undefined;\n const { integrationId, type, externalId, url } = value;\n if (typeof integrationId !== 'string' || integrationId.length === 0 || integrationId.length > 128) return undefined;\n if (typeof type !== 'string' || type.length === 0 || type.length > 128) return undefined;\n if (typeof externalId !== 'string' || externalId.length === 0 || externalId.length > 512) return undefined;\n if (url !== undefined && (typeof url !== 'string' || url.length > 2048)) return undefined;\n return { integrationId, type, externalId, ...(url !== undefined ? { url } : {}) };\n}\n\nfunction parseParentWorkItemId(value: unknown): string | null | undefined {\n if (value === null) return null;\n if (typeof value !== 'string' || !UUID_RE.test(value)) return undefined;\n return value;\n}\n\nfunction parseSessions(value: unknown): Record<string, WorkItemSessionInput> | undefined {\n if (!isRecord(value)) return undefined;\n const out: Record<string, WorkItemSessionInput> = {};\n for (const [role, session] of Object.entries(value)) {\n if (!role || role.length > 64 || !isRecord(session)) return undefined;\n const { sessionId, branch, threadId } = session;\n if (typeof sessionId !== 'string' || sessionId.length === 0 || sessionId.length > 512) return undefined;\n if (typeof branch !== 'string' || branch.length === 0 || branch.length > 512) return undefined;\n if (typeof threadId !== 'string' || threadId.length === 0 || threadId.length > 512) return undefined;\n out[role] = { sessionId, branch, threadId };\n }\n return out;\n}\n\n/** Validate an untrusted create body. Unknown keys are dropped. */\nexport function parseCreateWorkItem(body: unknown): CreateWorkItemInput | null {\n if (!isRecord(body)) return null;\n const { board, externalSource, title, stages, sessions, metadata } = body;\n if (typeof title !== 'string' || title.trim().length === 0 || title.length > 500) return null;\n if (board !== undefined && (typeof board !== 'string' || board.length === 0 || board.length > 128)) return null;\n\n const hasParentWorkItemId = 'parentWorkItemId' in body;\n const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : undefined;\n if (hasParentWorkItemId && parentWorkItemId === undefined) return null;\n const parsedSource = parseExternalSource(externalSource);\n if (externalSource !== undefined && parsedSource === undefined) return null;\n if (stages !== undefined && !validStages(stages)) return null;\n const parsedSessions = sessions === undefined ? undefined : parseSessions(sessions);\n if (sessions !== undefined && parsedSessions === undefined) return null;\n let parsedMetadata: Record<string, unknown> | null | undefined;\n if (metadata !== undefined) {\n if (!validMetadata(metadata)) return null;\n parsedMetadata = publicWorkItemMetadata(metadata);\n }\n\n return {\n title: title.trim(),\n ...(board !== undefined ? { board } : {}),\n ...(parsedSource !== undefined ? { externalSource: parsedSource } : {}),\n ...(hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {}),\n ...(stages !== undefined ? { stages } : {}),\n ...(parsedSessions !== undefined ? { sessions: parsedSessions } : {}),\n ...(parsedMetadata !== undefined ? { metadata: parsedMetadata } : {}),\n };\n}\n\n/** Validate an untrusted patch body. Unknown keys are dropped. */\nexport function parseUpdateWorkItem(body: unknown): UpdateWorkItemInput | null {\n if (!isRecord(body)) return null;\n const { board, title, stages, sessions, metadata, plansPreapproved } = body;\n const hasParentWorkItemId = 'parentWorkItemId' in body;\n if (\n board === undefined &&\n title === undefined &&\n stages === undefined &&\n sessions === undefined &&\n metadata === undefined &&\n plansPreapproved === undefined &&\n !hasParentWorkItemId\n )\n return null;\n if (board !== undefined && (typeof board !== 'string' || board.length === 0 || board.length > 128)) return null;\n if (plansPreapproved !== undefined && plansPreapproved !== true) return null;\n const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : undefined;\n if (hasParentWorkItemId && parentWorkItemId === undefined) return null;\n if (title !== undefined && (typeof title !== 'string' || title.trim().length === 0 || title.length > 500))\n return null;\n if (stages !== undefined && !validStages(stages)) return null;\n const parsedSessions = sessions === undefined ? undefined : parseSessions(sessions);\n if (sessions !== undefined && parsedSessions === undefined) return null;\n let parsedMetadata: Record<string, unknown> | null | undefined;\n if (metadata !== undefined) {\n if (!validMetadata(metadata)) return null;\n parsedMetadata = publicWorkItemMetadata(metadata);\n }\n\n return {\n ...(board !== undefined ? { board } : {}),\n ...(hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {}),\n ...(title !== undefined ? { title: title.trim() } : {}),\n ...(stages !== undefined ? { stages } : {}),\n ...(parsedSessions !== undefined ? { sessions: parsedSessions } : {}),\n ...(parsedMetadata !== undefined ? { metadata: parsedMetadata } : {}),\n ...(plansPreapproved === true ? { plansPreapproved: true } : {}),\n };\n}\n\nasync function readJson(c: Context): Promise<unknown | undefined> {\n try {\n return await c.req.json();\n } catch {\n return undefined;\n }\n}\n\n/** Fields a PATCH touched, for the bounded `updated` event summary. */\nfunction patchedFields(patch: Record<string, unknown>): string[] {\n return Object.keys(patch).filter(key => patch[key] !== undefined);\n}\n\nfunction boundedText(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const normalized = value.trim();\n return normalized.length > 0 && normalized.length <= max ? normalized : undefined;\n}\n\nfunction parseTransitionBody(\n body: unknown,\n): Omit<FactoryTransitionRequest, 'orgId' | 'factoryProjectId' | 'workItemId' | 'actor'> | null {\n if (!isRecord(body)) return null;\n const board = typeof body.board === 'string' && body.board.length > 0 ? (body.board as FactoryRuleBoard) : undefined;\n const stage = typeof body.stage === 'string' && body.stage.length > 0 ? (body.stage as FactoryRuleStage) : undefined;\n const requestId = boundedText(body.requestId, 256);\n const cause = boundedText(body.cause, 256);\n if (\n !board ||\n !stage ||\n !requestId ||\n !UUID_RE.test(requestId) ||\n !cause ||\n !Number.isInteger(body.expectedRevision) ||\n Number(body.expectedRevision) < 1\n ) {\n return null;\n }\n return {\n board,\n stage,\n expectedRevision: Number(body.expectedRevision),\n ingress: { type: 'human', identity: requestId },\n cause,\n ...(body.reenter === true ? { reenter: true } : {}),\n };\n}\n\nfunction parseStartBody(\n body: unknown,\n tenant: { orgId: string; userId: string },\n factoryProjectId: string,\n): FactoryStartRequest | null {\n if (!isRecord(body) || !isRecord(body.workItem)) return null;\n const input = parseCreateWorkItem(body.workItem.input);\n const sessionId = boundedText(body.sessionId, 256);\n const threadTitle = boundedText(body.threadTitle, 512);\n const kickoffKey = boundedText(body.kickoffKey, 256);\n const role = boundedText(body.workItem.role, 32);\n const id = boundedText(body.workItem.id, 64);\n if (\n !input ||\n !sessionId ||\n !UUID_RE.test(sessionId) ||\n !threadTitle ||\n !kickoffKey ||\n !UUID_RE.test(kickoffKey) ||\n !id ||\n !UUID_RE.test(id) ||\n !role\n ) {\n return null;\n }\n return { ...tenant, factoryProjectId, sessionId, threadTitle, kickoffKey, workItem: { id, role, input } };\n}\n\nconst DECISION_STATUSES = new Set<FactoryDispatchStatus>([\n 'pending',\n 'proposed',\n 'dismissed',\n 'superseded',\n 'leased',\n 'retry',\n 'succeeded',\n 'failed',\n]);\nconst DEFAULT_DECISION_PAGE_SIZE = 25;\nconst MAX_DECISION_PAGE_SIZE = 50;\n\nfunction parseDecisionStatuses(raw: string | undefined): FactoryDispatchStatus[] | undefined {\n if (!raw) return undefined;\n const statuses = [...new Set(raw.split(',').map(status => status.trim()))].filter(\n (status): status is FactoryDispatchStatus => DECISION_STATUSES.has(status as FactoryDispatchStatus),\n );\n return statuses.length > 0 ? statuses : undefined;\n}\n\nfunction parseDecisionLimit(raw: string | undefined): number {\n const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_DECISION_PAGE_SIZE;\n if (!Number.isFinite(parsed)) return DEFAULT_DECISION_PAGE_SIZE;\n return Math.max(1, Math.min(MAX_DECISION_PAGE_SIZE, parsed));\n}\n\nfunction encodeDecisionCursor(decision: FactoryDeferredDecisionRecord): string {\n return Buffer.from(JSON.stringify([decision.createdAt.toISOString(), decision.id]), 'utf8').toString('base64url');\n}\n\nfunction parseDecisionCursor(raw: string | undefined): { createdAt: Date; id: string } | undefined {\n if (!raw) return undefined;\n try {\n const decoded = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8')) as unknown;\n if (\n !Array.isArray(decoded) ||\n decoded.length !== 2 ||\n typeof decoded[0] !== 'string' ||\n typeof decoded[1] !== 'string'\n ) {\n return undefined;\n }\n const createdAt = new Date(decoded[0]);\n if (Number.isNaN(createdAt.getTime()) || !UUID_RE.test(decoded[1])) return undefined;\n return { createdAt, id: decoded[1] };\n } catch {\n return undefined;\n }\n}\n\n/** A proposed transition names the seat its lane addresses, so the card can label what approving starts. */\nfunction summaryRole(decision: Record<string, unknown>): string | null {\n if (typeof decision.role === 'string') return decision.role.slice(0, 32);\n if (decision.type !== 'transition') return null;\n const board = decision.board;\n const stage = decision.stage;\n if ((board !== 'work' && board !== 'review') || !isFactoryRuleStage(stage)) return null;\n return roleForStage(board, stage);\n}\n\n/** A linked-card decision names where the card is synced from, so the UI can say \"GitHub\" rather than \"a linked card\". */\nfunction summarySource(decision: Record<string, unknown>): WorkItemSource | null {\n if (decision.type !== 'upsertLinkedWorkItem') return null;\n const source = decision.source;\n return source === 'github-issue' || source === 'github-pr' || source === 'linear-issue' || source === 'manual'\n ? source\n : null;\n}\n\nfunction decisionSummary(decision: FactoryDeferredDecisionRecord) {\n return {\n id: decision.id,\n evaluationId: decision.evaluationId,\n workItemId: decision.workItemId,\n type: factoryDecisionType(decision),\n role: summaryRole(decision.decision),\n source: summarySource(decision.decision),\n status: decision.status,\n attempts: decision.attempts,\n failureOccurrence: decision.failureOccurrence,\n failureCode: decision.failureCode,\n canRetry: factoryDispatchFailureMetadata(decision.failureCode).canRetry,\n lastError: decision.lastError?.slice(0, 512) ?? null,\n createdAt: decision.createdAt.toISOString(),\n updatedAt: decision.updatedAt.toISOString(),\n completedAt: decision.completedAt?.toISOString() ?? null,\n };\n}\n\nexport class WorkItemRoutes extends Route<WorkItemRoutesDeps> {\n /** Resolve the `(orgId, userId)` tenant or a ready-to-return error response. */\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'The Factory board requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n /**\n * Resolve the tenant AND the org-owned project from the `:id` param. Work\n * items hang off a project, so listing/creating requires the project to\n * exist in the caller's org.\n */\n async #resolveProject(\n c: Context,\n ): Promise<\n { orgId: string; userId: string; factoryProjectId: string; defaultModelId: string | null } | { response: Response }\n > {\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n const { projects } = this.deps;\n await projects.ensureReady();\n const project = await projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n return { ...tenant, factoryProjectId: projectId, defaultModelId: project.defaultModelId };\n }\n\n /**\n * Emit the audit events a successful work-item PATCH implies: always\n * `updated`, plus `stage_moved` when the stages actually changed and one\n * `run.started` per session role the patch introduced.\n */\n async #auditWorkItemPatch({\n context,\n item,\n previous,\n patch,\n }: {\n context: Context;\n item: WorkItemRow;\n previous: WorkItemPriorState;\n patch: Record<string, unknown>;\n }): Promise<void> {\n const { audit } = this.deps;\n const target = { type: 'work_item', id: item.id, name: item.title };\n await audit.emit({\n context,\n input: {\n action: 'factory.work_item.updated',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: { fields: patchedFields(patch) },\n },\n });\n\n const stagesChanged =\n patch.stages !== undefined &&\n (previous.stages.length !== item.stages.length || previous.stages.some((s, i) => s !== item.stages[i]));\n if (stagesChanged) {\n await audit.emit({\n context,\n input: {\n action: 'factory.work_item.stage_moved',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: { from: previous.stages, to: item.stages },\n },\n });\n }\n\n const newRoles = Object.keys(item.sessions).filter(role => !previous.sessionRoles.includes(role));\n for (const role of newRoles) {\n const session = item.sessions[role];\n await audit.emit({\n context,\n input: {\n action: 'factory.run.started',\n factoryProjectId: item.factoryProjectId,\n targets: [target],\n metadata: {\n role,\n branch: session?.branch,\n threadId: session?.threadId,\n sessionId: session?.sessionId,\n },\n },\n });\n }\n }\n\n /** Releasing a parked run and dropping it: same request, opposite outcomes, both audited as consent. */\n #proposalRoute({\n verb,\n settle,\n }: {\n verb: 'approve' | 'dismiss';\n settle: (\n orgId: string,\n factoryProjectId: string,\n decisionId: string,\n now: Date,\n userId: string,\n ) => Promise<FactoryDeferredDecisionRecord | null>;\n }): ApiRoute {\n const { audit, workItems } = this.deps;\n return registerApiRoute(`/web/factory/projects/:id/decisions/:decisionId/${verb}`, {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n if (!decisionId || !UUID_RE.test(decisionId)) return c.json({ error: 'invalid_decision_id' }, 422);\n await workItems.ensureReady();\n const now = new Date();\n const decision = await settle(resolved.orgId, resolved.factoryProjectId, decisionId, now, resolved.userId);\n if (!decision) return c.json({ error: 'decision_not_proposed' }, 409);\n // Releasing a proposal is a person taking the item on. Approval arms the\n // item's autonomy inside the same storage transaction (see\n // approveDeferredDecision), so follow-up runs no longer wait for approval.\n await audit.emit({\n context,\n input: {\n action: verb === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n factoryProjectId: resolved.factoryProjectId,\n targets: decision.workItemId\n ? [{ type: 'work_item', id: decision.workItemId }]\n : [{ type: 'rule_decision', id: decision.id }],\n metadata: { decisionId: decision.id, effect: factoryDecisionType(decision) },\n },\n });\n return c.json({ decision: decisionSummary(decision) });\n },\n });\n }\n\n /** Build the Factory work-item routes as Mastra `apiRoutes`. */\n routes(): ApiRoute[] {\n const { audit, workItems, queueHealth, transitionService, startCoordinator, liveSessions } = this.deps;\n return [\n // ── List the org's work items for a project, and which are being worked ─\n registerApiRoute('/web/factory/projects/:id/work-items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n await workItems.ensureReady();\n const items = await workItems.list({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n });\n return c.json({\n workItems: items.map(toWireWorkItem),\n runningSessionIds: runningSessionIds(items, liveSessions),\n });\n },\n }),\n\n // ── Flow metrics aggregated over the project's work items ───────────────\n registerApiRoute('/web/factory/projects/:id/metrics', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n const { windowStart, windowEnd } = parseMetricsRange(\n loose(c).req.query('from'),\n loose(c).req.query('to'),\n new Date(),\n );\n await workItems.ensureReady();\n const items = await workItems.list({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n });\n return c.json({ metrics: computeFactoryMetrics(items, { windowStart, windowEnd }) });\n },\n }),\n\n // ── Per-project queue-health age-threshold config (seconds) ─────────────\n registerApiRoute('/web/factory/projects/:id/health/thresholds', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n await queueHealth.ensureReady();\n const stored = await queueHealth.getConfig(resolved.orgId, resolved.factoryProjectId);\n // Validate at the read choke point: `getConfig` round-trips a stored\n // JSONB row, and only `saveConfig` validates on write — a corrupted or\n // hand-edited row (empty / non-ascending) would otherwise flow to the\n // chart and invert bucket colors. Fall back to the default on invalid.\n return c.json({ thresholds: thresholdsOrDefault(stored) });\n },\n }),\n\n // ── Bounded durable rule-decision status ────────────────────────────────\n registerApiRoute('/web/factory/projects/:id/decisions', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n\n const cursorRaw = context.req.query('before');\n const before = parseDecisionCursor(cursorRaw);\n if (cursorRaw && !before) return c.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n const page = await workItems.listDeferredDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: parseDecisionStatuses(context.req.query('statuses')),\n before,\n limit: parseDecisionLimit(context.req.query('limit')),\n });\n const last = page.decisions.at(-1);\n return c.json({\n decisions: page.decisions.map(decisionSummary),\n ...(page.hasMore && last ? { nextCursor: encodeDecisionCursor(last) } : {}),\n });\n },\n }),\n\n ...buildAttentionRoutes({\n workItems,\n comments: this.deps.comments,\n resolveProject: context => this.#resolveProject(loose(context)),\n }),\n\n ...buildSupervisorRoutes({\n workItems,\n resolveProject: context => this.#resolveProject(loose(context)),\n }),\n\n this.#proposalRoute({ verb: 'approve', settle: workItems.approveDeferredDecision.bind(workItems) }),\n this.#proposalRoute({ verb: 'dismiss', settle: workItems.dismissDeferredDecision.bind(workItems) }),\n\n registerApiRoute('/web/factory/projects/:id/decisions/:decisionId/retry', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const context = loose(c);\n const resolved = await this.#resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n if (!decisionId || !UUID_RE.test(decisionId)) return c.json({ error: 'invalid_decision_id' }, 422);\n await workItems.ensureReady();\n const current = await workItems.getDeferredDecision(resolved.orgId, resolved.factoryProjectId, decisionId);\n if (\n !current ||\n current.status !== 'failed' ||\n !factoryDispatchFailureMetadata(current.failureCode).canRetry\n ) {\n return c.json({ error: 'decision_not_retryable' }, 409);\n }\n const decision = await workItems.retryDeferredDecision(\n resolved.orgId,\n resolved.factoryProjectId,\n decisionId,\n new Date(),\n );\n if (!decision) return c.json({ error: 'decision_not_retryable' }, 409);\n return c.json({ decision: decisionSummary(decision) });\n },\n }),\n\n // ── Create (upsert on sourceKey) a work item ─────────────────────────────\n registerApiRoute('/web/factory/projects/:id/work-items', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n\n const body = await readJson(loose(c));\n if (body === undefined) return c.json({ error: 'Invalid JSON body' }, 400);\n const input = parseCreateWorkItem(body);\n if (!input) return c.json({ error: 'invalid_work_item' }, 400);\n const boardId = input.board ?? (input.externalSource?.type === 'pull-request' ? 'review' : 'work');\n const board = (this.deps.boardRegistry ?? createBoardRegistry()).get(boardId);\n if (!board) return c.json({ error: 'invalid_board', message: `Board '${boardId}' is not installed.` }, 422);\n const initialStages = input.stages ?? [board.initialPhase];\n if (initialStages.length !== 1 || initialStages[0] !== board.initialPhase) {\n return c.json(\n {\n error: 'governed_transition_required',\n message: `New work items must enter through the '${board.initialPhase}' phase of board '${boardId}'.`,\n },\n 409,\n );\n }\n\n await workItems.ensureReady();\n try {\n const result = await workItems.upsert({\n orgId: resolved.orgId,\n userId: resolved.userId,\n factoryProjectId: resolved.factoryProjectId,\n input: { ...input, board: boardId, stages: initialStages },\n reuseMode: 'non-stage',\n });\n let item = result.item;\n if (result.created) {\n if (!transitionService) {\n await workItems.delete({ orgId: resolved.orgId, id: item.id });\n return c.json({ error: 'factory_transitions_unavailable' }, 503);\n }\n const entered = await transitionService.transition({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n workItemId: item.id,\n board: boardId,\n stage: board.initialPhase,\n expectedRevision: item.revision,\n actor: { type: 'human', id: resolved.userId },\n ingress: { type: 'human', identity: `work-item:${item.id}:initial-entry` },\n cause: 'work_item_created',\n initialEntry: true,\n });\n if (entered.status === 'rejected') {\n await workItems.delete({ orgId: resolved.orgId, id: item.id });\n return c.json({ status: 'rejected', code: entered.code, reason: entered.reason }, 422);\n }\n item = (await workItems.getForProject(resolved.orgId, resolved.factoryProjectId, item.id)) ?? item;\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.work_item.created',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: item.id, name: item.title }],\n metadata: { externalSource: item.externalSource, stages: item.stages },\n },\n });\n } else {\n // Source-key reuse: the POST updated an existing card, so audit it\n // as an update (plus stage/run events) instead of a false creation.\n const { stages: _stages, sessions: _sessions, ...boundedPatch } = input;\n await this.#auditWorkItemPatch({\n context: loose(c),\n item,\n previous: result.previous,\n patch: boundedPatch as unknown as Record<string, unknown>,\n });\n }\n return c.json({ workItem: toWireWorkItem(item) });\n } catch (error) {\n if (error instanceof WorkItemRelationError) {\n return c.json({ error: error.code, message: error.message }, 400);\n }\n throw error;\n }\n },\n }),\n\n // ── Authoritative stage transition ──────────────────────────────────────\n registerApiRoute('/web/factory/projects/:id/work-items/:workItemId/transition', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n const workItemId = loose(c).req.param('workItemId');\n if (!workItemId || !UUID_RE.test(workItemId)) return c.json({ error: 'Work item not found' }, 404);\n const parsed = parseTransitionBody(await readJson(loose(c)));\n if (!parsed) return c.json({ error: 'invalid_transition_request' }, 400);\n if (!transitionService) {\n return c.json({ error: 'factory_transition_unavailable' }, 503);\n }\n await workItems.ensureReady();\n const result = await transitionService.transition({\n ...parsed,\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n workItemId,\n actor: { type: 'human', id: resolved.userId },\n ingress: {\n ...parsed.ingress,\n identity: `human:${resolved.userId}:${parsed.ingress.identity}`,\n },\n });\n await audit.emit({\n context: loose(c),\n input: {\n action:\n result.status === 'accepted'\n ? 'factory.work_item.stage_moved'\n : 'factory.work_item.transition_rejected',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: workItemId }],\n metadata: {\n transitionId: result.transitionId,\n ingressType: parsed.ingress.type,\n ruleSetVersion: transitionService.ruleSetVersion,\n ...(result.status === 'accepted'\n ? { to: result.stage, revision: result.revision }\n : { code: result.code, reason: result.reason }),\n },\n },\n });\n if (result.status === 'accepted') return c.json({ result });\n return c.json({ result }, result.code === 'stale' ? 409 : 422);\n },\n }),\n\n // ── Bind a Factory run before dispatching its kickoff ────────────────────\n registerApiRoute('/web/factory/projects/:id/runs/start', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveProject(loose(c));\n if ('response' in resolved) return resolved.response;\n if (!startCoordinator) {\n return c.json({ error: 'factory_start_unavailable' }, 503);\n }\n const input = parseStartBody(await readJson(loose(c)), resolved, resolved.factoryProjectId);\n if (!input) return c.json({ error: 'invalid_factory_start' }, 400);\n input.requestContext = loose(c).get('requestContext');\n input.defaultModelId = resolved.defaultModelId ?? undefined;\n await workItems.ensureReady();\n let prepared: FactoryStartPreparedResult;\n try {\n prepared = await startCoordinator.prepare(input);\n } catch (error) {\n if (error instanceof FactoryStartTransitionError) {\n return c.json({ result: error.result }, error.result.code === 'stale' ? 409 : 422);\n }\n throw error;\n }\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.run.started',\n factoryProjectId: resolved.factoryProjectId,\n targets: [{ type: 'work_item', id: prepared.workItemId }],\n metadata: {\n role: input.workItem.role,\n branch: prepared.branch,\n threadId: prepared.threadId,\n sessionId: prepared.sessionId,\n bindingId: prepared.bindingId,\n },\n },\n });\n return c.json({ prepared }, 202);\n },\n }),\n\n // ── Patch non-stage metadata / sessions / title ──────────────────────────\n registerApiRoute('/web/factory/work-items/:id', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n const id = loose(c).req.param('id');\n if (!id || !UUID_RE.test(id)) return c.json({ error: 'Work item not found' }, 404);\n\n const body = await readJson(loose(c));\n if (body === undefined) return c.json({ error: 'Invalid JSON body' }, 400);\n const patch = parseUpdateWorkItem(body);\n if (!patch) return c.json({ error: 'invalid_work_item_patch' }, 400);\n\n await workItems.ensureReady();\n const existing = await workItems.get({ orgId: tenant.orgId, id });\n if (!existing) return c.json({ error: 'Work item not found' }, 404);\n if (patch.board !== undefined) {\n const board = (this.deps.boardRegistry ?? createBoardRegistry()).get(patch.board);\n const stage = patch.stages?.length === 1 ? patch.stages[0] : undefined;\n if (existing.board !== null) {\n return c.json({ error: 'board_already_assigned' }, 409);\n }\n if (!board) {\n return c.json({ error: 'board_not_installed' }, 400);\n }\n if (!stage || !Object.prototype.hasOwnProperty.call(board.phases, stage)) {\n return c.json({ error: 'invalid_board_migration_phase' }, 400);\n }\n } else if (patch.stages !== undefined) {\n return c.json(\n { error: 'governed_transition_required', message: 'Use the Factory transition endpoint to move stages.' },\n 409,\n );\n }\n\n try {\n const updated = await workItems.update({\n orgId: tenant.orgId,\n id,\n userId: tenant.userId,\n patch,\n ...(patch.board !== undefined ? { expectedRevision: existing.revision, expectedBoard: null } : {}),\n });\n if (!updated) return c.json({ error: 'Work item not found' }, 404);\n await this.#auditWorkItemPatch({\n context: loose(c),\n item: updated.item,\n previous: updated.previous,\n patch: patch as Record<string, unknown>,\n });\n return c.json({ workItem: toWireWorkItem(updated.item) });\n } catch (error) {\n if (error instanceof WorkItemRelationError) {\n return c.json({ error: error.code, message: error.message }, 400);\n }\n if (error instanceof WorkItemUpdateConflictError) {\n return c.json({ error: error.reason === 'board' ? 'board_already_assigned' : error.code }, 409);\n }\n throw error;\n }\n },\n }),\n\n // ── Remove a work item ───────────────────────────────────────────────────\n registerApiRoute('/web/factory/work-items/:id', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n const id = loose(c).req.param('id');\n if (!id || !UUID_RE.test(id)) return c.json({ error: 'Work item not found' }, 404);\n\n await workItems.ensureReady();\n const deleted = await workItems.delete({ orgId: tenant.orgId, id });\n if (!deleted) return c.json({ error: 'Work item not found' }, 404);\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.work_item.deleted',\n factoryProjectId: deleted.factoryProjectId,\n targets: [{ type: 'work_item', id: deleted.id, name: deleted.title }],\n },\n });\n return c.json({ ok: true });\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/rules/defaults.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/rules/defaults.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,YAAY,EACZ,qBAAqB,EAKtB,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAoJjE,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,qBAAqB,EAC3B,SAAS,GAAE,qBAA0B,GACpC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAO/B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,qBAAqB,CAAA;CAAE,GAAG,YAAY,CAW/G;AAED,wBAAgB,mBAAmB,IAAI,YAAY,CAElD"}
|
package/dist/rules/defaults.js
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
import { FactoryRuleValidationError, assertFactoryRules } from "./validation.js";
|
|
2
1
|
import { reviewBoard } from "../boards/review.js";
|
|
3
2
|
import { workBoard } from "../boards/work.js";
|
|
3
|
+
import { FactoryRuleValidationError, assertFactoryRules } from "./validation.js";
|
|
4
4
|
//#region src/rules/defaults.ts
|
|
5
5
|
const DEFAULT_FACTORY_RULE_VERSION = "factory-default-v1";
|
|
6
|
-
function trustedGithubActor(context) {
|
|
7
|
-
return context.actor.type === "github" && context.actor.trusted;
|
|
8
|
-
}
|
|
9
|
-
function githubActorLogin(context) {
|
|
10
|
-
return context.actor.type === "github" ? context.actor.login : void 0;
|
|
11
|
-
}
|
|
12
|
-
function retriageGithubIssue(context) {
|
|
13
|
-
if (!context.item || context.item.source !== "github-issue" || !context.item.url) return;
|
|
14
|
-
if (context.actor.type === "github" && context.actor.factoryAuthored) return;
|
|
15
|
-
const reason = context.event === "issueEdited" ? context.issueChange?.title && context.issueChange.body ? "issue title and body edited" : context.issueChange?.title ? "issue title edited" : "issue body edited" : context.event === "issueCommentDeleted" ? "comment deleted" : context.event === "issueCommentEdited" ? "comment edited" : "comment created";
|
|
16
|
-
return {
|
|
17
|
-
type: "invokeSkill",
|
|
18
|
-
idempotencyKey: `${context.ingress.id}:factory-triage`,
|
|
19
|
-
role: "triage",
|
|
20
|
-
skillName: "factory-triage",
|
|
21
|
-
arguments: `Re-triage GitHub issue (${context.item.url}) after ${reason}.`
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
6
|
function resultContent(value) {
|
|
25
7
|
if (typeof value === "string") return value;
|
|
26
8
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
@@ -36,187 +18,6 @@ function advanceApprovedPlan(context) {
|
|
|
36
18
|
stage: "execute"
|
|
37
19
|
};
|
|
38
20
|
}
|
|
39
|
-
function createdAfterFactory(createdAt, factoryCreatedAt) {
|
|
40
|
-
if (!createdAt) return false;
|
|
41
|
-
const sourceCreatedAt = Date.parse(createdAt);
|
|
42
|
-
const projectCreatedAt = Date.parse(factoryCreatedAt);
|
|
43
|
-
return Number.isFinite(sourceCreatedAt) && Number.isFinite(projectCreatedAt) && sourceCreatedAt > projectCreatedAt;
|
|
44
|
-
}
|
|
45
|
-
function issueOpened(context) {
|
|
46
|
-
if (!context.issue) return;
|
|
47
|
-
return {
|
|
48
|
-
type: "upsertLinkedWorkItem",
|
|
49
|
-
idempotencyKey: `${context.ingress.id}:issue-intake`,
|
|
50
|
-
board: "work",
|
|
51
|
-
source: "github-issue",
|
|
52
|
-
sourceKey: `github-issue:${context.issue.number}`,
|
|
53
|
-
title: context.issue.title,
|
|
54
|
-
url: context.issue.url,
|
|
55
|
-
stage: "intake",
|
|
56
|
-
metadata: {
|
|
57
|
-
githubRepositoryId: context.repository.id,
|
|
58
|
-
githubIssueNumber: context.issue.number,
|
|
59
|
-
...context.issue.createdAt ? { sourceCreatedAt: context.issue.createdAt } : {},
|
|
60
|
-
...githubActorLogin(context) ? { author: githubActorLogin(context) } : {},
|
|
61
|
-
authorTrusted: trustedGithubActor(context),
|
|
62
|
-
autoStartCandidate: trustedGithubActor(context) && createdAfterFactory(context.issue.createdAt, context.factory.createdAt),
|
|
63
|
-
assignees: context.issue.assignees ?? [],
|
|
64
|
-
labels: context.issue.labels ?? []
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function issueClosed(context) {
|
|
69
|
-
if (!context.item || context.item.source !== "github-issue" || !context.issue) return;
|
|
70
|
-
if (context.board !== "work") return;
|
|
71
|
-
if (context.item.stages.some((stage) => stage === "done" || stage === "canceled")) return;
|
|
72
|
-
const canceled = context.issue.stateReason === "not_planned" || context.issue.stateReason === "duplicate";
|
|
73
|
-
return {
|
|
74
|
-
type: "transition",
|
|
75
|
-
idempotencyKey: `${context.ingress.id}:issue-closed`,
|
|
76
|
-
board: "work",
|
|
77
|
-
stage: canceled ? "canceled" : "done",
|
|
78
|
-
message: { text: `GitHub issue #${context.issue.number} was closed${context.issue.stateReason ? ` (${context.issue.stateReason})` : ""}; this Work card was moved to ${canceled ? "Canceled" : "Done"}.` }
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function materializePullRequestIntake(context, { idempotencyKey, autoStartCandidate }) {
|
|
82
|
-
if (!context.pullRequest) return;
|
|
83
|
-
return {
|
|
84
|
-
type: "upsertLinkedWorkItem",
|
|
85
|
-
idempotencyKey,
|
|
86
|
-
board: "review",
|
|
87
|
-
source: "github-pr",
|
|
88
|
-
sourceKey: `github-pr:${context.pullRequest.number}`,
|
|
89
|
-
title: context.pullRequest.title,
|
|
90
|
-
url: context.pullRequest.url,
|
|
91
|
-
stage: "intake",
|
|
92
|
-
metadata: {
|
|
93
|
-
githubRepositoryId: context.repository.id,
|
|
94
|
-
githubPullRequestNumber: context.pullRequest.number,
|
|
95
|
-
...context.pullRequest.createdAt ? { sourceCreatedAt: context.pullRequest.createdAt } : {},
|
|
96
|
-
factoryAuthored: context.pullRequest.factoryAuthored,
|
|
97
|
-
authorTrusted: trustedGithubActor(context),
|
|
98
|
-
autoStartCandidate,
|
|
99
|
-
state: context.pullRequest.state,
|
|
100
|
-
draft: context.pullRequest.draft,
|
|
101
|
-
merged: context.pullRequest.merged,
|
|
102
|
-
assignees: context.pullRequest.assignees ?? [],
|
|
103
|
-
requestedReviewers: context.pullRequest.requestedReviewers ?? [],
|
|
104
|
-
labels: context.pullRequest.labels ?? [],
|
|
105
|
-
headBranch: context.pullRequest.headBranch,
|
|
106
|
-
baseBranch: context.pullRequest.baseBranch,
|
|
107
|
-
...context.pullRequest.author ? { author: context.pullRequest.author } : {}
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function pullRequestOpened(context) {
|
|
112
|
-
if (!context.pullRequest) return;
|
|
113
|
-
const autoStartCandidate = (trustedGithubActor(context) || context.pullRequest.factoryAuthored) && createdAfterFactory(context.pullRequest.createdAt, context.factory.createdAt);
|
|
114
|
-
return materializePullRequestIntake(context, {
|
|
115
|
-
idempotencyKey: `${context.ingress.id}:pull-request-intake`,
|
|
116
|
-
autoStartCandidate
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
function pullRequestMerged(context) {
|
|
120
|
-
if (!context.item || !context.pullRequest?.merged) return;
|
|
121
|
-
if (context.board === "review") return {
|
|
122
|
-
type: "transition",
|
|
123
|
-
idempotencyKey: `${context.ingress.id}:pull-request-merged`,
|
|
124
|
-
board: "review",
|
|
125
|
-
stage: "done",
|
|
126
|
-
message: { text: `Pull request #${context.pullRequest.number} merged; this Review card was moved to Done. No further review is needed unless follow-up work was requested.` }
|
|
127
|
-
};
|
|
128
|
-
return {
|
|
129
|
-
type: "sendMessage",
|
|
130
|
-
idempotencyKey: `${context.ingress.id}:assess-work-completion`,
|
|
131
|
-
role: "work",
|
|
132
|
-
message: `Pull request #${context.pullRequest.number} merged. Assess whether the linked Work item is complete. Do not mark it Done solely because this PR merged; use factory_transition_work_item only after verifying the work.`
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function addressReviewFeedback(context) {
|
|
136
|
-
if (!context.item || !context.pullRequest || !context.review) return;
|
|
137
|
-
if (context.board !== "work") return;
|
|
138
|
-
if (context.pullRequest.state !== "open" || context.pullRequest.merged) return;
|
|
139
|
-
if (context.review.state.toLowerCase() !== "changes_requested") return;
|
|
140
|
-
return {
|
|
141
|
-
type: "sendMessage",
|
|
142
|
-
idempotencyKey: `${context.ingress.id}:address-review-feedback`,
|
|
143
|
-
role: "work",
|
|
144
|
-
priority: "high",
|
|
145
|
-
message: `Changes were requested on pull request #${context.pullRequest.number} (${context.review.url}). Read the review comments on this PR, address the ones you agree with, and push the fixes to the PR branch. Reply on GitHub to anything you are deliberately not changing, explaining why.`
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Detects the `factory-review` handoff verdict in a comment body.
|
|
150
|
-
*
|
|
151
|
-
* GitHub forbids an app from reviewing a pull request it authored, so on
|
|
152
|
-
* Factory-authored PRs the review skill falls back to posting its verdict as a
|
|
153
|
-
* plain comment. That comment is the only signal the authoring agent gets, so
|
|
154
|
-
* it has to be readable back out. The skill's handoff contract puts the verdict
|
|
155
|
-
* on the first line (`Verdict: request changes`), so only that line is
|
|
156
|
-
* inspected — a verdict quoted later in the findings must not count.
|
|
157
|
-
*/
|
|
158
|
-
function requestsChangesVerdict(body) {
|
|
159
|
-
const firstLine = body?.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
|
|
160
|
-
if (!firstLine) return false;
|
|
161
|
-
const normalized = firstLine.replaceAll(/[*_`#>\s]+/g, " ").trim().toLowerCase();
|
|
162
|
-
return /^verdict: ?(request changes|changes requested)$/.test(normalized);
|
|
163
|
-
}
|
|
164
|
-
function addressPullRequestComment(context) {
|
|
165
|
-
if (context.reviewCommand) return reReviewRequestedPullRequest(context);
|
|
166
|
-
if (!context.item || !context.pullRequest || !context.issueComment) return;
|
|
167
|
-
if (context.board !== "work") return;
|
|
168
|
-
if (context.pullRequest.state !== "open" || context.pullRequest.merged) return;
|
|
169
|
-
if (context.actor.type === "github" && context.actor.factoryAuthored && !requestsChangesVerdict(context.issueComment.body)) return;
|
|
170
|
-
return {
|
|
171
|
-
type: "sendMessage",
|
|
172
|
-
idempotencyKey: `${context.ingress.id}:address-pull-request-comment`,
|
|
173
|
-
role: "work",
|
|
174
|
-
priority: "high",
|
|
175
|
-
message: `${context.issueComment.author ?? "Someone"} commented on pull request #${context.pullRequest.number} (${context.issueComment.url ?? context.pullRequest.url}). Read the comment, address it if you agree, and push the fixes to the PR branch. Reply on GitHub to anything you are deliberately not changing, explaining why.`
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function pullRequestClosed(context) {
|
|
179
|
-
if (!context.item || !context.pullRequest || context.pullRequest.merged) return;
|
|
180
|
-
if (context.board !== "review") return;
|
|
181
|
-
return {
|
|
182
|
-
type: "transition",
|
|
183
|
-
idempotencyKey: `${context.ingress.id}:pull-request-closed`,
|
|
184
|
-
board: "review",
|
|
185
|
-
stage: "canceled",
|
|
186
|
-
message: { text: `Pull request #${context.pullRequest.number} was closed without merging; this Review card was moved to Canceled.` }
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function reReviewRequestedPullRequest(context) {
|
|
190
|
-
const factoryReviewEntry = context.reviewRequest?.factoryReviewer || context.reviewCommand !== void 0;
|
|
191
|
-
if (context.item && context.board !== "review" || !factoryReviewEntry) return;
|
|
192
|
-
if (!context.pullRequest || context.pullRequest.state !== "open" || context.pullRequest.merged) return;
|
|
193
|
-
if (!trustedGithubActor(context)) return;
|
|
194
|
-
if (context.actor.type === "github" && context.actor.factoryAuthored) return;
|
|
195
|
-
if (!context.item) return materializePullRequestIntake(context, {
|
|
196
|
-
idempotencyKey: `${context.ingress.id}:pull-request-review-requested-intake`,
|
|
197
|
-
autoStartCandidate: true
|
|
198
|
-
});
|
|
199
|
-
if (context.item.stages.length === 1 && context.item.stages[0] === "review") return;
|
|
200
|
-
return {
|
|
201
|
-
type: "transition",
|
|
202
|
-
idempotencyKey: `${context.ingress.id}:re-review-requested`,
|
|
203
|
-
board: "review",
|
|
204
|
-
stage: "review"
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function reReviewUpdatedPullRequest(context) {
|
|
208
|
-
if (!context.item || context.board !== "review") return;
|
|
209
|
-
if (!context.pullRequest || context.pullRequest.state !== "open" || context.pullRequest.merged) return;
|
|
210
|
-
if (context.item.stages.some((stage) => stage === "intake")) return;
|
|
211
|
-
const alreadyReviewing = context.item.stages.some((stage) => stage === "review");
|
|
212
|
-
return {
|
|
213
|
-
type: "transition",
|
|
214
|
-
idempotencyKey: `${context.ingress.id}:re-review-updated`,
|
|
215
|
-
board: "review",
|
|
216
|
-
stage: "review",
|
|
217
|
-
...alreadyReviewing ? { reenter: true } : {}
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
21
|
function linearIssueObserved(context) {
|
|
221
22
|
if (context.item) return;
|
|
222
23
|
return {
|
|
@@ -266,21 +67,6 @@ const BUILT_IN_DEFAULTS = {
|
|
|
266
67
|
work: workBoard.rules,
|
|
267
68
|
review: reviewBoard.rules,
|
|
268
69
|
tools: { submit_plan: { onResult: advanceApprovedPlan } },
|
|
269
|
-
github: {
|
|
270
|
-
issueOpened: { onEvent: issueOpened },
|
|
271
|
-
issueEdited: { onEvent: retriageGithubIssue },
|
|
272
|
-
issueClosed: { onEvent: issueClosed },
|
|
273
|
-
issueCommentCreated: { onEvent: retriageGithubIssue },
|
|
274
|
-
issueCommentEdited: { onEvent: retriageGithubIssue },
|
|
275
|
-
issueCommentDeleted: { onEvent: retriageGithubIssue },
|
|
276
|
-
pullRequestOpened: { onEvent: pullRequestOpened },
|
|
277
|
-
pullRequestUpdated: { onEvent: reReviewUpdatedPullRequest },
|
|
278
|
-
pullRequestCommentCreated: { onEvent: addressPullRequestComment },
|
|
279
|
-
pullRequestReviewRequested: { onEvent: reReviewRequestedPullRequest },
|
|
280
|
-
pullRequestReviewSubmitted: { onEvent: addressReviewFeedback },
|
|
281
|
-
pullRequestMerged: { onEvent: pullRequestMerged },
|
|
282
|
-
pullRequestClosed: { onEvent: pullRequestClosed }
|
|
283
|
-
},
|
|
284
70
|
linear: {
|
|
285
71
|
issueObserved: { onEvent: linearIssueObserved },
|
|
286
72
|
issueClosed: { onEvent: linearIssueClosed }
|
|
@@ -320,19 +106,6 @@ function mergeToolRules(base, overrides) {
|
|
|
320
106
|
}
|
|
321
107
|
return result;
|
|
322
108
|
}
|
|
323
|
-
function mergeGithubRules(base, overrides) {
|
|
324
|
-
const result = {};
|
|
325
|
-
const events = /* @__PURE__ */ new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]);
|
|
326
|
-
for (const event of events) {
|
|
327
|
-
const baseLeaf = base?.[event];
|
|
328
|
-
const overrideLeaf = overrides?.[event];
|
|
329
|
-
result[event] = {
|
|
330
|
-
...baseLeaf && "onEvent" in baseLeaf ? { onEvent: baseLeaf.onEvent } : {},
|
|
331
|
-
...overrideLeaf && "onEvent" in overrideLeaf ? { onEvent: overrideLeaf.onEvent } : {}
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
return result;
|
|
335
|
-
}
|
|
336
109
|
function mergeLinearRules(base, overrides) {
|
|
337
110
|
const result = {};
|
|
338
111
|
const events = /* @__PURE__ */ new Set([...Object.keys(base ?? {}), ...Object.keys(overrides ?? {})]);
|
|
@@ -351,7 +124,6 @@ function mergeFactoryRuleOverrides(base, overrides = {}) {
|
|
|
351
124
|
work: mergeBoardRules(base.work, overrides.work),
|
|
352
125
|
review: mergeBoardRules(base.review, overrides.review),
|
|
353
126
|
tools: mergeToolRules(base.tools, overrides.tools),
|
|
354
|
-
github: mergeGithubRules(base.github, overrides.github),
|
|
355
127
|
linear: mergeLinearRules(base.linear, overrides.linear)
|
|
356
128
|
};
|
|
357
129
|
}
|