@vercel/factory 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.mts +225 -0
  3. package/dist/api.mjs +312 -0
  4. package/dist/api.mjs.map +1 -0
  5. package/dist/approval-JX2Px9IC.mjs +132 -0
  6. package/dist/approval-JX2Px9IC.mjs.map +1 -0
  7. package/dist/blob/index.d.mts +4 -3
  8. package/dist/blob/index.mjs +21 -15
  9. package/dist/blob/index.mjs.map +1 -1
  10. package/dist/budget-DzSiIA4t.mjs +42 -0
  11. package/dist/budget-DzSiIA4t.mjs.map +1 -0
  12. package/dist/build-factory.mjs +1 -1
  13. package/dist/{change-CzEAoFVE.mjs → change-D2U6v7Ek.mjs} +10 -76
  14. package/dist/change-D2U6v7Ek.mjs.map +1 -0
  15. package/dist/communication-Kd9nU5RP.mjs +30 -0
  16. package/dist/communication-Kd9nU5RP.mjs.map +1 -0
  17. package/dist/connectors/index.d.mts +3 -2
  18. package/dist/connectors/index.mjs +53 -26
  19. package/dist/connectors/index.mjs.map +1 -1
  20. package/dist/connectors/slack.d.mts +13 -2
  21. package/dist/connectors/slack.mjs +18 -2
  22. package/dist/connectors/slack.mjs.map +1 -1
  23. package/dist/coordination-Cq08hb1l.mjs +76 -0
  24. package/dist/coordination-Cq08hb1l.mjs.map +1 -0
  25. package/dist/dispatch-BaZS3PT8.d.mts +76 -0
  26. package/dist/{driver-BJutB0PE.d.mts → driver-BC0Mt5KG.d.mts} +2 -2
  27. package/dist/driver-Bbd25rNM.mjs.map +1 -1
  28. package/dist/{dispatch-B77cuPxh.d.mts → engine-DFHU2Pe-.d.mts} +356 -288
  29. package/dist/engine-WaUM2cGA.mjs +1068 -0
  30. package/dist/engine-WaUM2cGA.mjs.map +1 -0
  31. package/dist/eve/index.d.mts +44 -334
  32. package/dist/eve/index.mjs +51 -38
  33. package/dist/eve/index.mjs.map +1 -1
  34. package/dist/{factory-config-BJcjsPKl.mjs → factory-config-BoRXOw43.mjs} +5 -41
  35. package/dist/factory-config-BoRXOw43.mjs.map +1 -0
  36. package/dist/{github-DXsxboUL.d.mts → github-C5HNBN6p.d.mts} +11 -3
  37. package/dist/github.d.mts +2 -2
  38. package/dist/github.mjs +15 -1
  39. package/dist/github.mjs.map +1 -1
  40. package/dist/{id-DwCihQr4.mjs → id-BLIkCVue.mjs} +4 -2
  41. package/dist/{id-DwCihQr4.mjs.map → id-BLIkCVue.mjs.map} +1 -1
  42. package/dist/index-Bw6hggoH.d.mts +40 -0
  43. package/dist/index.d.mts +176 -6
  44. package/dist/index.mjs +50 -652
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/postgres/index.d.mts +1 -1
  47. package/dist/{approval-CwhTP1XB.mjs → task-CcOyOJ3k.mjs} +18 -133
  48. package/dist/task-CcOyOJ3k.mjs.map +1 -0
  49. package/package.json +5 -1
  50. package/dist/approval-CwhTP1XB.mjs.map +0 -1
  51. package/dist/change-CzEAoFVE.mjs.map +0 -1
  52. package/dist/factory-config-BJcjsPKl.mjs.map +0 -1
  53. package/dist/index-Cy4jfXh5.d.mts +0 -67
  54. package/dist/signal-CHVEmzvI.d.mts +0 -59
@@ -1 +0,0 @@
1
- {"version":3,"file":"approval-CwhTP1XB.mjs","names":[],"sources":["../src/schema/task.ts","../src/schema/finding.ts","../src/approval.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n isoDateTimeSchema,\n replyToSchema,\n schemaVersionSchema,\n usdSchema,\n vocabularySchema,\n} from \"./common\";\nimport {\n changeIdSchema,\n repositoryIdSchema,\n repositoryIdsSchema,\n signalIdSchema,\n taskIdSchema,\n} from \"./id\";\n\n/**\n * Kinds the standard loop understands. Any vocabulary value is valid; the\n * kind-scoped rules below apply only to these standard kinds.\n */\nexport const standardTaskKinds = [\n \"coordination\",\n \"code_change\",\n \"verification\",\n \"investigation\",\n \"question\",\n] as const;\n\nexport const taskKindSchema = vocabularySchema.describe(\n \"What kind of work this is, e.g. code_change, question, or a registered custom kind.\",\n);\n\nexport const taskStateSchema = z\n .enum([\"queued\", \"running\", \"stalled\", \"verifying\", \"succeeded\", \"failed\", \"needs_human\", \"cancelled\"])\n .describe(\"Lifecycle state. Transitions are fixed; see taskTransitions.\");\n\nexport const taskApprovalSchema = z\n .enum([\"required\", \"granted\", \"denied\"])\n .describe(\n \"Human approval status. Absent means no approval is needed. Stamped required at creation by policy; granted or denied by an operator.\",\n );\n\nexport const taskExecutionRepositoryIdsSchema = z\n .tuple([repositoryIdSchema])\n .describe(\"The one repository used to execute a task.\");\n\nexport const coordinationRepositoryIdsSchema = z\n .tuple([repositoryIdSchema, repositoryIdSchema])\n .rest(repositoryIdSchema)\n .refine((repositoryIds) => new Set(repositoryIds).size === repositoryIds.length, {\n message: \"Coordination tasks require distinct repositories.\",\n })\n .describe(\"The distinct repositories coordinated by a non-executable parent task.\");\n\nexport const taskOriginSchema = z\n .strictObject({\n signalId: signalIdSchema.optional().describe(\"Signal that led to this task.\"),\n parentTaskId: taskIdSchema.optional().describe(\"Parent task that planned this child.\"),\n operator: z.string().min(1).optional().describe(\"Human operator who requested it.\"),\n })\n .refine(\n (origin) =>\n origin.signalId !== undefined ||\n origin.parentTaskId !== undefined ||\n origin.operator !== undefined,\n { message: \"origin requires a signalId, parentTaskId, or operator\" },\n );\n\nexport const harnessSchema = z\n .string()\n .min(1)\n .describe(\"Coding harness running inside the execution environment, e.g. claude-code or codex.\");\n\n/**\n * The provider's handle on the current attempt's execution. Opaque to the\n * lifecycle: Factory records it so the execution can be inspected or\n * cancelled, never to mirror the provider's own state machine.\n */\nexport const executionReferenceSchema = z.strictObject({\n provider: z.string().min(1).describe(\"Execution provider, e.g. eve.\"),\n sessionId: z.string().min(1).describe(\"Provider-side session running this attempt.\"),\n environmentId: z.string().min(1).optional().describe(\"Provider-side execution environment.\"),\n harness: harnessSchema.optional(),\n traceId: z.string().min(1).optional().describe(\"Provider-side trace for this execution.\"),\n});\n\nconst tokenCountSchema = z.number().int().nonnegative();\n\nexport const taskUsageSchema = z.strictObject({\n costUsd: usdSchema.describe(\"AI Gateway cost summed over recorded steps.\"),\n inputTokens: tokenCountSchema,\n outputTokens: tokenCountSchema,\n cacheReadTokens: tokenCountSchema,\n cacheWriteTokens: tokenCountSchema,\n steps: z\n .record(z.string().min(1), tokenCountSchema)\n .describe(\"Highest recorded stepIndex per turnId; steps at or below it are already counted.\"),\n});\n\nexport const taskQuestionSchema = z.strictObject({\n text: z.string().min(1).describe(\"The input or action requested from a human before work can resume.\"),\n options: z.array(z.string().min(1)).describe(\"Suggested replies; free-text is also allowed.\"),\n answer: z.string().min(1).optional(),\n answeredBy: z.string().min(1).optional().describe(\"Authenticated operator who answered.\"),\n});\n\nexport const taskSchema = z\n .strictObject({\n schemaVersion: schemaVersionSchema,\n id: taskIdSchema,\n repositoryIds: repositoryIdsSchema.describe(\"Repositories the task concerns.\"),\n kind: taskKindSchema,\n area: z\n .string()\n .min(1)\n .optional()\n .describe(\"Investigation area, e.g. onboarding or security. Investigation tasks only.\"),\n state: taskStateSchema,\n approval: taskApprovalSchema.optional(),\n origin: taskOriginSchema,\n dependsOn: z\n .array(taskIdSchema)\n .min(1)\n .optional()\n .describe(\"Sibling tasks that must produce a change before this one may run.\"),\n replyTo: replyToSchema,\n attempt: z\n .number()\n .int()\n .min(1)\n .describe(\"Execution attempt, starting at 1; bumps on retry.\"),\n execution: executionReferenceSchema\n .optional()\n .describe(\"Execution of the current attempt, recorded at dispatch and replaced on retry.\"),\n lastProgressAt: isoDateTimeSchema\n .optional()\n .describe(\"Last time the execution demonstrably advanced: dispatch, or a completed model step.\"),\n stalledAt: isoDateTimeSchema.optional().describe(\"When the reconciler marked the task stalled.\"),\n stalledReason: z.string().min(1).optional().describe(\"Why the task was marked stalled.\"),\n reservedUsd: usdSchema.optional().describe(\"Budget reserved before dispatch.\"),\n reservedAt: isoDateTimeSchema\n .optional()\n .describe(\"When the reservation was stamped; attributes spend to a budget month.\"),\n usage: taskUsageSchema\n .optional()\n .describe(\"Measured model usage, accumulated per step by recordUsage and settled into settledUsd.\"),\n settledUsd: usdSchema\n .optional()\n .describe(\n \"Measured cost settled after execution. A task settles once; re-execution cost is not separately metered (see the unresolved multi-attempt accounting decision).\",\n ),\n question: taskQuestionSchema.optional(),\n changeId: changeIdSchema.optional().describe(\"Change produced or verified by this task.\"),\n reportRef: z\n .string()\n .min(1)\n .optional()\n .describe(\"Reference to the report artifact an investigation produced.\"),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n })\n .superRefine((task, ctx) => {\n if (\n task.kind === \"coordination\" &&\n !coordinationRepositoryIdsSchema.safeParse(task.repositoryIds).success\n ) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"repositoryIds\"],\n message: \"coordination tasks require at least two distinct repositories\",\n });\n }\n if (\n (task.kind === \"code_change\" || task.kind === \"verification\") &&\n task.repositoryIds.length !== 1\n ) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"repositoryIds\"],\n message: `${task.kind} tasks require exactly one repository`,\n });\n }\n if (task.area !== undefined && task.kind !== \"investigation\") {\n ctx.addIssue({\n code: \"custom\",\n path: [\"area\"],\n message: \"area is only valid on investigation tasks\",\n });\n }\n if (task.kind === \"question\") {\n if (task.question === undefined) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"question\"],\n message: \"question tasks require a question\",\n });\n }\n } else if (task.question !== undefined) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"question\"],\n message: \"question is only valid on question tasks\",\n });\n }\n if (task.state !== \"stalled\" && (task.stalledAt !== undefined || task.stalledReason !== undefined)) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"stalledAt\"],\n message: \"stalledAt and stalledReason are only valid while the task is stalled\",\n });\n }\n if (\n (task.kind === \"question\" || task.kind === \"coordination\") &&\n task.execution !== undefined\n ) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"execution\"],\n message: `${task.kind} tasks never execute`,\n });\n }\n if (\n task.changeId !== undefined &&\n task.kind !== \"code_change\" &&\n task.kind !== \"verification\"\n ) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"changeId\"],\n message: \"changeId is only valid on code_change or verification tasks\",\n });\n }\n if (task.reportRef !== undefined && task.kind !== \"investigation\") {\n ctx.addIssue({\n code: \"custom\",\n path: [\"reportRef\"],\n message: \"reportRef is only valid on investigation tasks\",\n });\n }\n });\n\nexport type TaskApproval = z.infer<typeof taskApprovalSchema>;\nexport type TaskKind = z.infer<typeof taskKindSchema>;\nexport type TaskState = z.infer<typeof taskStateSchema>;\nexport type TaskUsage = z.infer<typeof taskUsageSchema>;\nexport type ExecutionReference = z.infer<typeof executionReferenceSchema>;\nexport type Task = z.infer<typeof taskSchema>;\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nexport const findingPrioritySchema = z\n .enum([\"P0\", \"P1\", \"P2\", \"P3\"])\n .describe(\"Urgency of a finding: P0 is drop-everything, P3 is nice-to-have.\");\n\nexport const findingCategorySchema = z.enum([\n \"security\",\n \"correctness\",\n \"reliability\",\n \"performance\",\n \"compatibility\",\n \"accessibility\",\n \"documentation\",\n \"maintainability\",\n \"testing\",\n \"usability\",\n]);\n\nexport const findingVerificationLevelSchema = z\n .enum([\"unverified\", \"supported\", \"confirmed\"])\n .describe(\n \"How sure the reporter is: unverified = suspected from reading; supported = evidence points to it; confirmed = demonstrated.\",\n );\n\nexport const findingVerificationMethodSchema = z.enum([\n \"code_inspection\",\n \"static_analysis\",\n \"manual_reproduction\",\n \"unit_test\",\n \"integration_test\",\n \"e2e_test\",\n \"runtime_observation\",\n \"documentation\",\n]);\n\n/**\n * What was done to check the finding. Forces a reporter to distinguish \"I\n * think this is a problem\" from \"I demonstrated it\", which is the axis a human\n * approving fixes cares about most.\n */\nexport const findingVerificationSchema = z.strictObject({\n level: findingVerificationLevelSchema,\n method: findingVerificationMethodSchema,\n summary: z.string().min(1).max(2000).describe(\"What was checked and what it showed.\"),\n limitations: z.string().min(1).max(2000).optional().describe(\"What could not be checked.\"),\n});\n\n/** A single problem found in a repository, as a scanner or agent reports it. */\nexport const findingSchema = z.strictObject({\n title: z.string().min(1).max(200),\n body: z.string().min(1).max(8000).describe(\"The problem, its impact, and how to fix it. Markdown.\"),\n path: z.string().min(1).max(500).optional().describe(\"Repository-relative file path.\"),\n line: z.number().int().min(1).optional(),\n endLine: z.number().int().min(1).optional(),\n priority: findingPrioritySchema,\n category: findingCategorySchema,\n verification: findingVerificationSchema,\n tool: z.string().min(1).max(100).optional().describe(\"Scanner that produced it, e.g. deepsec.\"),\n ruleId: z.string().min(1).max(200).optional().describe(\"Scanner rule or check id, when there is one.\"),\n});\n\nexport type FindingPriority = z.infer<typeof findingPrioritySchema>;\nexport type FindingCategory = z.infer<typeof findingCategorySchema>;\nexport type FindingVerification = z.infer<typeof findingVerificationSchema>;\nexport type Finding = z.infer<typeof findingSchema>;\n\n/**\n * Stable identity of a finding across scans, used as the signal's delivery id\n * so re-reporting is a no-op instead of a second task. A scanner finding is\n * the same problem when the same tool reports the same rule on the same file;\n * its title is model-written prose and changes between runs, so it is left\n * out. Without a rule id the title is all there is, normalised.\n */\nexport function findingFingerprint(repositoryId: string, finding: Finding): string {\n const parts =\n finding.tool !== undefined && finding.ruleId !== undefined && finding.path !== undefined\n ? [repositoryId, finding.tool, finding.ruleId, finding.path]\n : [\n repositoryId,\n finding.tool ?? \"\",\n finding.ruleId ?? \"\",\n finding.path ?? \"\",\n finding.title.trim().toLowerCase().replaceAll(/\\s+/gu, \" \"),\n ];\n return createHash(\"sha256\").update(parts.join(\" \")).digest(\"hex\").slice(0, 32);\n}\n","import { z } from \"zod\";\nimport { vocabularySchema } from \"./schema/common\";\nimport { findingPrioritySchema, type FindingPriority } from \"./schema/finding\";\n\n/**\n * One approval rule. Every listed field must match for the rule to apply;\n * absent fields match anything. Rules are evaluated first-match.\n */\nexport const approvalRuleSchema = z.strictObject({\n source: vocabularySchema.optional().describe(\"Signal source, e.g. security_scan or github_issue.\"),\n kind: vocabularySchema.optional().describe(\"Task kind, e.g. code_change.\"),\n priority: z\n .array(findingPrioritySchema)\n .min(1)\n .optional()\n .describe(\"Finding priorities the rule applies to.\"),\n require: z.boolean().describe(\"Whether matching tasks wait for a human before dispatch.\"),\n});\n\nexport const approvalPolicySchema = z\n .array(approvalRuleSchema)\n .describe(\"First-match rules deciding which tasks need a human before they run.\");\n\nexport type ApprovalRule = z.infer<typeof approvalRuleSchema>;\nexport type ApprovalPolicy = z.infer<typeof approvalPolicySchema>;\n\n/**\n * The default: fixes for security findings wait for a human; everything else\n * runs as it always has. Handoffs between agents (verification) never wait,\n * because a human already approved the work they continue.\n */\nexport const defaultApprovalPolicy: ApprovalPolicy = [\n { kind: \"verification\", require: false },\n { source: \"security_scan\", require: true },\n { require: false },\n];\n\nexport interface ApprovalSubject {\n source?: string;\n kind: string;\n priority?: FindingPriority;\n}\n\n/**\n * Decide at task-creation time whether a task needs approval. Pure: the answer\n * is stamped on the task and never re-evaluated, so a later policy change\n * cannot silently release work a human was expecting to see.\n */\nexport function requiresApproval(policy: ApprovalPolicy, subject: ApprovalSubject): boolean {\n for (const rule of policy) {\n if (rule.source !== undefined && rule.source !== subject.source) continue;\n if (rule.kind !== undefined && rule.kind !== subject.kind) continue;\n if (rule.priority !== undefined) {\n if (subject.priority === undefined || !rule.priority.includes(subject.priority)) continue;\n }\n return rule.require;\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"change-CzEAoFVE.mjs","names":[],"sources":["../src/budget.ts","../src/coordination.ts","../src/schema/transitions.ts","../src/dispatch.ts","../src/schema/change.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { factoryIdSchema, type FactoryId, type TaskId } from \"./schema/id\";\nimport type { BudgetLimits } from \"./schema/budget\";\nimport type { Task } from \"./schema/task\";\nimport type { FactoryStores } from \"./store/engine\";\n\n/**\n * Stable factory identity for budget rows. Nothing mints factory ids yet, so\n * the app derives one from a deployment-stable seed such as the Vercel\n * project id.\n */\nexport function deriveFactoryId(seed: string): FactoryId {\n return factoryIdSchema.parse(\n `fac_${createHash(\"sha256\").update(seed).digest(\"hex\").slice(0, 32)}`,\n );\n}\n\n/** The budget month (\"YYYY-MM\", UTC) a timestamp falls into. */\nexport function budgetMonthOf(isoDateTime: string): string {\n return isoDateTime.slice(0, 7);\n}\n\n// Currency math runs in integer micro-dollars so decimal limits compare\n// exactly; 0.1 + 0.2 must never refuse an exact-fit reservation.\nfunction microsOf(usd: number): number {\n return Math.round(usd * 1_000_000);\n}\n\n/**\n * Monthly spend is derived from task rows, never stored: each task counts its\n * settled cost once that exists, otherwise its held reservation. A task\n * settles once, so a settled row never also counts its reservation.\n * Attribution uses the reservation time so tasks that wait across a month\n * boundary charge the month they actually ran in.\n */\nexport function computeMonthSpendUsd(tasks: readonly Task[], month: string): number {\n let spendMicros = 0;\n for (const task of tasks) {\n if (budgetMonthOf(task.reservedAt ?? task.createdAt) !== month) {\n continue;\n }\n spendMicros += microsOf(task.settledUsd ?? task.reservedUsd ?? 0);\n }\n return spendMicros / 1_000_000;\n}\n\nexport type BudgetStatus = \"ok\" | \"warning\" | \"exhausted\";\n\n/**\n * True when one more per-task slice fits under the monthly total. Dispatch\n * refuses on this exact predicate and budgetStatus reports \"exhausted\" on its\n * negation, so refusing and reporting can never disagree.\n */\nexport function canReserve(\n spendUsd: number,\n limits: Pick<BudgetLimits, \"totalUsd\" | \"perTaskReservationUsd\">,\n): boolean {\n return microsOf(spendUsd) + microsOf(limits.perTaskReservationUsd) <= microsOf(limits.totalUsd);\n}\n\nexport function budgetStatus(\n spendUsd: number,\n limits: Pick<BudgetLimits, \"totalUsd\" | \"perTaskReservationUsd\" | \"warningPercent\">,\n): BudgetStatus {\n if (!canReserve(spendUsd, limits)) {\n return \"exhausted\";\n }\n // Integer comparison: spend/total * 100 >= warningPercent without float\n // drift on decimal limits.\n if (microsOf(spendUsd) * 100 >= microsOf(limits.totalUsd) * limits.warningPercent) {\n return \"warning\";\n }\n return \"ok\";\n}\n\nconst TERMINAL_TASK_STATES = new Set<Task[\"state\"]>([\"succeeded\", \"failed\", \"cancelled\"]);\n\nexport interface SettleSweepOptions {\n /** How long a terminal task waits for late usage before the sweep settles it. */\n graceMinutes?: number;\n now?: () => Date;\n}\n\nexport interface SettleSweepResult {\n settled: { taskId: TaskId; totalUsd: number }[];\n}\n\n/**\n * Settle reserved terminal tasks the session hook did not: a task that ended\n * after its session (a change verified later) or whose session died. Settles\n * at the recorded usage, or at zero with a warning reason when none was\n * recorded within the grace window.\n */\nexport async function sweepSettlements(\n stores: FactoryStores,\n options: SettleSweepOptions = {},\n): Promise<SettleSweepResult> {\n const graceMs = (options.graceMinutes ?? 10) * 60_000;\n const nowMs = (options.now ?? (() => new Date()))().getTime();\n const settled: SettleSweepResult[\"settled\"] = [];\n for (const task of await stores.tasks.list()) {\n if (task.reservedUsd === undefined || task.settledUsd !== undefined) {\n continue;\n }\n if (!TERMINAL_TASK_STATES.has(task.state)) {\n continue;\n }\n if (nowMs - Date.parse(task.updatedAt) < graceMs) {\n continue;\n }\n // A concurrent writer (operator retry, another sweep) may move the task\n // between the list and the settle; skip it rather than aborting the pass.\n const totalUsd = task.usage?.costUsd ?? 0;\n try {\n const result = await stores.tasks.settle(\n task.id,\n { totalUsd },\n {\n reason: task.usage === undefined\n ? \"usage missing after ten minutes; settled at zero\"\n : \"measured usage settled by sweep\",\n },\n );\n // Report this settlement's amount, not the task's accumulated total.\n settled.push({ taskId: result.id, totalUsd });\n } catch {\n continue;\n }\n }\n return { settled };\n}\n\nexport interface BudgetNotice {\n outcome: \"budget_warning\" | \"budget_exhausted\";\n month: string;\n spendUsd: number;\n limits: BudgetLimits;\n}\n\nexport interface BudgetSweepOptions {\n factoryId: FactoryId;\n limits: BudgetLimits;\n /** Defaults to the current UTC month. */\n month?: string;\n now?: () => Date;\n /** Owner-only delivery; invoked at most once per month per outcome. */\n notify?: (notice: BudgetNotice) => Promise<void>;\n}\n\nexport interface BudgetSweepResult {\n month: string;\n spendUsd: number;\n status: BudgetStatus;\n warned: boolean;\n exhausted: boolean;\n}\n\n/**\n * Ensure the month's budget row, derive spend, and record threshold crossings\n * write-once. Delivery comes first and the stamp only lands after it, so a\n * failed notification retries on the next sweep instead of being lost; the\n * write-once stamp still keeps each threshold to one message per month.\n */\nexport async function sweepBudget(\n stores: FactoryStores,\n options: BudgetSweepOptions,\n): Promise<BudgetSweepResult> {\n const now = options.now ?? (() => new Date());\n const month = options.month ?? budgetMonthOf(now().toISOString());\n const budget = await stores.budgets.ensure({\n factoryId: options.factoryId,\n month,\n limits: options.limits,\n });\n const spendUsd = computeMonthSpendUsd(await stores.tasks.list(), month);\n const status = budgetStatus(spendUsd, options.limits);\n\n async function crossThreshold(\n notice: \"warning\" | \"exhausted\",\n outcome: BudgetNotice[\"outcome\"],\n ): Promise<boolean> {\n const field = notice === \"warning\" ? \"warningAt\" : \"exhaustedAt\";\n if (budget.notices[field] !== undefined) {\n return false;\n }\n if (options.notify !== undefined) {\n try {\n await options.notify({ outcome, month, spendUsd, limits: options.limits });\n } catch {\n // Leave the notice unstamped so the next sweep retries delivery.\n return false;\n }\n }\n return (await stores.budgets.stampNotice(month, notice)).stamped;\n }\n\n let warned = false;\n let exhausted = false;\n if (status === \"exhausted\") {\n exhausted = await crossThreshold(\"exhausted\", \"budget_exhausted\");\n // The warning threshold was necessarily crossed too; stamp it silently so\n // a later settle-down cannot deliver \"warning\" after \"exhausted\".\n if (budget.notices.warningAt === undefined) {\n await stores.budgets.stampNotice(month, \"warning\");\n }\n } else if (status === \"warning\") {\n warned = await crossThreshold(\"warning\", \"budget_warning\");\n }\n return { month, spendUsd, status, warned, exhausted };\n}\n","import type { ReplyTo } from \"./schema/common\";\nimport type { RepositoryId, SignalId, TaskId } from \"./schema/id\";\nimport type { Task } from \"./schema/task\";\nimport { RecordNotFoundError } from \"./store/driver\";\nimport type { FactoryStores } from \"./store/engine\";\n\ninterface CreateCoordinationInputBase {\n repositoryIds: readonly [RepositoryId, RepositoryId, ...RepositoryId[]];\n replyTo: ReplyTo;\n /** Same key recreates neither the parent nor its repository-scoped children. */\n dedupeKey?: string;\n}\n\nexport type CreateCoordinationInput = CreateCoordinationInputBase &\n ({ signalId: SignalId; operator?: never } | { operator: string; signalId?: never });\n\nexport interface CoordinationPlan {\n parent: Task;\n children: readonly Task[];\n}\n\nexport type CoordinationReconciliation =\n | { status: \"pending\"; parent: Task; children: readonly Task[] }\n | { status: \"succeeded\" | \"failed\"; transitioned: boolean; parent: Task; children: readonly Task[] };\n\nexport interface ReconcileCoordinationOptions {\n present(input: {\n status: \"succeeded\" | \"failed\";\n parent: Task;\n children: readonly Task[];\n }): Promise<void>;\n}\n\n/** Create one non-executable parent and one independently executable child per repository. */\nexport async function createCoordination(\n stores: FactoryStores,\n input: CreateCoordinationInput,\n): Promise<CoordinationPlan> {\n const repositoryIds = [...new Set(input.repositoryIds)];\n if (repositoryIds.length < 2) {\n throw new Error(\"coordination requires at least two distinct repositories\");\n }\n const parent = await stores.tasks.create({\n repositoryIds,\n kind: \"coordination\",\n origin: input.signalId === undefined ? { operator: input.operator } : { signalId: input.signalId },\n replyTo: input.replyTo,\n dedupeKey: input.dedupeKey,\n });\n const running = parent.state === \"queued\"\n ? await stores.tasks.transition(parent.id, \"running\", {\n reason: \"repository-scoped child tasks created\",\n })\n : parent;\n const children = await Promise.all(\n repositoryIds.map((repositoryId) =>\n stores.tasks.create({\n repositoryIds: [repositoryId],\n kind: \"code_change\",\n origin: { parentTaskId: parent.id },\n replyTo: input.replyTo,\n dedupeKey: input.dedupeKey === undefined ? undefined : `${input.dedupeKey}:${repositoryId}`,\n }),\n ),\n );\n return { parent: running, children };\n}\n\n/** Reconcile a coordination parent from its direct children without ever dispatching the parent. */\nexport async function reconcileCoordination(\n stores: FactoryStores,\n parentTaskId: TaskId,\n options: ReconcileCoordinationOptions,\n): Promise<CoordinationReconciliation> {\n const parent = await stores.tasks.get(parentTaskId);\n if (parent === null) throw new RecordNotFoundError(\"tasks\", parentTaskId);\n if (parent.kind !== \"coordination\") {\n throw new Error(`task ${parentTaskId} is ${parent.kind}, not coordination`);\n }\n const children = (await stores.tasks.list()).filter(\n (task) => task.origin.parentTaskId === parentTaskId,\n );\n if (children.length !== parent.repositoryIds.length) {\n return { status: \"pending\", parent, children };\n }\n if (parent.state === \"succeeded\" || parent.state === \"failed\") {\n return { status: parent.state, transitioned: false, parent, children };\n }\n if (children.some((child) => child.state === \"failed\" || child.state === \"cancelled\")) {\n await options.present({ status: \"failed\", parent, children });\n const failed = await stores.tasks.transition(parent.id, \"failed\", {\n reason: \"one or more repository tasks failed\",\n });\n return { status: \"failed\", transitioned: true, parent: failed, children };\n }\n if (!children.every((child) => child.state === \"succeeded\")) {\n return { status: \"pending\", parent, children };\n }\n await options.present({ status: \"succeeded\", parent, children });\n await stores.tasks.transition(parent.id, \"verifying\", {\n reason: \"all repository tasks succeeded\",\n });\n const succeeded = await stores.tasks.transition(parent.id, \"succeeded\", {\n reason: \"aggregate multi-repository handoff is ready\",\n });\n return { status: \"succeeded\", transitioned: true, parent: succeeded, children };\n}\n","import type { ChangeState } from \"./change\";\nimport type { SignalOutcome } from \"./signal\";\nimport type { TaskState } from \"./task\";\n\nexport type TransitionActor =\n | \"system\"\n | \"reconciler\"\n | \"foreman\"\n | \"operator\"\n | \"webhook\"\n | \"human\";\n\nexport interface Transition<State extends string> {\n readonly from: State;\n readonly to: State;\n readonly trigger: string;\n readonly actor: TransitionActor;\n}\n\nexport const taskInitialState = \"queued\" satisfies TaskState;\n\nexport const taskTransitions: readonly Transition<TaskState>[] = [\n {\n from: \"queued\",\n to: \"running\",\n trigger: \"dependencies have a change, reservation succeeded, devbox created\",\n actor: \"reconciler\",\n },\n { from: \"queued\", to: \"failed\", trigger: \"reservation refused\", actor: \"reconciler\" },\n { from: \"queued\", to: \"needs_human\", trigger: \"kind is question\", actor: \"system\" },\n { from: \"queued\", to: \"cancelled\", trigger: \"cancel or kill\", actor: \"operator\" },\n { from: \"running\", to: \"verifying\", trigger: \"devbox task reported complete\", actor: \"reconciler\" },\n {\n from: \"running\",\n to: \"queued\",\n trigger: \"launch failed below attempt limit\",\n actor: \"reconciler\",\n },\n {\n from: \"running\",\n to: \"failed\",\n trigger: \"devbox task errored, deadline exceeded, or launch failed at attempt limit\",\n actor: \"reconciler\",\n },\n {\n from: \"running\",\n to: \"needs_human\",\n trigger: \"an open QuestionTask requests human input or action\",\n actor: \"reconciler\",\n },\n { from: \"running\", to: \"cancelled\", trigger: \"cancel or kill\", actor: \"operator\" },\n { from: \"running\", to: \"stalled\", trigger: \"no progress within the stall window\", actor: \"reconciler\" },\n {\n from: \"stalled\",\n to: \"queued\",\n trigger: \"replacement attempt below attempt limit, or operator retry\",\n actor: \"reconciler\",\n },\n { from: \"stalled\", to: \"failed\", trigger: \"attempt limit exhausted\", actor: \"reconciler\" },\n { from: \"stalled\", to: \"cancelled\", trigger: \"cancel or kill\", actor: \"operator\" },\n { from: \"verifying\", to: \"succeeded\", trigger: \"kind criterion true\", actor: \"reconciler\" },\n {\n from: \"verifying\",\n to: \"running\",\n trigger: \"verification failed below attempt limit\",\n actor: \"reconciler\",\n },\n {\n from: \"verifying\",\n to: \"failed\",\n trigger: \"verification failed at attempt limit\",\n actor: \"reconciler\",\n },\n { from: \"verifying\", to: \"cancelled\", trigger: \"cancel or kill\", actor: \"operator\" },\n { from: \"needs_human\", to: \"succeeded\", trigger: \"question answered\", actor: \"operator\" },\n { from: \"needs_human\", to: \"queued\", trigger: \"operator retry\", actor: \"operator\" },\n { from: \"needs_human\", to: \"cancelled\", trigger: \"cancel or kill\", actor: \"operator\" },\n { from: \"failed\", to: \"queued\", trigger: \"operator retry\", actor: \"operator\" },\n];\n\nexport const changeInitialState = \"verifying\" satisfies ChangeState;\n\nexport const changeTransitions: readonly Transition<ChangeState>[] = [\n {\n from: \"verifying\",\n to: \"ready\",\n trigger: \"configured verification policy completed, risk and confidence written\",\n actor: \"reconciler\",\n },\n {\n from: \"verifying\",\n to: \"closed\",\n trigger: \"verification failed at attempt limit or pr closed upstream\",\n actor: \"reconciler\",\n },\n { from: \"ready\", to: \"merged\", trigger: \"pr merged on github\", actor: \"webhook\" },\n { from: \"ready\", to: \"closed\", trigger: \"pr closed without merge\", actor: \"webhook\" },\n {\n from: \"merged\",\n to: \"deployed\",\n trigger: \"production deployment ready containing the merge sha\",\n actor: \"webhook\",\n },\n {\n from: \"deployed\",\n to: \"rolled_out\",\n trigger: \"no flag, or flag ramped to full and held\",\n actor: \"foreman\",\n },\n { from: \"deployed\", to: \"rolled_back\", trigger: \"rollback rule crossed\", actor: \"foreman\" },\n {\n from: \"rolled_out\",\n to: \"rolled_back\",\n trigger: \"rollback rule crossed or flag flipped off\",\n actor: \"foreman\",\n },\n { from: \"rolled_back\", to: \"deployed\", trigger: \"human retried the ramp\", actor: \"human\" },\n];\n\nexport const signalInitialOutcome = \"pending\" satisfies SignalOutcome;\n\nexport const signalOutcomeTransitions: readonly Transition<SignalOutcome>[] = [\n { from: \"pending\", to: \"accepted\", trigger: \"classified as actionable\", actor: \"reconciler\" },\n { from: \"pending\", to: \"duplicate\", trigger: \"classified as duplicate\", actor: \"reconciler\" },\n {\n from: \"pending\",\n to: \"unactionable\",\n trigger: \"classified as unactionable\",\n actor: \"reconciler\",\n },\n {\n from: \"pending\",\n to: \"low_confidence\",\n trigger: \"classified below confidence threshold\",\n actor: \"reconciler\",\n },\n { from: \"pending\", to: \"ignored\", trigger: \"classified as ignorable\", actor: \"reconciler\" },\n];\n\nexport function isTransitionAllowed<State extends string>(\n transitions: readonly Transition<State>[],\n from: State,\n to: State,\n): boolean {\n return transitions.some((transition) => transition.from === from && transition.to === to);\n}\n\nexport function legalNextStates<State extends string>(\n transitions: readonly Transition<State>[],\n from: State,\n): readonly State[] {\n return [\n ...new Set(\n transitions.filter((transition) => transition.from === from).map((t) => t.to),\n ),\n ];\n}\n\nexport class InvalidTransitionError<State extends string> extends Error {\n readonly from: State;\n readonly to: State;\n readonly legalNextStates: readonly State[];\n\n constructor(from: State, to: State, legal: readonly State[]) {\n super(\n legal.length > 0\n ? `invalid transition from \"${from}\" to \"${to}\"; legal next states: ${legal\n .map((state) => `\"${state}\"`)\n .join(\", \")}`\n : `invalid transition from \"${from}\" to \"${to}\"; \"${from}\" is terminal`,\n );\n this.name = \"InvalidTransitionError\";\n this.from = from;\n this.to = to;\n this.legalNextStates = legal;\n }\n}\n\nexport function assertTransition<State extends string>(\n transitions: readonly Transition<State>[],\n from: State,\n to: State,\n): void {\n if (!isTransitionAllowed(transitions, from, to)) {\n throw new InvalidTransitionError(from, to, legalNextStates(transitions, from));\n }\n}\n\nexport function terminalStates<State extends string>(\n transitions: readonly Transition<State>[],\n): readonly State[] {\n const states = new Set<State>();\n for (const transition of transitions) {\n states.add(transition.from);\n states.add(transition.to);\n }\n return [...states].filter(\n (state) => !transitions.some((transition) => transition.from === state),\n );\n}\n","import { budgetMonthOf, canReserve, computeMonthSpendUsd } from \"./budget\";\nimport type { BudgetLimits } from \"./schema/budget\";\nimport type { TaskId } from \"./schema/id\";\nimport { taskExecutionRepositoryIdsSchema, type ExecutionReference, type Task } from \"./schema/task\";\nimport { InvalidTransitionError } from \"./schema/transitions\";\nimport { RecordNotFoundError, VersionConflictError } from \"./store/driver\";\nimport type { FactoryStores } from \"./store/engine\";\n\nexport class DispatchRefusedError extends Error {\n readonly taskId: TaskId;\n readonly reason: string;\n\n constructor(taskId: TaskId, reason: string) {\n super(`dispatch of \"${taskId}\" refused: ${reason}`);\n this.name = \"DispatchRefusedError\";\n this.taskId = taskId;\n this.reason = reason;\n }\n}\n\n/**\n * An exhausted budget is definitive, not a retryable refusal: the task has\n * already been failed and receipted when this throws, so it never re-enters\n * the sweep. Operator retry recovers it once budget frees up.\n */\nexport class BudgetExhaustedError extends Error {\n readonly taskId: TaskId;\n readonly month: string;\n readonly spendUsd: number;\n\n constructor(taskId: TaskId, month: string, spendUsd: number) {\n super(`dispatch of \"${taskId}\" refused: budget for ${month} is exhausted at $${spendUsd}`);\n this.name = \"BudgetExhaustedError\";\n this.taskId = taskId;\n this.month = month;\n this.spendUsd = spendUsd;\n }\n}\n\nexport interface DispatchBudget {\n limits: Pick<BudgetLimits, \"totalUsd\" | \"perTaskReservationUsd\">;\n /** Defaults to the current UTC month. */\n month?: string;\n now?: () => Date;\n}\n\nexport interface DispatchOptions {\n /**\n * Starts the durable execution for the task (an Eve agent run). Runs after\n * the task has won the queued -> running transition. Returning the\n * provider's execution reference records it on the task so the reconciler\n * can find the run; returning nothing leaves the task reachable only by the\n * stall window.\n */\n launch: (task: Task) => Promise<ExecutionReference | void>;\n /** How many launches to try before the task fails for good; a failure re-queues for the next sweep. */\n maxLaunchAttempts?: number;\n /**\n * Reserve-before-execution. When set, dispatch refuses definitively once\n * the month's derived spend cannot fit another reservation, and otherwise\n * stamps the reservation in the same write that wins queued -> running.\n */\n budget?: DispatchBudget;\n}\n\n// A fresh Vercel project rejects its own agents' sessions until its identity\n// finishes provisioning; at one sweep per minute the budget must outlast that.\nexport const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;\n\n/**\n * The handshake between recorded work and execution: win the store-gated\n * transition (at most one dispatcher can succeed), then start the run. A\n * launch failure re-queues the task (bumping its attempt) until the attempt\n * limit, then fails it; a crash between the transition and the launch leaves\n * a running task with no execution, which the sweep reaps.\n */\nexport async function dispatchTask(\n stores: FactoryStores,\n taskId: TaskId,\n options: DispatchOptions,\n): Promise<Task> {\n const task = await stores.tasks.get(taskId);\n if (task === null) {\n throw new RecordNotFoundError(\"tasks\", taskId);\n }\n if (task.kind === \"question\") {\n throw new DispatchRefusedError(taskId, \"question tasks are answered, not dispatched\");\n }\n if (task.kind === \"coordination\") {\n throw new DispatchRefusedError(taskId, \"coordination tasks are reconciled, not dispatched\");\n }\n if (!taskExecutionRepositoryIdsSchema.safeParse(task.repositoryIds).success) {\n throw new DispatchRefusedError(taskId, \"task execution requires exactly one repository\");\n }\n if (task.approval === \"required\") {\n throw new DispatchRefusedError(taskId, \"awaiting human approval\");\n }\n if (task.approval === \"denied\") {\n throw new DispatchRefusedError(taskId, \"approval was denied\");\n }\n for (const dependencyId of task.dependsOn ?? []) {\n const dependency = await stores.tasks.get(dependencyId);\n if (dependency === null) {\n throw new DispatchRefusedError(taskId, `dependency \"${dependencyId}\" does not exist`);\n }\n if (dependency.changeId === undefined) {\n throw new DispatchRefusedError(\n taskId,\n `dependency \"${dependencyId}\" has not produced a change`,\n );\n }\n }\n\n // The reservation gates queued -> running only; any other state falls\n // through to the transition below, which rejects it as invalid.\n let reservation: { reservedUsd: number; reservedAt: string } | undefined;\n if (options.budget !== undefined && task.state === \"queued\") {\n const budgetNow = (options.budget.now ?? (() => new Date()))().toISOString();\n const month = options.budget.month ?? budgetMonthOf(budgetNow);\n // This task's own retained reservation from a prior attempt is excluded,\n // because re-reserving overwrites it. Its settled cost stays counted:\n // that money was actually spent and a retry must not reopen it.\n const rows = (await stores.tasks.list()).map((row) =>\n row.id === taskId ? { ...row, reservedUsd: undefined } : row,\n );\n const spendUsd = computeMonthSpendUsd(rows, month);\n const { totalUsd, perTaskReservationUsd } = options.budget.limits;\n if (!canReserve(spendUsd, options.budget.limits)) {\n const reason = `budget exhausted: ${month} spend $${spendUsd} cannot fit a $${perTaskReservationUsd} reservation under $${totalUsd}`;\n // Fail first, so BudgetExhaustedError's contract (task already failed)\n // holds; losing the task to a concurrent dispatcher surfaces as a\n // refusal, never as a false exhaustion with a misleading receipt.\n try {\n await stores.tasks.transition(taskId, \"failed\", { reason, expectFrom: \"queued\" });\n } catch (error) {\n if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) {\n throw new DispatchRefusedError(taskId, \"another dispatcher took the task\");\n }\n throw error;\n }\n // Best-effort: the failed transition above already wrote its own\n // receipt carrying this reason, so losing this supplementary one\n // cannot lose the evidence.\n if (task.repositoryIds !== undefined) {\n await stores.receipts\n .append({\n repositoryIds: task.repositoryIds,\n taskId,\n kind: task.kind,\n state: \"reservation_refused\",\n phase: \"budget\",\n reason,\n attempt: task.attempt,\n dedupeKey: `task:${taskId}:reservation_refused:${task.attempt}`,\n })\n .catch(() => undefined);\n }\n throw new BudgetExhaustedError(taskId, month, spendUsd);\n }\n reservation = { reservedUsd: perTaskReservationUsd, reservedAt: budgetNow };\n }\n\n // A resurrected task keeps its settled cost: multi-attempt accumulation is\n // an open spec decision, so the first settlement stays authoritative and\n // the new slice is only held until then.\n const running = await stores.tasks.transition(\n taskId,\n \"running\",\n reservation === undefined\n ? undefined\n : {\n patch: {\n reservedUsd: reservation.reservedUsd,\n reservedAt: reservation.reservedAt,\n },\n },\n );\n let execution: ExecutionReference | void;\n try {\n execution = await options.launch(running);\n } catch (error) {\n const reason = (error instanceof Error ? error.message : String(error)).slice(0, 500);\n const limit = options.maxLaunchAttempts ?? DEFAULT_MAX_LAUNCH_ATTEMPTS;\n if (running.attempt < limit) {\n const retryReason = `launch failed (attempt ${running.attempt} of ${limit}), will retry: ${reason}`;\n await stores.tasks.transition(taskId, \"queued\", { reason: retryReason }).catch(() => undefined);\n // The queued transition writes no receipt; leave one so the retry is visible.\n if (running.repositoryIds !== undefined) {\n await stores.receipts\n .append({\n repositoryIds: running.repositoryIds,\n taskId,\n kind: \"task\",\n state: \"launch_retry\",\n reason: retryReason,\n dedupeKey: `task:${taskId}:launch_retry:${running.attempt}`,\n })\n .catch(() => undefined);\n }\n } else {\n await stores.tasks\n .transition(taskId, \"failed\", { reason: `launch failed after ${running.attempt} attempts: ${reason}` })\n .catch(() => undefined);\n }\n throw error;\n }\n if (execution === undefined) {\n return running;\n }\n // The run exists either way; a lost write here leaves a running task with\n // no reference, which the stall sweep still reaps.\n return stores.tasks.recordExecution(taskId, execution).catch(() => running);\n}\n","import { z } from \"zod\";\nimport { isoDateTimeSchema, schemaVersionSchema } from \"./common\";\nimport { changeIdSchema, repositoryIdSchema, taskIdSchema } from \"./id\";\n\nexport const levelSchema = z.enum([\"low\", \"medium\", \"high\"]);\n\nexport const changeStateSchema = z.enum([\n \"verifying\",\n \"ready\",\n \"merged\",\n \"deployed\",\n \"rolled_out\",\n \"rolled_back\",\n \"closed\",\n]);\n\nconst postMergeStates: readonly string[] = [\"merged\", \"deployed\", \"rolled_out\", \"rolled_back\"];\n\nexport const changeEvidenceSchema = z.strictObject({\n reproVerified: z.boolean(),\n testsAdded: z.number().int().min(0),\n checksGreen: z.boolean(),\n attackPassed: z.boolean(),\n scopeMatches: z.boolean(),\n previewUrl: z.url().optional(),\n});\n\nexport const changeSchema = z\n .strictObject({\n schemaVersion: schemaVersionSchema,\n id: changeIdSchema,\n repositoryId: repositoryIdSchema,\n taskId: taskIdSchema.optional(),\n groupId: taskIdSchema.optional(),\n prNumber: z.number().int().min(1),\n prUrl: z.url(),\n author: z.string().min(1),\n risk: levelSchema,\n confidence: levelSchema,\n evidence: changeEvidenceSchema,\n state: changeStateSchema,\n mergeSha: z\n .string()\n .regex(/^[0-9a-f]{40}$/)\n .optional()\n .describe(\"Merge commit sha recorded when the pull request merges, matched against production deployments.\"),\n decidedBy: z.string().min(1).optional(),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n })\n .superRefine((change, ctx) => {\n if (postMergeStates.includes(change.state) && change.decidedBy === undefined) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"decidedBy\"],\n message: `${change.state} changes require decidedBy`,\n });\n }\n });\n\nexport type Level = z.infer<typeof levelSchema>;\nexport type ChangeState = z.infer<typeof changeStateSchema>;\nexport type ChangeEvidence = z.infer<typeof changeEvidenceSchema>;\nexport type Change = z.infer<typeof changeSchema>;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"factory-config-BJcjsPKl.mjs","names":[],"sources":["../src/schema/repository.ts","../src/schema/budget.ts","../src/schema/factory-config.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { isoDateTimeSchema, schemaVersionSchema } from \"./common\";\nimport { repositoryIdSchema } from \"./id\";\n\nexport const repositorySlugSchema = z\n .string()\n .regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?\\/[a-z0-9._-]+$/)\n .describe(\"owner/name, lowercase, e.g. acme/web.\");\n\nexport const repositorySchema = z.strictObject({\n schemaVersion: schemaVersionSchema,\n id: repositoryIdSchema,\n slug: repositorySlugSchema,\n defaultBranch: z.string().min(1).describe(\"Branch changes land on, usually main.\"),\n enabled: z.boolean().describe(\"Whether the factory may work on this repository.\"),\n dependsOn: z\n .array(repositorySlugSchema)\n .describe(\"Slugs of repositories this one consumes, for multi-repo work ordering.\"),\n devbox: z\n .strictObject({\n projectId: z.string().min(1),\n teamId: z.string().min(1),\n snapshot: z.string().min(1).optional().describe(\"Prebuilt snapshot to boot from.\"),\n })\n .optional()\n .describe(\"Devbox execution config. Absent when this repository never uses devboxes.\"),\n connectedAt: isoDateTimeSchema,\n});\n\nexport type RepositorySlug = z.infer<typeof repositorySlugSchema>;\nexport type Repository = z.infer<typeof repositorySchema>;\n","import { z } from \"zod\";\nimport { isoDateTimeSchema, schemaVersionSchema, usdSchema } from \"./common\";\nimport { factoryIdSchema } from \"./id\";\nimport { repositorySlugSchema } from \"./repository\";\n\nexport const budgetMonthSchema = z.string().regex(/^\\d{4}-(0[1-9]|1[0-2])$/);\n\nexport const budgetLimitsSchema = z.strictObject({\n totalUsd: usdSchema,\n investigationUsd: usdSchema,\n perTaskReservationUsd: usdSchema,\n warningPercent: z.number().int().min(1).max(100),\n perRepositoryUsd: z.record(repositorySlugSchema, usdSchema).optional(),\n});\n\nexport const budgetSchema = z.strictObject({\n schemaVersion: schemaVersionSchema,\n factoryId: factoryIdSchema,\n month: budgetMonthSchema,\n limits: budgetLimitsSchema,\n notices: z.strictObject({\n warningAt: isoDateTimeSchema.optional(),\n exhaustedAt: isoDateTimeSchema.optional(),\n }),\n});\n\nexport type BudgetLimits = z.infer<typeof budgetLimitsSchema>;\nexport type Budget = z.infer<typeof budgetSchema>;\n","import { z } from \"zod\";\nimport { approvalPolicySchema, defaultApprovalPolicy } from \"../approval\";\nimport { budgetLimitsSchema } from \"./budget\";\nimport { repositorySlugSchema } from \"./repository\";\nimport { taskKindSchema } from \"./task\";\n\nexport const factoryConfigRepositorySchema = z.strictObject({\n slug: repositorySlugSchema.describe(\"Repository this factory may work on.\"),\n dependsOn: z\n .array(repositorySlugSchema)\n .default([])\n .describe(\"Repositories that must land changes before this repository.\"),\n});\n\nconst githubConnectorSchema = z.strictObject({\n owner: z\n .string()\n .regex(/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/)\n .describe(\"GitHub user or organization that owns this connector installation.\"),\n connector: z\n .string()\n .regex(/^github\\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/)\n .describe(\"Vercel Connect UID installed for this GitHub owner.\"),\n});\n\nconst slackConnectorSchema = z.strictObject({\n connector: z\n .string()\n .regex(/^slack\\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/)\n .describe(\"Vercel Connect UID installed for this Slack app.\"),\n});\n\nexport const executionPolicySchema = z.strictObject({\n maxAttempts: z\n .number()\n .int()\n .min(1)\n .describe(\"Attempts a task may consume before a stall fails it for good.\"),\n stallMinutes: z\n .number()\n .int()\n .min(1)\n .describe(\"Minutes without progress before a running task is marked stalled.\"),\n stallMinutesByKind: z\n .record(taskKindSchema, z.number().int().min(1))\n .default({})\n .describe(\"Per-kind stall windows for long single-step work, e.g. security_scan: 300.\"),\n});\n\nexport type ExecutionPolicy = z.infer<typeof executionPolicySchema>;\n\nexport const defaultExecutionPolicy: ExecutionPolicy = {\n maxAttempts: 3,\n stallMinutes: 45,\n stallMinutesByKind: {},\n};\n\nexport const factoryConfigSchema = z.strictObject({\n connectors: z\n .strictObject({\n github: z\n .array(githubConnectorSchema)\n .refine(\n (connectors) => new Set(connectors.map(({ owner }) => owner)).size === connectors.length,\n \"GitHub connector owners must be unique.\",\n ),\n slack: z\n .array(slackConnectorSchema)\n .max(1, \"A factory may configure one Slack connector.\")\n .default([]),\n })\n .default({ github: [], slack: [] })\n .describe(\"Provider connections available to this factory.\"),\n repositories: z\n .array(factoryConfigRepositorySchema)\n .min(1)\n .describe(\"Repositories this factory is allowed to inspect and change.\"),\n operators: z\n .array(z.string().min(1))\n .min(1)\n .describe(\"Provider-qualified identities allowed to operate this factory.\"),\n approval: approvalPolicySchema\n .default(defaultApprovalPolicy)\n .describe(\"Which tasks wait for a human before they run. First match wins.\"),\n budget: budgetLimitsSchema\n .default({\n totalUsd: 500,\n investigationUsd: 100,\n perTaskReservationUsd: 5,\n warningPercent: 80,\n })\n .describe(\"Monthly spend limits. One non-disableable budget per factory per month.\"),\n execution: executionPolicySchema\n .default(defaultExecutionPolicy)\n .describe(\"Stall detection and retry limits for running tasks.\"),\n});\n\nexport type FactoryConfig = z.infer<typeof factoryConfigSchema>;\nexport type FactoryConfigInput = z.input<typeof factoryConfigSchema>;\n\nexport function defineFactory(config: FactoryConfigInput): FactoryConfig {\n return factoryConfigSchema.parse(config);\n}\n"],"mappings":"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"}
@@ -1,67 +0,0 @@
1
- import { c as ReplyTo, r as SignalSource } from "./signal-CHVEmzvI.mjs";
2
- import { z } from "zod";
3
- //#region src/connectors/index.d.ts
4
- interface WebhookEvent {
5
- deliveryId: string;
6
- event: string;
7
- payload: unknown;
8
- }
9
- interface NormalizedSignal<TSource extends SignalSource = SignalSource> {
10
- source: TSource;
11
- deliveryId: string;
12
- replyTo: ReplyTo;
13
- payload: unknown;
14
- }
15
- type SignalNormalizer<TSignal extends NormalizedSignal = NormalizedSignal> = (event: WebhookEvent) => TSignal | null;
16
- declare const communicationOutcomes: readonly ["accepted", "started", "succeeded", "failed", "needs_human", "question", "digest", "rolled_back", "budget_warning", "budget_exhausted", "message"];
17
- declare const communicationOutcomeSchema: z.ZodEnum<{
18
- message: "message";
19
- succeeded: "succeeded";
20
- failed: "failed";
21
- needs_human: "needs_human";
22
- question: "question";
23
- accepted: "accepted";
24
- rolled_back: "rolled_back";
25
- started: "started";
26
- digest: "digest";
27
- budget_warning: "budget_warning";
28
- budget_exhausted: "budget_exhausted";
29
- }>;
30
- declare const communicationEventSchema: z.ZodObject<{
31
- eventId: z.ZodString;
32
- repositoryIds: z.ZodArray<z.ZodString>;
33
- taskId: z.ZodOptional<z.ZodString>;
34
- changeId: z.ZodOptional<z.ZodString>;
35
- replyTo: z.ZodObject<{
36
- channel: z.ZodString;
37
- address: z.ZodString;
38
- }, z.core.$strict>;
39
- outcome: z.ZodEnum<{
40
- message: "message";
41
- succeeded: "succeeded";
42
- failed: "failed";
43
- needs_human: "needs_human";
44
- question: "question";
45
- accepted: "accepted";
46
- rolled_back: "rolled_back";
47
- started: "started";
48
- digest: "digest";
49
- budget_warning: "budget_warning";
50
- budget_exhausted: "budget_exhausted";
51
- }>;
52
- data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
53
- }, z.core.$strict>;
54
- type CommunicationOutcome = z.infer<typeof communicationOutcomeSchema>;
55
- type CommunicationEvent = z.infer<typeof communicationEventSchema>;
56
- interface CommunicationAdapter {
57
- readonly channel: string;
58
- deliver(event: CommunicationEvent): Promise<void>;
59
- }
60
- interface CommunicationRouter {
61
- readonly channels: readonly string[];
62
- deliver(event: CommunicationEvent): Promise<void>;
63
- }
64
- declare function createCommunicationRouter(adapters: readonly CommunicationAdapter[]): CommunicationRouter;
65
- //#endregion
66
- export { NormalizedSignal as a, communicationEventSchema as c, createCommunicationRouter as d, CommunicationRouter as i, communicationOutcomeSchema as l, CommunicationEvent as n, SignalNormalizer as o, CommunicationOutcome as r, WebhookEvent as s, CommunicationAdapter as t, communicationOutcomes as u };
67
- //# sourceMappingURL=index-Cy4jfXh5.d.mts.map
@@ -1,59 +0,0 @@
1
- import { z } from "zod";
2
- //#region src/schema/common.d.ts
3
- declare const SCHEMA_VERSION = 1;
4
- declare const schemaVersionSchema: z.ZodLiteral<1>;
5
- declare const isoDateTimeSchema: z.ZodISODateTime;
6
- declare const usdSchema: z.ZodNumber;
7
- /**
8
- * Open vocabulary value. Standard values ship as exported constants; custom
9
- * values matching the pattern are equally valid.
10
- */
11
- declare const vocabularySchema: z.ZodString;
12
- declare const replyToSchema: z.ZodObject<{
13
- channel: z.ZodString;
14
- address: z.ZodString;
15
- }, z.core.$strict>;
16
- type ReplyTo = z.infer<typeof replyToSchema>;
17
- //#endregion
18
- //#region src/schema/signal.d.ts
19
- /** Sources used by the standard connectors. Any vocabulary value is valid. */
20
- declare const standardSignalSources: readonly ["github_issue", "github_pr", "github_pr_comment", "slack", "human", "github_ci_failure", "vercel_deploy_failure", "security_scan", "investigation", "api"];
21
- declare const signalSourceSchema: z.ZodString;
22
- declare const signalOutcomeSchema: z.ZodEnum<{
23
- accepted: "accepted";
24
- duplicate: "duplicate";
25
- unactionable: "unactionable";
26
- low_confidence: "low_confidence";
27
- ignored: "ignored";
28
- pending: "pending";
29
- }>;
30
- declare const signalSchema: z.ZodObject<{
31
- schemaVersion: z.ZodLiteral<1>;
32
- id: z.ZodString;
33
- repositoryId: z.ZodString;
34
- source: z.ZodString;
35
- deliveryId: z.ZodString;
36
- replyTo: z.ZodObject<{
37
- channel: z.ZodString;
38
- address: z.ZodString;
39
- }, z.core.$strict>;
40
- payload: z.ZodUnknown;
41
- receivedAt: z.ZodISODateTime;
42
- outcome: z.ZodEnum<{
43
- accepted: "accepted";
44
- duplicate: "duplicate";
45
- unactionable: "unactionable";
46
- low_confidence: "low_confidence";
47
- ignored: "ignored";
48
- pending: "pending";
49
- }>;
50
- detail: z.ZodOptional<z.ZodString>;
51
- reason: z.ZodOptional<z.ZodString>;
52
- decidedAt: z.ZodOptional<z.ZodISODateTime>;
53
- }, z.core.$strict>;
54
- type SignalSource = z.infer<typeof signalSourceSchema>;
55
- type SignalOutcome = z.infer<typeof signalOutcomeSchema>;
56
- type Signal = z.infer<typeof signalSchema>;
57
- //#endregion
58
- export { signalSchema as a, ReplyTo as c, replyToSchema as d, schemaVersionSchema as f, signalOutcomeSchema as i, SCHEMA_VERSION as l, vocabularySchema as m, SignalOutcome as n, signalSourceSchema as o, usdSchema as p, SignalSource as r, standardSignalSources as s, Signal as t, isoDateTimeSchema as u };
59
- //# sourceMappingURL=signal-CHVEmzvI.d.mts.map