@vercel/factory 0.0.12 → 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 (57) 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-DdvEKLiE.mjs → approval-JX2Px9IC.mjs} +2 -2
  6. package/dist/{approval-DdvEKLiE.mjs.map → approval-JX2Px9IC.mjs.map} +1 -1
  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.d.mts +1 -1
  13. package/dist/build-factory.mjs +1 -1
  14. package/dist/change-D2U6v7Ek.mjs +554 -0
  15. package/dist/change-D2U6v7Ek.mjs.map +1 -0
  16. package/dist/communication-Kd9nU5RP.mjs +30 -0
  17. package/dist/communication-Kd9nU5RP.mjs.map +1 -0
  18. package/dist/connectors/index.d.mts +3 -2
  19. package/dist/connectors/index.mjs +53 -26
  20. package/dist/connectors/index.mjs.map +1 -1
  21. package/dist/connectors/slack.d.mts +17 -6
  22. package/dist/connectors/slack.mjs +99 -32
  23. package/dist/connectors/slack.mjs.map +1 -1
  24. package/dist/coordination-Cq08hb1l.mjs +76 -0
  25. package/dist/coordination-Cq08hb1l.mjs.map +1 -0
  26. package/dist/dispatch-BaZS3PT8.d.mts +76 -0
  27. package/dist/{driver-CrZsy7sm.d.mts → driver-BC0Mt5KG.d.mts} +2 -2
  28. package/dist/driver-Bbd25rNM.mjs.map +1 -1
  29. package/dist/engine-DFHU2Pe-.d.mts +781 -0
  30. package/dist/engine-WaUM2cGA.mjs +1068 -0
  31. package/dist/engine-WaUM2cGA.mjs.map +1 -0
  32. package/dist/eve/index.d.mts +289 -153
  33. package/dist/eve/index.mjs +378 -89
  34. package/dist/eve/index.mjs.map +1 -1
  35. package/dist/{factory-config-ifd3JxiU.mjs → factory-config-BoRXOw43.mjs} +23 -22
  36. package/dist/factory-config-BoRXOw43.mjs.map +1 -0
  37. package/dist/{factory-config-Bu4mklm9.d.mts → factory-config-D2yU3G0c.d.mts} +21 -2
  38. package/dist/{github-wThBfbm-.d.mts → github-C5HNBN6p.d.mts} +34 -3
  39. package/dist/github.d.mts +2 -2
  40. package/dist/github.mjs +61 -1
  41. package/dist/github.mjs.map +1 -1
  42. package/dist/{id-DwCihQr4.mjs → id-BLIkCVue.mjs} +4 -2
  43. package/dist/{id-DwCihQr4.mjs.map → id-BLIkCVue.mjs.map} +1 -1
  44. package/dist/index-Bw6hggoH.d.mts +40 -0
  45. package/dist/index.d.mts +291 -61
  46. package/dist/index.mjs +140 -783
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/postgres/index.d.mts +1 -1
  49. package/dist/task-CcOyOJ3k.mjs +154 -0
  50. package/dist/task-CcOyOJ3k.mjs.map +1 -0
  51. package/package.json +5 -1
  52. package/dist/approval-CIXB8IF8.d.mts +0 -522
  53. package/dist/change-CFNGXVqD.mjs +0 -230
  54. package/dist/change-CFNGXVqD.mjs.map +0 -1
  55. package/dist/factory-config-ifd3JxiU.mjs.map +0 -1
  56. package/dist/index-Cy4jfXh5.d.mts +0 -67
  57. package/dist/signal-CHVEmzvI.d.mts +0 -59
package/dist/index.mjs CHANGED
@@ -1,10 +1,48 @@
1
- import { a as repositoryIdsSchema, c as SCHEMA_VERSION, d as schemaVersionSchema, f as usdSchema, i as repositoryIdSchema, l as isoDateTimeSchema, n as factoryIdSchema, o as signalIdSchema, p as vocabularySchema, r as receiptIdSchema, s as taskIdSchema, t as changeIdSchema, u as replyToSchema } from "./id-DwCihQr4.mjs";
2
- import { a as findingCategorySchema, c as findingSchema, d as findingVerificationSchema, i as requiresApproval, l as findingVerificationLevelSchema, n as approvalRuleSchema, o as findingFingerprint, r as defaultApprovalPolicy, s as findingPrioritySchema, t as approvalPolicySchema, u as findingVerificationMethodSchema } from "./approval-DdvEKLiE.mjs";
1
+ import { a as receiptIdSchema, c as signalIdSchema, d as isoDateTimeSchema, f as replyToSchema, h as vocabularySchema, i as outboxIdSchema, l as taskIdSchema, m as usdSchema, n as changeIdSchema, o as repositoryIdSchema, p as schemaVersionSchema, r as factoryIdSchema, s as repositoryIdsSchema, t as answerIdSchema, u as SCHEMA_VERSION } from "./id-BLIkCVue.mjs";
2
+ import { a as taskApprovalSchema, c as taskOriginSchema, d as taskStateSchema, f as taskUsageSchema, i as standardTaskKinds, l as taskQuestionSchema, n as executionReferenceSchema, o as taskExecutionRepositoryIdsSchema, r as harnessSchema, s as taskKindSchema, t as coordinationRepositoryIdsSchema, u as taskSchema } from "./task-CcOyOJ3k.mjs";
3
+ import { a as findingCategorySchema, c as findingSchema, d as findingVerificationSchema, i as requiresApproval, l as findingVerificationLevelSchema, n as approvalRuleSchema, o as findingFingerprint, r as defaultApprovalPolicy, s as findingPrioritySchema, t as approvalPolicySchema, u as findingVerificationMethodSchema } from "./approval-JX2Px9IC.mjs";
4
+ import { C as computeMonthSpendUsd, E as sweepSettlements, S as canReserve, T as sweepBudget, _ as taskInitialState, a as BudgetExhaustedError, b as budgetMonthOf, c as dispatchTask, d as changeInitialState, f as changeTransitions, g as signalOutcomeTransitions, h as signalInitialOutcome, i as levelSchema, l as InvalidTransitionError, m as legalNextStates, n as changeSchema, o as DEFAULT_MAX_LAUNCH_ATTEMPTS, p as isTransitionAllowed, r as changeStateSchema, s as DispatchRefusedError, t as changeEvidenceSchema, u as assertTransition, v as taskTransitions, w as deriveFactoryId, x as budgetStatus, y as terminalStates } from "./change-D2U6v7Ek.mjs";
3
5
  import { n as RecordNotFoundError, r as VersionConflictError, t as RecordExistsError } from "./driver-Bbd25rNM.mjs";
4
- import { _ as taskSchema, a as DEFAULT_MAX_LAUNCH_ATTEMPTS, c as coordinationRepositoryIdsSchema, d as taskApprovalSchema, f as taskDevboxSchema, g as taskQuestionSchema, h as taskOriginSchema, i as levelSchema, l as harnessSchema, m as taskKindSchema, n as changeSchema, o as DispatchRefusedError, p as taskExecutionRepositoryIdsSchema, r as changeStateSchema, s as dispatchTask, t as changeEvidenceSchema, u as standardTaskKinds, v as taskStateSchema } from "./change-CFNGXVqD.mjs";
5
- import { a as repositorySlugSchema, i as repositorySchema, n as factoryConfigRepositorySchema, r as factoryConfigSchema, t as defineFactory } from "./factory-config-ifd3JxiU.mjs";
6
+ import { n as reconcileCoordination, t as createCoordination } from "./coordination-Cq08hb1l.mjs";
7
+ import { a as repositorySlugSchema, i as repositorySchema, n as budgetMonthSchema, r as budgetSchema, t as budgetLimitsSchema } from "./budget-DzSiIA4t.mjs";
8
+ import { C as standardReceiptPhases, S as standardReceiptKinds, T as answerEventSchema, _ as costSchema, a as SessionConflictError, b as receiptSchema, c as agentSessionIdSchema, d as signalSchema, f as signalSourceSchema, g as outboxStateSchema, h as outboxQueueSchema, i as OutboxEventConflictError, l as agentSessionSchema, m as outboxMessageSchema, n as ApprovalError, o as SettlementError, p as standardSignalSources, r as ChangeAssociationError, s as createStores, t as AnswerEventConflictError, u as signalOutcomeSchema, v as receiptKindSchema, w as standardReceiptStates, x as receiptStateSchema, y as receiptPhaseSchema } from "./engine-WaUM2cGA.mjs";
9
+ import { n as communicationOutcomeSchema, r as communicationOutcomes, t as communicationEventSchema } from "./communication-Kd9nU5RP.mjs";
10
+ import { a as factoryConfigSchema, i as factoryConfigRepositorySchema, n as defineFactory, r as executionPolicySchema, t as defaultExecutionPolicy } from "./factory-config-BoRXOw43.mjs";
6
11
  import { z } from "zod";
7
- import { createHash, randomUUID } from "node:crypto";
12
+ //#region src/agent-routes.ts
13
+ const agentRouteSchema = z.strictObject({
14
+ id: z.string().regex(/^[a-z][a-z0-9-]{0,63}$/u).describe("Stable route selected by a conversational agent or workflow."),
15
+ agent: z.string().regex(/^[a-z][a-z0-9-]{0,31}$/u).describe("Internally addressable Eve agent that executes this route."),
16
+ taskKind: taskKindSchema.describe("Factory task kind created for this route."),
17
+ description: z.string().min(1).max(500).describe("Model-facing guidance for when this route is appropriate."),
18
+ permissions: z.array(z.string().min(1)).default([]).describe("Inspectable capabilities granted to the target agent.")
19
+ });
20
+ /**
21
+ * Define the application-owned routes an agent or workflow may select. Route
22
+ * descriptions inform judgment; this validated registry remains the authority.
23
+ */
24
+ function defineAgentRoutes(inputs) {
25
+ const routes = inputs.map((input) => agentRouteSchema.parse(input));
26
+ const byId = /* @__PURE__ */ new Map();
27
+ const byTaskKind = /* @__PURE__ */ new Map();
28
+ for (const route of routes) {
29
+ if (byId.has(route.id)) throw new Error(`Agent route ${route.id} is registered more than once.`);
30
+ if (byTaskKind.has(route.taskKind)) throw new Error(`Task kind ${route.taskKind} is handled by more than one agent route.`);
31
+ byId.set(route.id, route);
32
+ byTaskKind.set(route.taskKind, route);
33
+ }
34
+ const frozen = Object.freeze(routes.map((route) => Object.freeze(route)));
35
+ return Object.freeze({
36
+ routes: frozen,
37
+ get(id) {
38
+ return byId.get(id);
39
+ },
40
+ forTaskKind(kind) {
41
+ return byTaskKind.get(kind);
42
+ }
43
+ });
44
+ }
45
+ //#endregion
8
46
  //#region src/briefs.ts
9
47
  /** The finding behind a task, when its signal carries one. */
10
48
  async function findingForTask(stores, task) {
@@ -48,8 +86,6 @@ function verificationBrief(task, repository, change) {
48
86
  //#endregion
49
87
  //#region src/changes.ts
50
88
  async function adoptPullRequest(stores, input) {
51
- const existing = (await stores.changes.list()).find((change) => change.repositoryId === input.repositoryId && change.prNumber === input.prNumber && (change.state === "verifying" || change.state === "ready"));
52
- if (existing !== void 0) return existing;
53
89
  return stores.changes.create({
54
90
  ...input,
55
91
  risk: "low",
@@ -64,76 +100,6 @@ async function adoptPullRequest(stores, input) {
64
100
  });
65
101
  }
66
102
  //#endregion
67
- //#region src/coordination.ts
68
- /** Create one non-executable parent and one independently executable child per repository. */
69
- async function createCoordination(stores, input) {
70
- const repositoryIds = [...new Set(input.repositoryIds)];
71
- if (repositoryIds.length < 2) throw new Error("coordination requires at least two distinct repositories");
72
- const parent = await stores.tasks.create({
73
- repositoryIds,
74
- kind: "coordination",
75
- origin: { signalId: input.signalId },
76
- replyTo: input.replyTo
77
- });
78
- return {
79
- parent: await stores.tasks.transition(parent.id, "running", { reason: "repository-scoped child tasks created" }),
80
- children: await Promise.all(repositoryIds.map((repositoryId) => stores.tasks.create({
81
- repositoryIds: [repositoryId],
82
- kind: "code_change",
83
- origin: { parentTaskId: parent.id },
84
- replyTo: input.replyTo
85
- })))
86
- };
87
- }
88
- /** Reconcile a coordination parent from its direct children without ever dispatching the parent. */
89
- async function reconcileCoordination(stores, parentTaskId, options) {
90
- const parent = await stores.tasks.get(parentTaskId);
91
- if (parent === null) throw new RecordNotFoundError("tasks", parentTaskId);
92
- if (parent.kind !== "coordination") throw new Error(`task ${parentTaskId} is ${parent.kind}, not coordination`);
93
- const children = (await stores.tasks.list()).filter((task) => task.origin.parentTaskId === parentTaskId);
94
- if (children.length !== parent.repositoryIds.length) return {
95
- status: "pending",
96
- parent,
97
- children
98
- };
99
- if (parent.state === "succeeded" || parent.state === "failed") return {
100
- status: parent.state,
101
- transitioned: false,
102
- parent,
103
- children
104
- };
105
- if (children.some((child) => child.state === "failed" || child.state === "cancelled")) {
106
- await options.present({
107
- status: "failed",
108
- parent,
109
- children
110
- });
111
- return {
112
- status: "failed",
113
- transitioned: true,
114
- parent: await stores.tasks.transition(parent.id, "failed", { reason: "one or more repository tasks failed" }),
115
- children
116
- };
117
- }
118
- if (!children.every((child) => child.state === "succeeded")) return {
119
- status: "pending",
120
- parent,
121
- children
122
- };
123
- await options.present({
124
- status: "succeeded",
125
- parent,
126
- children
127
- });
128
- await stores.tasks.transition(parent.id, "verifying", { reason: "all repository tasks succeeded" });
129
- return {
130
- status: "succeeded",
131
- transitioned: true,
132
- parent: await stores.tasks.transition(parent.id, "succeeded", { reason: "aggregate multi-repository handoff is ready" }),
133
- children
134
- };
135
- }
136
- //#endregion
137
103
  //#region src/metrics.ts
138
104
  const bucketTable = {
139
105
  low: {
@@ -258,15 +224,17 @@ function computeLedgerMetrics(rows, options = {}) {
258
224
  }
259
225
  const openChangeStates = /* @__PURE__ */ new Set(["verifying", "ready"]);
260
226
  /**
261
- * Green, yellow, or red from rows, never from a model. Budget and onboarding
262
- * signals are not evaluated yet: budgets do not settle and onboarding
263
- * investigations are not recorded, so those spec rules have no rows to read.
227
+ * Green, yellow, or red from rows, never from a model. Budget threshold and
228
+ * onboarding rules are not evaluated here yet: budget notices live on the
229
+ * budgets collection (not in these rows), and onboarding investigations are
230
+ * not recorded.
264
231
  */
265
232
  function computeRepositoryHealth(rows, options = {}) {
266
233
  const now = (options.now ?? /* @__PURE__ */ new Date()).getTime();
267
234
  const dayMs = 864e5;
268
235
  const red = [];
269
236
  const yellow = [];
237
+ const tasksById = new Map(rows.tasks.map((task) => [task.id, task]));
270
238
  for (const receipt of rows.receipts) {
271
239
  if (now - Date.parse(receipt.recordedAt) >= dayMs) continue;
272
240
  if (receipt.state === "rolled_back") red.push(`rollback in the last 24h (${receipt.receiptId})`);
@@ -276,6 +244,12 @@ function computeRepositoryHealth(rows, options = {}) {
276
244
  if (openQuestions > 0) yellow.push(`${openQuestions} open question(s)`);
277
245
  const needsHuman = rows.tasks.filter((task) => task.kind !== "question" && task.state === "needs_human").length;
278
246
  if (needsHuman > 0) yellow.push(`${needsHuman} task(s) waiting on a human`);
247
+ const maxQueuedMinutes = options.maxQueuedMinutes ?? 5;
248
+ const staleQueued = rows.tasks.filter((task) => {
249
+ if (task.state !== "queued" || task.kind === "question" || task.kind === "coordination" || task.approval === "required") return false;
250
+ return (task.dependsOn ?? []).every((dependencyId) => tasksById.get(dependencyId)?.changeId !== void 0) && now - Date.parse(task.updatedAt) >= maxQueuedMinutes * 60 * 1e3;
251
+ }).length;
252
+ if (staleQueued > 0) yellow.push(`${staleQueued} eligible task(s) queued for at least ${maxQueuedMinutes} minutes`);
279
253
  const blocked = rows.changes.filter((change) => openChangeStates.has(change.state) && reviewBucket(change.risk, change.confidence) === "blocked").length;
280
254
  if (blocked > 0) yellow.push(`${blocked} change(s) in the blocked bucket`);
281
255
  if (options.maxOpenChanges !== void 0) {
@@ -296,365 +270,6 @@ function computeRepositoryHealth(rows, options = {}) {
296
270
  };
297
271
  }
298
272
  //#endregion
299
- //#region src/schema/budget.ts
300
- const budgetMonthSchema = z.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/);
301
- const budgetSchema = z.strictObject({
302
- schemaVersion: schemaVersionSchema,
303
- factoryId: factoryIdSchema,
304
- month: budgetMonthSchema,
305
- limits: z.strictObject({
306
- totalUsd: usdSchema,
307
- investigationUsd: usdSchema,
308
- perTaskReservationUsd: usdSchema,
309
- warningPercent: z.number().int().min(1).max(100),
310
- perRepositoryUsd: z.record(repositorySlugSchema, usdSchema).optional()
311
- }),
312
- notices: z.strictObject({
313
- warningAt: isoDateTimeSchema.optional(),
314
- exhaustedAt: isoDateTimeSchema.optional()
315
- })
316
- });
317
- //#endregion
318
- //#region src/schema/receipt.ts
319
- /** States the engine writes on transitions. Any vocabulary value is valid. */
320
- const standardReceiptStates = [
321
- "approved",
322
- "rejected",
323
- "dispatched",
324
- "succeeded",
325
- "failed",
326
- "needs_human",
327
- "cancelled",
328
- "settled",
329
- "ready",
330
- "merged",
331
- "deployed",
332
- "rolled_out",
333
- "rolled_back"
334
- ];
335
- /** Phases the standard loop uses. Any vocabulary value is valid. */
336
- const standardReceiptPhases = [
337
- "approval",
338
- "budget",
339
- "dispatch",
340
- "implementation",
341
- "verification",
342
- "pull_request",
343
- "cleanup"
344
- ];
345
- /** Kinds the standard loop writes: the standard task kinds plus "change". */
346
- const standardReceiptKinds = [...standardTaskKinds, "change"];
347
- const receiptStateSchema = vocabularySchema.describe("What happened, e.g. dispatched, settled, merged, or a custom event name.");
348
- const receiptPhaseSchema = vocabularySchema.describe("Where in the process it happened, e.g. dispatch or verification.");
349
- const receiptKindSchema = vocabularySchema.describe("What the receipt is about: a task kind, change, or a custom kind.");
350
- const costSchema = z.strictObject({ totalUsd: usdSchema });
351
- const receiptSchema = z.strictObject({
352
- schemaVersion: schemaVersionSchema,
353
- receiptId: receiptIdSchema,
354
- repositoryIds: repositoryIdsSchema.describe("Repositories the recorded event concerns."),
355
- taskId: taskIdSchema.optional(),
356
- changeId: changeIdSchema.optional(),
357
- kind: receiptKindSchema,
358
- state: receiptStateSchema,
359
- phase: receiptPhaseSchema.optional(),
360
- reason: z.string().min(1).max(2e3).optional().describe("Why it happened. Scrubbed and truncated; never secrets."),
361
- attempt: z.number().int().min(1).describe("Attempt the event belongs to."),
362
- cost: costSchema.optional().describe("Measured cost. Settled receipts only."),
363
- decidedBy: z.string().min(1).optional().describe("Human who made the decision. Change and approval receipts only."),
364
- recordedAt: isoDateTimeSchema
365
- }).superRefine((receipt, ctx) => {
366
- if (receipt.state === "settled" && receipt.cost === void 0) ctx.addIssue({
367
- code: "custom",
368
- path: ["cost"],
369
- message: "settled receipts require cost"
370
- });
371
- if (receipt.state !== "settled" && receipt.cost !== void 0) ctx.addIssue({
372
- code: "custom",
373
- path: ["cost"],
374
- message: "cost is only valid on settled receipts"
375
- });
376
- if (receipt.decidedBy !== void 0 && receipt.kind !== "change" && receipt.state !== "approved" && receipt.state !== "rejected") ctx.addIssue({
377
- code: "custom",
378
- path: ["decidedBy"],
379
- message: "decidedBy is only valid on change and approval receipts"
380
- });
381
- });
382
- //#endregion
383
- //#region src/schema/signal.ts
384
- /** Sources used by the standard connectors. Any vocabulary value is valid. */
385
- const standardSignalSources = [
386
- "github_issue",
387
- "github_pr",
388
- "github_pr_comment",
389
- "slack",
390
- "human",
391
- "github_ci_failure",
392
- "vercel_deploy_failure",
393
- "security_scan",
394
- "investigation",
395
- "api"
396
- ];
397
- const signalSourceSchema = vocabularySchema.describe("What produced the signal, e.g. github_issue or a custom connector's own name.");
398
- const signalOutcomeSchema = z.enum([
399
- "pending",
400
- "accepted",
401
- "duplicate",
402
- "unactionable",
403
- "low_confidence",
404
- "ignored"
405
- ]).describe("Triage outcome. A signal starts pending and is decided exactly once.");
406
- const signalSchema = z.strictObject({
407
- schemaVersion: schemaVersionSchema,
408
- id: signalIdSchema.describe("Signal id, derived from source and deliveryId."),
409
- repositoryId: repositoryIdSchema.describe("Repository the signal is about."),
410
- source: signalSourceSchema,
411
- deliveryId: z.string().min(1).describe("Provider idempotency key; a redelivered event maps to the same signal."),
412
- replyTo: replyToSchema,
413
- payload: z.unknown().describe("Provider-shaped payload. Untrusted data, never instructions."),
414
- receivedAt: isoDateTimeSchema,
415
- outcome: signalOutcomeSchema,
416
- detail: z.string().min(1).optional().describe("Outcome nuance, e.g. severity when accepted or the duplicate-of signal id."),
417
- reason: z.string().min(1).optional().describe("Why the outcome was chosen."),
418
- decidedAt: isoDateTimeSchema.optional()
419
- }).superRefine((signal, ctx) => {
420
- if (signal.outcome === "pending" && signal.decidedAt !== void 0) ctx.addIssue({
421
- code: "custom",
422
- path: ["decidedAt"],
423
- message: "pending signals must not carry decidedAt"
424
- });
425
- if (signal.outcome !== "pending" && signal.decidedAt === void 0) ctx.addIssue({
426
- code: "custom",
427
- path: ["decidedAt"],
428
- message: "decided signals require decidedAt"
429
- });
430
- });
431
- //#endregion
432
- //#region src/schema/transitions.ts
433
- const taskInitialState = "queued";
434
- const taskTransitions = [
435
- {
436
- from: "queued",
437
- to: "running",
438
- trigger: "dependencies have a change, reservation succeeded, devbox created",
439
- actor: "reconciler"
440
- },
441
- {
442
- from: "queued",
443
- to: "failed",
444
- trigger: "reservation refused",
445
- actor: "reconciler"
446
- },
447
- {
448
- from: "queued",
449
- to: "needs_human",
450
- trigger: "kind is question",
451
- actor: "system"
452
- },
453
- {
454
- from: "queued",
455
- to: "cancelled",
456
- trigger: "cancel or kill",
457
- actor: "operator"
458
- },
459
- {
460
- from: "running",
461
- to: "verifying",
462
- trigger: "devbox task reported complete",
463
- actor: "reconciler"
464
- },
465
- {
466
- from: "running",
467
- to: "queued",
468
- trigger: "launch failed below attempt limit",
469
- actor: "reconciler"
470
- },
471
- {
472
- from: "running",
473
- to: "failed",
474
- trigger: "devbox task errored, deadline exceeded, or launch failed at attempt limit",
475
- actor: "reconciler"
476
- },
477
- {
478
- from: "running",
479
- to: "needs_human",
480
- trigger: "devbox task requires attention",
481
- actor: "reconciler"
482
- },
483
- {
484
- from: "running",
485
- to: "cancelled",
486
- trigger: "cancel or kill",
487
- actor: "operator"
488
- },
489
- {
490
- from: "verifying",
491
- to: "succeeded",
492
- trigger: "kind criterion true",
493
- actor: "reconciler"
494
- },
495
- {
496
- from: "verifying",
497
- to: "running",
498
- trigger: "verification failed below attempt limit",
499
- actor: "reconciler"
500
- },
501
- {
502
- from: "verifying",
503
- to: "failed",
504
- trigger: "verification failed at attempt limit",
505
- actor: "reconciler"
506
- },
507
- {
508
- from: "verifying",
509
- to: "cancelled",
510
- trigger: "cancel or kill",
511
- actor: "operator"
512
- },
513
- {
514
- from: "needs_human",
515
- to: "succeeded",
516
- trigger: "question answered",
517
- actor: "operator"
518
- },
519
- {
520
- from: "needs_human",
521
- to: "queued",
522
- trigger: "operator retry",
523
- actor: "operator"
524
- },
525
- {
526
- from: "needs_human",
527
- to: "cancelled",
528
- trigger: "cancel or kill",
529
- actor: "operator"
530
- },
531
- {
532
- from: "failed",
533
- to: "queued",
534
- trigger: "operator retry",
535
- actor: "operator"
536
- }
537
- ];
538
- const changeInitialState = "verifying";
539
- const changeTransitions = [
540
- {
541
- from: "verifying",
542
- to: "ready",
543
- trigger: "verification task succeeded, risk and confidence written",
544
- actor: "reconciler"
545
- },
546
- {
547
- from: "verifying",
548
- to: "closed",
549
- trigger: "verification failed at attempt limit or pr closed upstream",
550
- actor: "reconciler"
551
- },
552
- {
553
- from: "ready",
554
- to: "merged",
555
- trigger: "pr merged on github",
556
- actor: "webhook"
557
- },
558
- {
559
- from: "ready",
560
- to: "closed",
561
- trigger: "pr closed without merge",
562
- actor: "webhook"
563
- },
564
- {
565
- from: "merged",
566
- to: "deployed",
567
- trigger: "production deployment ready containing the merge sha",
568
- actor: "webhook"
569
- },
570
- {
571
- from: "deployed",
572
- to: "rolled_out",
573
- trigger: "no flag, or flag ramped to full and held",
574
- actor: "foreman"
575
- },
576
- {
577
- from: "deployed",
578
- to: "rolled_back",
579
- trigger: "rollback rule crossed",
580
- actor: "foreman"
581
- },
582
- {
583
- from: "rolled_out",
584
- to: "rolled_back",
585
- trigger: "rollback rule crossed or flag flipped off",
586
- actor: "foreman"
587
- },
588
- {
589
- from: "rolled_back",
590
- to: "deployed",
591
- trigger: "human retried the ramp",
592
- actor: "human"
593
- }
594
- ];
595
- const signalInitialOutcome = "pending";
596
- const signalOutcomeTransitions = [
597
- {
598
- from: "pending",
599
- to: "accepted",
600
- trigger: "classified as actionable",
601
- actor: "reconciler"
602
- },
603
- {
604
- from: "pending",
605
- to: "duplicate",
606
- trigger: "classified as duplicate",
607
- actor: "reconciler"
608
- },
609
- {
610
- from: "pending",
611
- to: "unactionable",
612
- trigger: "classified as unactionable",
613
- actor: "reconciler"
614
- },
615
- {
616
- from: "pending",
617
- to: "low_confidence",
618
- trigger: "classified below confidence threshold",
619
- actor: "reconciler"
620
- },
621
- {
622
- from: "pending",
623
- to: "ignored",
624
- trigger: "classified as ignorable",
625
- actor: "reconciler"
626
- }
627
- ];
628
- function isTransitionAllowed(transitions, from, to) {
629
- return transitions.some((transition) => transition.from === from && transition.to === to);
630
- }
631
- function legalNextStates(transitions, from) {
632
- return [...new Set(transitions.filter((transition) => transition.from === from).map((t) => t.to))];
633
- }
634
- var InvalidTransitionError = class extends Error {
635
- from;
636
- to;
637
- legalNextStates;
638
- constructor(from, to, legal) {
639
- super(legal.length > 0 ? `invalid transition from "${from}" to "${to}"; legal next states: ${legal.map((state) => `"${state}"`).join(", ")}` : `invalid transition from "${from}" to "${to}"; "${from}" is terminal`);
640
- this.name = "InvalidTransitionError";
641
- this.from = from;
642
- this.to = to;
643
- this.legalNextStates = legal;
644
- }
645
- };
646
- function assertTransition(transitions, from, to) {
647
- if (!isTransitionAllowed(transitions, from, to)) throw new InvalidTransitionError(from, to, legalNextStates(transitions, from));
648
- }
649
- function terminalStates(transitions) {
650
- const states = /* @__PURE__ */ new Set();
651
- for (const transition of transitions) {
652
- states.add(transition.from);
653
- states.add(transition.to);
654
- }
655
- return [...states].filter((state) => !transitions.some((transition) => transition.from === state));
656
- }
657
- //#endregion
658
273
  //#region src/signals.ts
659
274
  /**
660
275
  * Bind normalized intake to signal creation alone. Connectors and API routes
@@ -667,346 +282,62 @@ function createSignalCreator(stores) {
667
282
  } };
668
283
  }
669
284
  //#endregion
670
- //#region src/store/engine.ts
671
- /** An approval decision that does not apply to the task's current approval or state. */
672
- var ApprovalError = class extends Error {
673
- taskId;
674
- reason;
675
- constructor(taskId, reason) {
676
- super(`approval decision on "${taskId}" refused: ${reason}`);
677
- this.name = "ApprovalError";
678
- this.taskId = taskId;
679
- this.reason = reason;
680
- }
681
- };
682
- var ChangeAssociationError = class extends Error {
683
- taskId;
684
- repositoryId;
685
- reason;
686
- constructor(taskId, repositoryId, reason) {
687
- super(`change for task "${taskId}" refused: ${reason}`);
688
- this.name = "ChangeAssociationError";
689
- this.taskId = taskId;
690
- this.repositoryId = repositoryId;
691
- this.reason = reason;
692
- }
693
- };
694
- const taskReceiptStates = {
695
- running: "dispatched",
696
- succeeded: "succeeded",
697
- failed: "failed",
698
- needs_human: "needs_human",
699
- cancelled: "cancelled"
700
- };
701
- const changeReceiptStates = {
702
- ready: "ready",
703
- merged: "merged",
704
- deployed: "deployed",
705
- rolled_out: "rolled_out",
706
- rolled_back: "rolled_back"
707
- };
708
- function randomId(prefix) {
709
- return `${prefix}_${randomUUID().replaceAll("-", "")}`;
710
- }
711
- function derivedId(prefix, key) {
712
- return `${prefix}_${createHash("sha256").update(key).digest("hex").slice(0, 32)}`;
285
+ //#region src/stall.ts
286
+ function stallWindowMinutes(policy, kind) {
287
+ return policy.stallMinutesByKind[kind] ?? policy.stallMinutes;
713
288
  }
714
- function bumpsAttempt(from, to) {
715
- return to === "queued" || from === "verifying" && to === "running";
716
- }
717
- function createStores(options) {
718
- const { driver } = options;
719
- const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
720
- async function read(collection, id, schema) {
721
- const record = await driver.get(collection, id);
722
- if (record === null) return null;
723
- return {
724
- value: schema.parse(record.value),
725
- version: record.version
726
- };
727
- }
728
- async function mustRead(collection, id, schema) {
729
- const record = await read(collection, id, schema);
730
- if (record === null) throw new RecordNotFoundError(collection, id);
731
- return record;
732
- }
733
- async function listAll(collection, schema) {
734
- return (await driver.list(collection)).map((entry) => schema.parse(entry.record.value));
735
- }
736
- async function insertReceipt(receiptId, receipt) {
737
- const full = receiptSchema.parse({
738
- ...receipt,
739
- schemaVersion: 1,
740
- receiptId,
741
- recordedAt: now()
742
- });
289
+ /**
290
+ * The reconciler's stall pass. A running task whose execution has not
291
+ * progressed within its stall window is marked stalled; a stalled task is
292
+ * re-queued for a replacement attempt while attempts remain, and failed once
293
+ * they are exhausted. Only `running` tasks can stall: a task whose result has
294
+ * already been recorded (verifying, or terminal) is never retried by this
295
+ * pass. Coordination parents run only through their children and are skipped.
296
+ */
297
+ async function sweepStalled(stores, options) {
298
+ const nowMs = (options.now ?? (() => /* @__PURE__ */ new Date()))().getTime();
299
+ const result = {
300
+ stalled: [],
301
+ requeued: [],
302
+ failed: []
303
+ };
304
+ const tasks = await stores.tasks.list();
305
+ for (const task of tasks) {
306
+ if (task.state !== "running" || task.kind === "coordination") continue;
307
+ const minutes = stallWindowMinutes(options.policy, task.kind);
308
+ const sinceProgress = nowMs - Date.parse(task.lastProgressAt ?? task.updatedAt);
309
+ if (sinceProgress < minutes * 6e4) continue;
743
310
  try {
744
- await driver.insert("receipts", receiptId, full);
745
- return full;
746
- } catch (error) {
747
- if (error instanceof RecordExistsError) return (await mustRead("receipts", receiptId, receiptSchema)).value;
748
- throw error;
311
+ await stores.tasks.transition(task.id, "stalled", {
312
+ reason: `no progress for ${Math.floor(sinceProgress / 6e4)} minutes (window ${minutes})`,
313
+ expectFrom: "running"
314
+ });
315
+ result.stalled.push(task.id);
316
+ } catch {
317
+ continue;
749
318
  }
750
319
  }
751
- async function appendReceipt(key, receipt) {
752
- await insertReceipt(derivedId("rcpt", key), receipt);
753
- }
754
- return {
755
- driverName: driver.name,
756
- repositories: {
757
- async put(repository) {
758
- const parsed = repositorySchema.parse(repository);
759
- const existing = await read("repositories", parsed.id, repositorySchema);
760
- if (existing === null) await driver.insert("repositories", parsed.id, parsed);
761
- else await driver.update("repositories", parsed.id, parsed, existing.version);
762
- return parsed;
763
- },
764
- async get(id) {
765
- const record = await read("repositories", id, repositorySchema);
766
- return record === null ? null : record.value;
767
- },
768
- list() {
769
- return listAll("repositories", repositorySchema);
770
- }
771
- },
772
- signals: {
773
- async create(input) {
774
- const id = derivedId("sig", `${input.source}:${input.deliveryId}`);
775
- const signal = signalSchema.parse({
776
- schemaVersion: 1,
777
- id,
778
- repositoryId: input.repositoryId,
779
- source: input.source,
780
- deliveryId: input.deliveryId,
781
- replyTo: input.replyTo,
782
- payload: input.payload,
783
- receivedAt: now(),
784
- outcome: "pending"
785
- });
786
- try {
787
- await driver.insert("signals", id, signal);
788
- return signal;
789
- } catch (error) {
790
- if (error instanceof RecordExistsError) return (await mustRead("signals", id, signalSchema)).value;
791
- throw error;
792
- }
793
- },
794
- async get(id) {
795
- const record = await read("signals", id, signalSchema);
796
- return record === null ? null : record.value;
797
- },
798
- async decide(id, outcome, decideOptions = {}) {
799
- const existing = await mustRead("signals", id, signalSchema);
800
- assertTransition(signalOutcomeTransitions, existing.value.outcome, outcome);
801
- const next = signalSchema.parse({
802
- ...existing.value,
803
- outcome,
804
- detail: decideOptions.detail,
805
- reason: decideOptions.reason,
806
- decidedAt: now()
807
- });
808
- await driver.update("signals", id, next, existing.version);
809
- return next;
810
- },
811
- list() {
812
- return listAll("signals", signalSchema);
813
- }
814
- },
815
- tasks: {
816
- async create(input) {
817
- const timestamp = now();
818
- const id = input.dedupeKey === void 0 ? randomId("task") : derivedId("task", input.dedupeKey);
819
- const task = taskSchema.parse({
820
- schemaVersion: 1,
821
- id,
822
- repositoryIds: input.repositoryIds,
823
- kind: input.kind,
824
- area: input.area,
825
- state: taskInitialState,
826
- approval: input.approval,
827
- origin: input.origin,
828
- dependsOn: input.dependsOn,
829
- replyTo: input.replyTo,
830
- attempt: 1,
831
- question: input.question,
832
- changeId: input.changeId,
833
- createdAt: timestamp,
834
- updatedAt: timestamp
835
- });
836
- try {
837
- await driver.insert("tasks", task.id, task);
838
- return task;
839
- } catch (error) {
840
- if (input.dedupeKey !== void 0 && error instanceof RecordExistsError) return (await mustRead("tasks", task.id, taskSchema)).value;
841
- throw error;
842
- }
843
- },
844
- async approve(id, decision) {
845
- const existing = await mustRead("tasks", id, taskSchema);
846
- const task = existing.value;
847
- if (task.approval !== "required") throw new ApprovalError(id, `approval is ${task.approval ?? "not required"}`);
848
- if (task.state !== "queued") throw new ApprovalError(id, `task is ${task.state}, not queued`);
849
- const next = taskSchema.parse({
850
- ...task,
851
- approval: "granted",
852
- updatedAt: now()
853
- });
854
- if (next.repositoryIds !== void 0) await appendReceipt(`task:${id}:approved:${next.attempt}`, {
855
- repositoryIds: next.repositoryIds,
856
- taskId: id,
857
- kind: next.kind,
858
- state: "approved",
859
- phase: "approval",
860
- reason: decision.reason,
861
- attempt: next.attempt,
862
- decidedBy: decision.by
863
- });
864
- await driver.update("tasks", id, next, existing.version);
865
- return next;
866
- },
867
- async reject(id, decision) {
868
- const existing = await mustRead("tasks", id, taskSchema);
869
- const task = existing.value;
870
- if (task.approval !== "required") throw new ApprovalError(id, `approval is ${task.approval ?? "not required"}`);
871
- if (task.state !== "queued") throw new ApprovalError(id, `task is ${task.state}, not queued`);
872
- const next = taskSchema.parse({
873
- ...task,
874
- approval: "denied",
875
- state: "cancelled",
876
- updatedAt: now()
877
- });
878
- if (next.repositoryIds !== void 0) {
879
- await appendReceipt(`task:${id}:rejected:${next.attempt}`, {
880
- repositoryIds: next.repositoryIds,
881
- taskId: id,
882
- kind: next.kind,
883
- state: "rejected",
884
- phase: "approval",
885
- reason: decision.reason,
886
- attempt: next.attempt,
887
- decidedBy: decision.by
888
- });
889
- await appendReceipt(`task:${id}:cancelled:${next.attempt}`, {
890
- repositoryIds: next.repositoryIds,
891
- taskId: id,
892
- kind: next.kind,
893
- state: "cancelled",
894
- reason: decision.reason ?? `rejected by ${decision.by}`,
895
- attempt: next.attempt
896
- });
897
- }
898
- await driver.update("tasks", id, next, existing.version);
899
- return next;
900
- },
901
- async get(id) {
902
- const record = await read("tasks", id, taskSchema);
903
- return record === null ? null : record.value;
904
- },
905
- async transition(id, to, transitionOptions = {}) {
906
- const existing = await mustRead("tasks", id, taskSchema);
907
- assertTransition(taskTransitions, existing.value.state, to);
908
- const attempt = bumpsAttempt(existing.value.state, to) ? existing.value.attempt + 1 : existing.value.attempt;
909
- const next = taskSchema.parse({
910
- ...existing.value,
911
- ...transitionOptions.patch,
912
- state: to,
913
- attempt,
914
- updatedAt: now()
915
- });
916
- const receiptState = taskReceiptStates[to];
917
- if (receiptState !== void 0 && next.repositoryIds !== void 0) await appendReceipt(`task:${id}:${receiptState}:${attempt}`, {
918
- repositoryIds: next.repositoryIds,
919
- taskId: id,
920
- changeId: next.changeId,
921
- kind: next.kind,
922
- state: receiptState,
923
- reason: transitionOptions.reason,
924
- attempt
925
- });
926
- await driver.update("tasks", id, next, existing.version);
927
- return next;
928
- },
929
- list() {
930
- return listAll("tasks", taskSchema);
931
- }
932
- },
933
- changes: {
934
- async create(input) {
935
- const timestamp = now();
936
- const change = changeSchema.parse({
937
- schemaVersion: 1,
938
- id: randomId("chg"),
939
- repositoryId: input.repositoryId,
940
- taskId: input.taskId,
941
- groupId: input.groupId,
942
- prNumber: input.prNumber,
943
- prUrl: input.prUrl,
944
- author: input.author,
945
- risk: input.risk,
946
- confidence: input.confidence,
947
- evidence: input.evidence,
948
- state: changeInitialState,
949
- createdAt: timestamp,
950
- updatedAt: timestamp
951
- });
952
- await mustRead("repositories", change.repositoryId, repositorySchema);
953
- if (change.taskId !== void 0) {
954
- const task = (await mustRead("tasks", change.taskId, taskSchema)).value;
955
- if (task.kind !== "code_change") throw new ChangeAssociationError(change.taskId, change.repositoryId, "changes can only be produced by code_change tasks");
956
- const taskRepositoryId = task.repositoryIds[0];
957
- if (taskRepositoryId !== change.repositoryId) throw new ChangeAssociationError(change.taskId, change.repositoryId, `repository "${change.repositoryId}" does not match task repository "${taskRepositoryId}"`);
958
- }
959
- await driver.insert("changes", change.id, change);
960
- return change;
961
- },
962
- async get(id) {
963
- const record = await read("changes", id, changeSchema);
964
- return record === null ? null : record.value;
965
- },
966
- async transition(id, to, transitionOptions = {}) {
967
- const existing = await mustRead("changes", id, changeSchema);
968
- assertTransition(changeTransitions, existing.value.state, to);
969
- const next = changeSchema.parse({
970
- ...existing.value,
971
- ...transitionOptions.patch,
972
- state: to,
973
- updatedAt: now()
974
- });
975
- const receiptState = changeReceiptStates[to];
976
- if (receiptState !== void 0) await appendReceipt(`change:${id}:${receiptState}`, {
977
- repositoryIds: [next.repositoryId],
978
- taskId: next.taskId,
979
- changeId: id,
980
- kind: "change",
981
- state: receiptState,
982
- reason: transitionOptions.reason,
983
- attempt: 1,
984
- decidedBy: next.decidedBy
320
+ for (const task of await stores.tasks.list()) {
321
+ if (task.state !== "stalled") continue;
322
+ try {
323
+ if (task.attempt < options.policy.maxAttempts) {
324
+ await stores.tasks.transition(task.id, "queued", {
325
+ reason: `stalled on attempt ${task.attempt} of ${options.policy.maxAttempts}; replacing`,
326
+ expectFrom: "stalled"
985
327
  });
986
- await driver.update("changes", id, next, existing.version);
987
- return next;
988
- },
989
- list() {
990
- return listAll("changes", changeSchema);
991
- }
992
- },
993
- receipts: {
994
- append(input) {
995
- const { dedupeKey, attempt, ...fields } = input;
996
- return insertReceipt(dedupeKey === void 0 ? randomId("rcpt") : derivedId("rcpt", `custom:${dedupeKey}`), {
997
- ...fields,
998
- attempt: attempt ?? 1
328
+ result.requeued.push(task.id);
329
+ } else {
330
+ await stores.tasks.transition(task.id, "failed", {
331
+ reason: `stalled on attempt ${task.attempt} of ${options.policy.maxAttempts}; attempts exhausted`,
332
+ expectFrom: "stalled"
999
333
  });
1000
- },
1001
- async get(id) {
1002
- const record = await read("receipts", id, receiptSchema);
1003
- return record === null ? null : record.value;
1004
- },
1005
- list() {
1006
- return listAll("receipts", receiptSchema);
334
+ result.failed.push(task.id);
1007
335
  }
336
+ } catch {
337
+ continue;
1008
338
  }
1009
- };
339
+ }
340
+ return result;
1010
341
  }
1011
342
  //#endregion
1012
343
  //#region src/store/memory.ts
@@ -1057,6 +388,27 @@ function createInMemoryDriver() {
1057
388
  }
1058
389
  //#endregion
1059
390
  //#region src/sweep.ts
391
+ async function failUnrouteable(stores, task, reason, result) {
392
+ try {
393
+ await stores.tasks.transition(task.id, "failed", {
394
+ reason,
395
+ expectFrom: "queued"
396
+ });
397
+ result.failed.push({
398
+ taskId: task.id,
399
+ reason
400
+ });
401
+ } catch (error) {
402
+ if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) {
403
+ result.skipped.push({
404
+ taskId: task.id,
405
+ reason: "task changed during routing"
406
+ });
407
+ return;
408
+ }
409
+ throw error;
410
+ }
411
+ }
1060
412
  /**
1061
413
  * One pass of the factory's dispatcher, meant to run on a schedule inside the
1062
414
  * deployment: every queued task that is allowed to run is started, in
@@ -1071,7 +423,10 @@ async function sweepQueued(stores, options) {
1071
423
  skipped: [],
1072
424
  failed: []
1073
425
  };
1074
- const queued = (await stores.tasks.list()).filter((task) => task.state === "queued" && task.kind !== "question").sort((a, b) => a.createdAt.localeCompare(b.createdAt));
426
+ const maxDispatches = options.maxDispatches ?? Number.POSITIVE_INFINITY;
427
+ if (!(Number.isInteger(maxDispatches) || maxDispatches === Number.POSITIVE_INFINITY) || maxDispatches < 1) throw new Error("maxDispatches must be a positive integer");
428
+ let attempted = 0;
429
+ const queued = (await stores.tasks.list()).filter((task) => task.state === "queued" && task.kind !== "question").sort((a, b) => b.attempt - a.attempt || a.createdAt.localeCompare(b.createdAt));
1075
430
  for (const task of queued) {
1076
431
  if (task.kind === "coordination") {
1077
432
  result.skipped.push({
@@ -1082,10 +437,7 @@ async function sweepQueued(stores, options) {
1082
437
  }
1083
438
  const executionRepositoryIds = taskExecutionRepositoryIdsSchema.safeParse(task.repositoryIds);
1084
439
  if (!executionRepositoryIds.success) {
1085
- result.skipped.push({
1086
- taskId: task.id,
1087
- reason: "task execution requires exactly one repository"
1088
- });
440
+ await failUnrouteable(stores, task, "task execution requires exactly one repository", result);
1089
441
  continue;
1090
442
  }
1091
443
  if (task.approval === "required") {
@@ -1097,22 +449,27 @@ async function sweepQueued(stores, options) {
1097
449
  }
1098
450
  const launcher = options.launchers[task.kind];
1099
451
  if (launcher === void 0) {
1100
- result.skipped.push({
1101
- taskId: task.id,
1102
- reason: `no agent handles ${task.kind} tasks`
1103
- });
452
+ await failUnrouteable(stores, task, `no registered launcher handles ${task.kind} tasks`, result);
1104
453
  continue;
1105
454
  }
1106
455
  const repository = await stores.repositories.get(executionRepositoryIds.data[0]);
1107
456
  if (repository === null) {
457
+ await failUnrouteable(stores, task, "task has no repository", result);
458
+ continue;
459
+ }
460
+ if (attempted >= maxDispatches) {
1108
461
  result.skipped.push({
1109
462
  taskId: task.id,
1110
- reason: "task has no repository"
463
+ reason: "per-sweep dispatch limit reached"
1111
464
  });
1112
465
  continue;
1113
466
  }
467
+ attempted += 1;
1114
468
  try {
1115
- await dispatchTask(stores, task.id, { launch: (dispatched) => launcher(dispatched, repository) });
469
+ await dispatchTask(stores, task.id, {
470
+ launch: (dispatched) => launcher(dispatched, repository),
471
+ budget: options.budget
472
+ });
1116
473
  result.dispatched.push(task.id);
1117
474
  } catch (error) {
1118
475
  if (error instanceof DispatchRefusedError) {
@@ -1160,6 +517,6 @@ async function ensureVerificationTasks(stores) {
1160
517
  return result;
1161
518
  }
1162
519
  //#endregion
1163
- export { ApprovalError, ChangeAssociationError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, InvalidTransitionError, RecordExistsError, RecordNotFoundError, SCHEMA_VERSION, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, computeLedgerMetrics, computeRepositoryHealth, coordinationRepositoryIdsSchema, costSchema, createCoordination, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, ensureVerificationTasks, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, reconcileCoordination, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, reviewBucket, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskExecutionRepositoryIdsSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, verificationBrief, vocabularySchema };
520
+ export { AnswerEventConflictError, ApprovalError, BudgetExhaustedError, ChangeAssociationError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, InvalidTransitionError, OutboxEventConflictError, RecordExistsError, RecordNotFoundError, SCHEMA_VERSION, SessionConflictError, SettlementError, VersionConflictError, adoptPullRequest, agentRouteSchema, agentSessionIdSchema, agentSessionSchema, answerEventSchema, answerIdSchema, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetLimitsSchema, budgetMonthOf, budgetMonthSchema, budgetSchema, budgetStatus, canReserve, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, communicationEventSchema, communicationOutcomeSchema, communicationOutcomes, computeLedgerMetrics, computeMonthSpendUsd, computeRepositoryHealth, coordinationRepositoryIdsSchema, costSchema, createCoordination, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defaultExecutionPolicy, defineAgentRoutes, defineFactory, deriveFactoryId, dispatchTask, ensureVerificationTasks, executionPolicySchema, executionReferenceSchema, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, outboxIdSchema, outboxMessageSchema, outboxQueueSchema, outboxStateSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, reconcileCoordination, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, reviewBucket, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, stallWindowMinutes, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepBudget, sweepQueued, sweepSettlements, sweepStalled, taskApprovalSchema, taskExecutionRepositoryIdsSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, taskUsageSchema, terminalStates, usdSchema, verificationBrief, vocabularySchema };
1164
521
 
1165
522
  //# sourceMappingURL=index.mjs.map