@vercel/factory 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +362 -0
- package/README.md +49 -261
- package/dist/agent-routes.d.mts +47 -3
- package/dist/agent-routes.mjs +28 -1
- package/dist/agent-routes.mjs.map +1 -1
- package/dist/api-contracts.d.mts +20 -1
- package/dist/api-contracts.mjs +2 -1
- package/dist/api-contracts.mjs.map +1 -1
- package/dist/api.d.mts +45 -2
- package/dist/api.mjs +199 -10
- package/dist/api.mjs.map +1 -1
- package/dist/approval-contracts.d.mts +6 -0
- package/dist/blob/index.d.mts +51 -14
- package/dist/blob/index.mjs +26 -10
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +7 -0
- package/dist/budget.mjs +6 -0
- package/dist/budget.mjs.map +1 -1
- package/dist/build-factory.d.mts +1 -0
- package/dist/change-verification/dispatch.d.mts +16 -3
- package/dist/change-verification/dispatch.mjs +45 -7
- package/dist/change-verification/dispatch.mjs.map +1 -1
- package/dist/change-verification/eve-tool.d.mts +2 -1
- package/dist/change-verification/eve-tool.mjs +35 -47
- package/dist/change-verification/eve-tool.mjs.map +1 -1
- package/dist/change-verification/result.mjs +130 -0
- package/dist/change-verification/result.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +2 -2
- package/dist/changes/eve-record-change.mjs +43 -9
- package/dist/changes/eve-record-change.mjs.map +1 -1
- package/dist/changes.d.mts +4 -3
- package/dist/changes.mjs +2 -2
- package/dist/changes.mjs.map +1 -1
- package/dist/client-events.d.mts +10 -3
- package/dist/client-events.mjs +6 -2
- package/dist/client-events.mjs.map +1 -1
- package/dist/client-stream.mjs +8 -2
- package/dist/client-stream.mjs.map +1 -1
- package/dist/client-transcript.mjs +5 -1
- package/dist/client-transcript.mjs.map +1 -1
- package/dist/client.d.mts +121 -12
- package/dist/client.mjs +117 -9
- package/dist/client.mjs.map +1 -1
- package/dist/code-review/contracts.d.mts +1 -0
- package/dist/code-review/eve-post-review.d.mts +4 -4
- package/dist/code-review/eve-post-review.mjs +29 -17
- package/dist/code-review/eve-post-review.mjs.map +1 -1
- package/dist/code-review/eve-review-comments.d.mts +13 -2
- package/dist/code-review/eve-review-comments.mjs +45 -11
- package/dist/code-review/eve-review-comments.mjs.map +1 -1
- package/dist/code-review/github-reporter.d.mts +2 -0
- package/dist/code-review/github-reporter.mjs +7 -5
- package/dist/code-review/github-reporter.mjs.map +1 -1
- package/dist/code-review.d.mts +3 -2
- package/dist/deepsec/eve-tool.mjs +3 -1
- package/dist/deepsec/eve-tool.mjs.map +1 -1
- package/dist/dispatch.d.mts +79 -8
- package/dist/dispatch.mjs +68 -9
- package/dist/dispatch.mjs.map +1 -1
- package/dist/eve/index.d.mts +70 -10
- package/dist/eve/index.mjs +93 -22
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.mjs +24 -11
- package/dist/eve/invoke.mjs.map +1 -1
- package/dist/eve/session-client.d.mts +122 -4
- package/dist/eve/session-client.mjs +127 -13
- package/dist/eve/session-client.mjs.map +1 -1
- package/dist/eve/task-execution.d.mts +380 -0
- package/dist/eve/task-execution.mjs +57 -2
- package/dist/eve/task-execution.mjs.map +1 -1
- package/dist/eve/task-session.d.mts +44 -2
- package/dist/eve/task-session.mjs +44 -2
- package/dist/eve/task-session.mjs.map +1 -1
- package/dist/eve/transcript.mjs +5 -1
- package/dist/eve/transcript.mjs.map +1 -1
- package/dist/execution.d.mts +117 -9
- package/dist/execution.mjs +76 -6
- package/dist/execution.mjs.map +1 -1
- package/dist/finding-remediation/admission.d.mts +2 -0
- package/dist/finding-remediation/admission.mjs +4 -1
- package/dist/finding-remediation/admission.mjs.map +1 -1
- package/dist/findings.d.mts +1 -0
- package/dist/github-publication.d.mts +1 -0
- package/dist/github-publication.mjs +97 -84
- package/dist/github-publication.mjs.map +1 -1
- package/dist/github-transfer.d.mts +15 -6
- package/dist/github-transfer.mjs +214 -66
- package/dist/github-transfer.mjs.map +1 -1
- package/dist/github.d.mts +51 -11
- package/dist/github.mjs +126 -24
- package/dist/github.mjs.map +1 -1
- package/dist/inbox-activity.d.mts +53 -0
- package/dist/inbox-activity.mjs +41 -0
- package/dist/inbox-activity.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -2
- package/dist/intake-contracts.d.mts +0 -1
- package/dist/integrations/deepsec.d.mts +1 -0
- package/dist/integrations/github.d.mts +2 -2
- package/dist/integrations/github.mjs +2 -2
- package/dist/integrations/slack.d.mts +3 -1
- package/dist/integrations/slack.mjs +3 -1
- package/dist/integrations/vercel.d.mts +4 -2
- package/dist/integrations/vercel.mjs +3 -2
- package/dist/merge-resolution/eve-tools.d.mts +1 -0
- package/dist/merge-resolution/eve-tools.mjs +7 -2
- package/dist/merge-resolution/eve-tools.mjs.map +1 -1
- package/dist/model-settings.d.mts +41 -0
- package/dist/model-settings.mjs +35 -0
- package/dist/model-settings.mjs.map +1 -0
- package/dist/planning/reconcile.mjs +6 -0
- package/dist/planning/reconcile.mjs.map +1 -1
- package/dist/postgres/index.d.mts +43 -2
- package/dist/postgres/index.mjs +40 -2
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/presets/software-development/dispatch.d.mts +4 -1
- package/dist/presets/software-development/dispatch.mjs +2 -1
- package/dist/presets/software-development/dispatch.mjs.map +1 -1
- package/dist/presets/software-development/task-communication.d.mts +1 -0
- package/dist/presets/software-development/task-communication.mjs +48 -11
- package/dist/presets/software-development/task-communication.mjs.map +1 -1
- package/dist/presets/software-development.d.mts +1 -0
- package/dist/pull-requests/github-publisher.d.mts +15 -1
- package/dist/pull-requests/github-publisher.mjs +61 -1
- package/dist/pull-requests/github-publisher.mjs.map +1 -1
- package/dist/pull-requests.d.mts +1 -0
- package/dist/sandbox/index.d.mts +1 -0
- package/dist/schema/agent-route.d.mts +19 -1
- package/dist/schema/agent-route.mjs +19 -1
- package/dist/schema/agent-route.mjs.map +1 -1
- package/dist/schema/factory-config.d.mts +27 -0
- package/dist/schema/factory-config.mjs +33 -3
- package/dist/schema/factory-config.mjs.map +1 -1
- package/dist/schema/repository.d.mts +4 -0
- package/dist/schema/repository.mjs +5 -1
- package/dist/schema/repository.mjs.map +1 -1
- package/dist/schema/session.d.mts +1 -0
- package/dist/schema/session.mjs +1 -0
- package/dist/schema/session.mjs.map +1 -1
- package/dist/schema/slack-pr-notifications.d.mts +12 -0
- package/dist/schema/slack-pr-notifications.mjs +11 -0
- package/dist/schema/slack-pr-notifications.mjs.map +1 -0
- package/dist/schema/task-graph.d.mts +39 -0
- package/dist/schema/task.d.mts +1 -0
- package/dist/schema/task.mjs +2 -1
- package/dist/schema/task.mjs.map +1 -1
- package/dist/schema/transcript.d.mts +6 -0
- package/dist/schema/transcript.mjs +2 -1
- package/dist/schema/transcript.mjs.map +1 -1
- package/dist/schema/work.d.mts +52 -3
- package/dist/schema/work.mjs.map +1 -1
- package/dist/session-previews.d.mts +76 -0
- package/dist/session-previews.mjs +55 -0
- package/dist/session-previews.mjs.map +1 -0
- package/dist/session-review.d.mts +120 -0
- package/dist/session-review.mjs +79 -0
- package/dist/session-review.mjs.map +1 -0
- package/dist/signal-triage.mjs +1 -1
- package/dist/signals.d.mts +1 -0
- package/dist/stall.d.mts +4 -1
- package/dist/stall.mjs +6 -2
- package/dist/stall.mjs.map +1 -1
- package/dist/store/driver.d.mts +1 -1
- package/dist/store/driver.mjs.map +1 -1
- package/dist/store/engine.d.mts +206 -8
- package/dist/store/engine.mjs +147 -13
- package/dist/store/engine.mjs.map +1 -1
- package/dist/store/memory.d.mts +18 -1
- package/dist/store/memory.mjs +18 -1
- package/dist/store/memory.mjs.map +1 -1
- package/dist/store/slack-pr-notifications.d.mts +44 -0
- package/dist/store/slack-pr-notifications.mjs +121 -0
- package/dist/store/slack-pr-notifications.mjs.map +1 -0
- package/dist/store/task-work.d.mts +121 -6
- package/dist/store/task-work.mjs +7 -4
- package/dist/store/task-work.mjs.map +1 -1
- package/dist/sweep.d.mts +28 -6
- package/dist/sweep.mjs +34 -6
- package/dist/sweep.mjs.map +1 -1
- package/dist/task-graph-view.d.mts +3 -0
- package/dist/tasks.d.mts +3 -3
- package/dist/tasks.mjs +3 -3
- package/dist/vercel-git.d.mts +35 -3
- package/dist/vercel-git.mjs +265 -33
- package/dist/vercel-git.mjs.map +1 -1
- package/dist/vercel-github-api.d.mts +103 -0
- package/dist/vercel-github-api.mjs +363 -0
- package/dist/vercel-github-api.mjs.map +1 -0
- package/dist/vercel.d.mts +3 -2
- package/dist/vercel.mjs +3 -2
- package/dist/vercel.mjs.map +1 -1
- package/dist/work-triage.d.mts +1 -0
- package/dist/workflows.d.mts +102 -4
- package/dist/workflows.mjs +55 -2
- package/dist/workflows.mjs.map +1 -1
- package/dist/workspace-files-git.d.mts +15 -0
- package/dist/workspace-files-git.mjs +61 -0
- package/dist/workspace-files-git.mjs.map +1 -0
- package/dist/workspace-files.d.mts +107 -0
- package/dist/workspace-files.mjs +74 -0
- package/dist/workspace-files.mjs.map +1 -0
- package/docs/getting-started.md +104 -0
- package/docs/index.md +100 -0
- package/docs/recipes/cancellation.md +215 -0
- package/docs/recipes/custom-workflow.md +153 -0
- package/docs/recipes/dependent-tasks.md +207 -0
- package/docs/recipes/eve-agent.md +277 -0
- package/docs/recipes/human-input.md +204 -0
- package/docs/recipes/persistence-recovery.md +268 -0
- package/docs/recipes/retry-recovery.md +241 -0
- package/docs/recipes/task-messaging.md +215 -0
- package/docs/recipes/typed-eve-result.md +161 -0
- package/docs/runtime-integration.md +137 -0
- package/package.json +20 -6
package/dist/store/engine.d.mts
CHANGED
|
@@ -81,12 +81,17 @@ interface DecideSignalInput {
|
|
|
81
81
|
readonly reason?: string;
|
|
82
82
|
}
|
|
83
83
|
interface CreateTaskInputBase {
|
|
84
|
+
/** Repository authorization scope; children must remain within their parent's scope. */
|
|
84
85
|
repositoryIds: RepositoryIds;
|
|
86
|
+
/** Descriptive work vocabulary; routing comes only from `work.route`. */
|
|
85
87
|
kind: Task["kind"];
|
|
88
|
+
/** Destination for lifecycle communication produced by this Task. */
|
|
86
89
|
replyTo: ReplyTo;
|
|
87
|
-
/** Same key creates one
|
|
90
|
+
/** Same key under the same parent creates one Task and returns its current snapshot on retries. */
|
|
88
91
|
dedupeKey?: string;
|
|
92
|
+
/** Optional application-owned grouping label. */
|
|
89
93
|
area?: string;
|
|
94
|
+
/** Optional associated software Change. */
|
|
90
95
|
changeId?: ChangeId;
|
|
91
96
|
/** Stamp the task as needing a human decision before dispatch. */
|
|
92
97
|
approval?: "required";
|
|
@@ -97,10 +102,14 @@ interface CreateTaskInputBase {
|
|
|
97
102
|
}
|
|
98
103
|
/** Provenance required when admitting a root Task with no parent to inherit from. */
|
|
99
104
|
type RootTaskOrigin = {
|
|
105
|
+
/** Durable inbound Signal that caused the root work. */
|
|
100
106
|
signalId: SignalId;
|
|
107
|
+
/** Authenticated operator additionally associated with the work. */
|
|
101
108
|
operator?: string;
|
|
102
109
|
} | {
|
|
110
|
+
/** Optional inbound Signal additionally associated with operator-created work. */
|
|
103
111
|
signalId?: SignalId;
|
|
112
|
+
/** Authenticated operator that caused the root work. */
|
|
104
113
|
operator: string;
|
|
105
114
|
};
|
|
106
115
|
/** Explicit work, graph placement, provenance, and policy input for Task admission. */
|
|
@@ -174,17 +183,30 @@ declare const stepUsageSchema: z.ZodObject<{
|
|
|
174
183
|
type StepUsage = z.infer<typeof stepUsageSchema>;
|
|
175
184
|
/** Attempt and provider execution that must still own a Task usage observation. */
|
|
176
185
|
interface TaskExecutionFence {
|
|
186
|
+
/** Current positive attempt that emitted the observation. */
|
|
177
187
|
readonly attempt: number;
|
|
188
|
+
/** Exact provider execution that emitted the observation. */
|
|
178
189
|
readonly execution: ExecutionReference;
|
|
179
190
|
}
|
|
191
|
+
/** Error raised when a Task operation loses its exact-attempt concurrency fence. */
|
|
192
|
+
declare class TaskAttemptConflictError extends Error {
|
|
193
|
+
readonly taskId: TaskId;
|
|
194
|
+
readonly actualAttempt: number;
|
|
195
|
+
readonly expectedAttempt: number;
|
|
196
|
+
constructor(taskId: TaskId, actualAttempt: number, expectedAttempt: number);
|
|
197
|
+
}
|
|
180
198
|
/** Provider execution and optional attempt fence recorded on one running Task. */
|
|
181
199
|
interface RecordTaskExecutionInput {
|
|
200
|
+
/** Provider handle to persist for later inspection, cancellation, and callback fencing. */
|
|
182
201
|
readonly execution: ExecutionReference;
|
|
202
|
+
/** Current running attempt required at the write; identical replay is then a no-op while running. */
|
|
183
203
|
readonly expectAttempt?: number;
|
|
184
204
|
}
|
|
185
205
|
/** One measured usage step and optional exact-execution fence for a Task. */
|
|
186
206
|
interface RecordTaskUsageInput {
|
|
207
|
+
/** Monotonic step observation to aggregate once per provider turn. */
|
|
187
208
|
readonly step: StepUsage;
|
|
209
|
+
/** Optional current-attempt and execution guard for provider-originated observations. */
|
|
188
210
|
readonly fence?: TaskExecutionFence;
|
|
189
211
|
}
|
|
190
212
|
interface TaskTransitionContext {
|
|
@@ -246,6 +268,12 @@ interface CreateChangeInput {
|
|
|
246
268
|
}
|
|
247
269
|
/** Verification and merge fields that may accompany a legal Change transition. */
|
|
248
270
|
type ChangePatch = Partial<Pick<Change, "confidence" | "evidence" | "decidedBy" | "mergeSha">>;
|
|
271
|
+
/** Trust evidence update fenced to the complete observed Change snapshot. */
|
|
272
|
+
interface UpdateChangeEvidenceInput {
|
|
273
|
+
evidence: Change["evidence"];
|
|
274
|
+
confidence: ChangeLevel;
|
|
275
|
+
expected: Change;
|
|
276
|
+
}
|
|
249
277
|
interface ChangeTransitionContext {
|
|
250
278
|
patch?: ChangePatch;
|
|
251
279
|
reason?: string;
|
|
@@ -272,7 +300,9 @@ interface BudgetNoticeStampResult {
|
|
|
272
300
|
}
|
|
273
301
|
/** Measured cost and optional audit reason used to settle one terminal Task. */
|
|
274
302
|
interface SettleTaskInput {
|
|
303
|
+
/** Final measured Task cost, normalized to micro-dollar precision. */
|
|
275
304
|
readonly cost: Cost;
|
|
305
|
+
/** Optional audit context retained on the immutable settlement receipt. */
|
|
276
306
|
readonly reason?: string;
|
|
277
307
|
}
|
|
278
308
|
/** Delivery attempt, failure reason, and retry delay for one outbox message. */
|
|
@@ -319,6 +349,7 @@ type AppendReceiptInput<State extends ReceiptState = StandardReceiptState, Kind
|
|
|
319
349
|
declare class SessionConflictError extends Error {}
|
|
320
350
|
/** Validating engine API for all canonical Factory state and auxiliary persistence. */
|
|
321
351
|
interface FactoryStores {
|
|
352
|
+
/** Name supplied by the configured persistence driver for diagnostics and health reporting. */
|
|
322
353
|
driverName: string;
|
|
323
354
|
/** Trusted workflow records bound to the same persistence backend as every validating store. */
|
|
324
355
|
workflowRecords: Pick<StoreDriver, "get" | "insert" | "update">;
|
|
@@ -326,19 +357,26 @@ interface FactoryStores {
|
|
|
326
357
|
taskEffects: TaskEffectStore;
|
|
327
358
|
/** Trusted workflow capabilities; application transports must authenticate and authorize callers. */
|
|
328
359
|
graphs: TaskGraphStore;
|
|
360
|
+
/** Validated, authorized Task-message operations; omitted protocols remain unavailable. */
|
|
329
361
|
messages: TaskMessageStore;
|
|
362
|
+
/** Workflow completion, message waiting, graph recovery, and usage aggregation operations. */
|
|
330
363
|
work: TaskWorkStore;
|
|
364
|
+
/** Materialized transcript snapshots keyed by exact provider execution. */
|
|
331
365
|
transcripts: {
|
|
332
366
|
get(execution: ExecutionReference): Promise<TranscriptSnapshot | null>;
|
|
333
367
|
/** Persist only forward progress; safe to repeat from response-boundary hooks. */
|
|
334
368
|
put(execution: ExecutionReference, snapshot: TranscriptSnapshot): Promise<TranscriptSnapshot>;
|
|
335
369
|
};
|
|
370
|
+
/** Owner-scoped image persistence and attachment validation. */
|
|
336
371
|
images: {
|
|
337
372
|
put(owner: string, input: z.infer<typeof imageUploadSchema>): Promise<ImageAttachment>;
|
|
338
373
|
get(owner: string, id: ImageId): Promise<StoredImage | null>;
|
|
339
374
|
validate(owner: string, attachments: readonly ImageAttachment[]): Promise<readonly ImageAttachment[]>;
|
|
340
375
|
};
|
|
376
|
+
/** Durable operator-session admission, binding, continuation, and notification operations. */
|
|
341
377
|
sessions: {
|
|
378
|
+
/** Looks up an admitted session without recomputing defaults on a repeated operation. */
|
|
379
|
+
getByOperation(owner: string, operationId: string): Promise<AgentSession | null>;
|
|
342
380
|
create(input: Omit<AgentSession, "schemaVersion" | "id" | "createdAt" | "execution" | "taskId" | "notifications" | "attached" | "continuations" | "external"> & {
|
|
343
381
|
operationId: string;
|
|
344
382
|
}): Promise<AgentSession>;
|
|
@@ -371,14 +409,23 @@ interface FactoryStores {
|
|
|
371
409
|
bind(id: AgentSessionId, reference: AgentSessionBinding): Promise<AgentSession>;
|
|
372
410
|
notify(event: CommunicationEvent): Promise<void>;
|
|
373
411
|
};
|
|
412
|
+
/** Configured repository snapshots used for admission and routing. */
|
|
374
413
|
repositories: {
|
|
414
|
+
/** Atomically replaces model overrides when their observed revision still matches. */
|
|
415
|
+
setModelDefaults(id: RepositoryId, input: {
|
|
416
|
+
expectedRevision: number;
|
|
417
|
+
agents: Readonly<Record<string, string>>;
|
|
418
|
+
}): Promise<Repository>;
|
|
375
419
|
put(repository: Repository): Promise<Repository>;
|
|
376
420
|
get(id: RepositoryId): Promise<Repository | null>;
|
|
377
421
|
list(): Promise<readonly Repository[]>;
|
|
378
422
|
};
|
|
423
|
+
/** External intake records and their explicit triage lifecycle. */
|
|
379
424
|
signals: {
|
|
380
425
|
create(input: CreateSignalInput): Promise<Signal>;
|
|
381
426
|
get(id: SignalId): Promise<Signal | null>;
|
|
427
|
+
/** Read a deterministic source/delivery record without scanning the Signal collection. */
|
|
428
|
+
getByDelivery(input: Pick<CreateSignalInput, "source" | "deliveryId">): Promise<Signal | null>;
|
|
382
429
|
beginTriage(id: SignalId, options: {
|
|
383
430
|
expectedAttempt: number;
|
|
384
431
|
by: string;
|
|
@@ -387,10 +434,61 @@ interface FactoryStores {
|
|
|
387
434
|
decide(id: SignalId, input: DecideSignalInput): Promise<Signal>;
|
|
388
435
|
list(): Promise<readonly Signal[]>;
|
|
389
436
|
};
|
|
437
|
+
/** Canonical Task lifecycle operations. */
|
|
390
438
|
tasks: {
|
|
439
|
+
/** Pins an application's model choice once, fencing the current attempt. */
|
|
440
|
+
pinModel(id: TaskId, input: {
|
|
441
|
+
attempt: number;
|
|
442
|
+
model: string;
|
|
443
|
+
}): Promise<Task>;
|
|
444
|
+
/**
|
|
445
|
+
* Validates and durably admits a queued Task into its canonical graph.
|
|
446
|
+
*
|
|
447
|
+
* @remarks
|
|
448
|
+
* Admission writes the immutable graph node before the mutable Task snapshot. A failure after
|
|
449
|
+
* graph admission can therefore leave a repairable missing snapshot; call
|
|
450
|
+
* `stores.work.recover(rootTaskId)` with the known root to restore it. The method returns only
|
|
451
|
+
* after both admission and the canonical Task snapshot are durable.
|
|
452
|
+
*
|
|
453
|
+
* A `dedupeKey` derives a stable ID within the parent scope. An identical retry returns the
|
|
454
|
+
* Task's current mutable snapshot, while changed immutable admission data is refused. Without
|
|
455
|
+
* a key, every call creates a new Task.
|
|
456
|
+
*
|
|
457
|
+
* @param input - Explicit workflow work, scope, provenance, graph placement, and policy.
|
|
458
|
+
* @returns The newly admitted Task, or the current snapshot selected by `dedupeKey`.
|
|
459
|
+
* @throws When workflow input is invalid or its exact version is unregistered, parent or
|
|
460
|
+
* dependency constraints fail, or a dedupe key conflicts with an earlier admission.
|
|
461
|
+
*/
|
|
391
462
|
create(input: CreateTaskInput): Promise<Task>;
|
|
463
|
+
/**
|
|
464
|
+
* Returns the validated current Task snapshot without repairing a missing admitted snapshot.
|
|
465
|
+
*
|
|
466
|
+
* @param id - Exact Task ID to read.
|
|
467
|
+
* @returns Current Task state, or `null` when its canonical snapshot is absent.
|
|
468
|
+
*/
|
|
392
469
|
get(id: TaskId): Promise<Task | null>;
|
|
393
470
|
findOpenQuestions(replyTo: ReplyTo): Promise<readonly Task[]>;
|
|
471
|
+
/**
|
|
472
|
+
* Performs one legal lifecycle transition with optional attempt, source-state, execution, and
|
|
473
|
+
* question fences.
|
|
474
|
+
*
|
|
475
|
+
* @remarks
|
|
476
|
+
* Lifecycle evidence is appended before the final Task-state write. A successful completion
|
|
477
|
+
* first persists its validated `workResult`, then its receipt, then the terminal state. The
|
|
478
|
+
* promise resolves only after the final Task snapshot is durable, but a rejected call may have
|
|
479
|
+
* persisted prepared output or its idempotent receipt before a later write failed. Reread
|
|
480
|
+
* canonical Task state before retrying, and retain the same fences so a late callback cannot
|
|
481
|
+
* advance a replacement attempt or execution. Transitions that re-enter `queued`, and
|
|
482
|
+
* `verifying` to `running`, increment the attempt and clear attempt-owned execution state.
|
|
483
|
+
*
|
|
484
|
+
* @param id - Exact Task to advance.
|
|
485
|
+
* @param to - Legal destination state.
|
|
486
|
+
* @param args - Destination data and optional concurrency fences.
|
|
487
|
+
* @returns The durably updated Task snapshot.
|
|
488
|
+
* @throws `InvalidTransitionError` for an illegal or stale source state,
|
|
489
|
+
* `TaskAttemptConflictError` for a replaced attempt, or a persistence conflict when a
|
|
490
|
+
* concurrent writer wins.
|
|
491
|
+
*/
|
|
394
492
|
transition<To extends TaskState>(id: TaskId, to: To, ...args: TaskTransitionArguments<NoInfer<To>>): Promise<Task>;
|
|
395
493
|
/** Remove a receipt after its CommunicationEvent is durable in the outbox. */
|
|
396
494
|
acknowledgeCommunication(id: TaskId, receiptId: ReceiptId): Promise<Task>;
|
|
@@ -403,14 +501,51 @@ interface FactoryStores {
|
|
|
403
501
|
reject(id: TaskId, decision: RejectionDecision): Promise<Task>;
|
|
404
502
|
/**
|
|
405
503
|
* Record the provider's handle on the running attempt's execution, so the
|
|
406
|
-
* reconciler can inspect or cancel it.
|
|
504
|
+
* reconciler can inspect or cancel it.
|
|
505
|
+
*
|
|
506
|
+
* @remarks
|
|
507
|
+
* The returned Task contains the durably recorded handle. Supply `expectAttempt` for launch
|
|
508
|
+
* callbacks: while that attempt remains running, replaying the same handle is a no-op, while a
|
|
509
|
+
* different handle or replaced attempt is refused. Terminal Tasks reject every binding call.
|
|
510
|
+
* The engine retries bounded transient driver failures, but surfaces compare-and-swap
|
|
511
|
+
* conflicts; callers should reread and reconcile rather than start an unkeyed replacement
|
|
512
|
+
* execution.
|
|
513
|
+
*
|
|
514
|
+
* @param id - Exact running Task to bind.
|
|
515
|
+
* @param input - Provider reference and recommended attempt fence.
|
|
516
|
+
* @returns The Task containing the durable execution reference.
|
|
517
|
+
* @throws When the Task is not running, the attempt or existing execution differs, or bounded
|
|
518
|
+
* persistence retries are exhausted.
|
|
407
519
|
*/
|
|
408
520
|
recordExecution(id: TaskId, input: RecordTaskExecutionInput): Promise<Task>;
|
|
409
|
-
/**
|
|
521
|
+
/**
|
|
522
|
+
* Adds one provider step's measured usage without settling the Task.
|
|
523
|
+
*
|
|
524
|
+
* @remarks
|
|
525
|
+
* Step indexes advance monotonically per `turnId`; replaying the recorded index or an earlier
|
|
526
|
+
* one returns the current Task without charging it again. Use an execution fence for provider
|
|
527
|
+
* callbacks. Version races are retried, transient storage failures are retried within bounded
|
|
528
|
+
* limits, and new usage after settlement is refused so measured cost cannot silently diverge
|
|
529
|
+
* from the immutable settlement receipt.
|
|
530
|
+
*
|
|
531
|
+
* @param id - Exact Task to charge.
|
|
532
|
+
* @param input - Validated step observation and optional execution fence.
|
|
533
|
+
* @returns The Task after aggregation, or unchanged when the step was already covered.
|
|
534
|
+
*/
|
|
410
535
|
recordUsage(id: TaskId, input: RecordTaskUsageInput): Promise<Task>;
|
|
411
536
|
/**
|
|
412
|
-
*
|
|
413
|
-
*
|
|
537
|
+
* Records measured cost on a terminal Task using receipt-first persistence.
|
|
538
|
+
*
|
|
539
|
+
* @remarks
|
|
540
|
+
* Cost is normalized to micro-dollar precision. The immutable receipt is written before the
|
|
541
|
+
* Task snapshot, so retrying after an interrupted snapshot write adopts the receipt's original
|
|
542
|
+
* cost. Once settled, every replay returns the unchanged Task even if a different cost is
|
|
543
|
+
* supplied; callers must treat the first settlement as authoritative.
|
|
544
|
+
*
|
|
545
|
+
* @param id - Exact terminal Task to settle.
|
|
546
|
+
* @param input - Final measured cost and optional audit reason.
|
|
547
|
+
* @returns The Task with its authoritative `settledUsd` value.
|
|
548
|
+
* @throws `SettlementError` when the Task is not terminal.
|
|
414
549
|
*/
|
|
415
550
|
settle(id: TaskId, input: SettleTaskInput): Promise<Task>;
|
|
416
551
|
/**
|
|
@@ -426,12 +561,16 @@ interface FactoryStores {
|
|
|
426
561
|
forReplyTo(replyTo: ReplyTo): Promise<readonly Task[]>;
|
|
427
562
|
list(): Promise<readonly Task[]>;
|
|
428
563
|
};
|
|
564
|
+
/** Canonical software Change admission and lifecycle operations. */
|
|
429
565
|
changes: {
|
|
430
566
|
create(input: CreateChangeInput): Promise<Change>;
|
|
431
567
|
get(id: ChangeId): Promise<Change | null>;
|
|
568
|
+
/** Save evidence without inventing a lifecycle transition; reject a changed snapshot. */
|
|
569
|
+
updateEvidence(id: ChangeId, input: UpdateChangeEvidenceInput): Promise<Change>;
|
|
432
570
|
transition<To extends ChangeState>(id: ChangeId, to: To, ...args: ChangeTransitionArguments<NoInfer<To>>): Promise<ChangeInState<To>>;
|
|
433
571
|
list(): Promise<readonly Change[]>;
|
|
434
572
|
};
|
|
573
|
+
/** Immutable audit evidence appended by engine and application policy. */
|
|
435
574
|
receipts: {
|
|
436
575
|
append<const State extends ReceiptState, const Kind extends ReceiptKind, const Phase extends ReceiptPhase | undefined = undefined>(input: AppendReceiptCallInput<State, Kind, Phase> & {
|
|
437
576
|
state: State;
|
|
@@ -441,6 +580,7 @@ interface FactoryStores {
|
|
|
441
580
|
get(id: ReceiptId): Promise<Receipt | null>;
|
|
442
581
|
list(): Promise<readonly Receipt[]>;
|
|
443
582
|
};
|
|
583
|
+
/** Monthly budget snapshots, limits, and once-only notification stamps. */
|
|
444
584
|
budgets: {
|
|
445
585
|
/**
|
|
446
586
|
* Insert-once per month; refreshes limits when configuration changed and
|
|
@@ -454,9 +594,11 @@ interface FactoryStores {
|
|
|
454
594
|
*/
|
|
455
595
|
stampNotice(month: BudgetMonth, notice: "warning" | "exhausted"): Promise<BudgetNoticeStampResult>;
|
|
456
596
|
};
|
|
597
|
+
/** Immutable authenticated answers addressed by deterministic event ID. */
|
|
457
598
|
answers: {
|
|
458
599
|
get(id: AnswerId): Promise<AnswerEvent | null>;
|
|
459
600
|
};
|
|
601
|
+
/** Durable communication delivery queue with attempt-fenced leases. */
|
|
460
602
|
outbox: {
|
|
461
603
|
enqueue(event: CommunicationEvent): Promise<OutboxMessage>;
|
|
462
604
|
get(id: OutboxId): Promise<OutboxMessage | null>;
|
|
@@ -468,18 +610,74 @@ interface FactoryStores {
|
|
|
468
610
|
}
|
|
469
611
|
/** Persistence, workflow, messaging, clock, and wakeup configuration for the Factory engine. */
|
|
470
612
|
interface CreateStoresOptions {
|
|
613
|
+
/** Persistence implementation. The engine owns validation and lifecycle policy above it. */
|
|
471
614
|
driver: StoreDriver;
|
|
472
|
-
/**
|
|
615
|
+
/**
|
|
616
|
+
* Optional post-commit wakeup transport. Delivery failure is reported through its `onError`
|
|
617
|
+
* callback and never rolls back the canonical write.
|
|
618
|
+
*/
|
|
473
619
|
workNotifications?: WorkNotifications;
|
|
620
|
+
/** ISO timestamp source used for engine-owned records. Defaults to the current wall clock. */
|
|
474
621
|
now?: IsoDateTimeClock;
|
|
622
|
+
/**
|
|
623
|
+
* Every workflow version that stored Tasks may reference. Omission registers only the generic
|
|
624
|
+
* `task@1` workflow. A supplied registry replaces that default, so include `defaultTaskWorkflow`
|
|
625
|
+
* when Factory tools or human-question flows may create generic work.
|
|
626
|
+
*/
|
|
475
627
|
workflows?: WorkflowRegistry;
|
|
628
|
+
/**
|
|
629
|
+
* Versioned Task-message protocols and deny-by-default authorization. Omit when the runtime
|
|
630
|
+
* does not use Task messages.
|
|
631
|
+
*/
|
|
476
632
|
messages?: {
|
|
633
|
+
/** Exact protocol versions accepted by this runtime. */
|
|
477
634
|
protocols: TaskMessageProtocols;
|
|
635
|
+
/** Trusted application policy applied to every message operation. */
|
|
478
636
|
authorize: TaskMessageAuthorize;
|
|
479
637
|
};
|
|
480
638
|
}
|
|
481
|
-
/**
|
|
639
|
+
/**
|
|
640
|
+
* Creates the validating Factory store engine over a deliberately policy-free driver.
|
|
641
|
+
*
|
|
642
|
+
* @remarks
|
|
643
|
+
* The returned stores validate entities on read and write, enforce legal transitions and
|
|
644
|
+
* compare-and-swap fences, and emit immutable receipts. Construct one configured instance per
|
|
645
|
+
* runtime and share it with launchers, hooks, and tools. A custom workflow must be registered on
|
|
646
|
+
* every process that may read its Tasks; omission is a configuration error rather than permission
|
|
647
|
+
* to select a newer workflow version. Supplying `workflows` replaces the default registry, so
|
|
648
|
+
* include `defaultTaskWorkflow` when generic `task@1` work, Factory tools, or human questions may
|
|
649
|
+
* be created.
|
|
650
|
+
*
|
|
651
|
+
* Wakeups run after their source write commits. Notification failure therefore cannot roll the
|
|
652
|
+
* mutation back; reconciliation must be able to rediscover canonical state.
|
|
653
|
+
*
|
|
654
|
+
* @param options - Persistence and exact workflow/message configuration for this runtime.
|
|
655
|
+
* @returns The complete set of validating Factory stores backed by `options.driver`.
|
|
656
|
+
* @see The version-matched `docs/getting-started.md` shipped with the package.
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```ts
|
|
660
|
+
* import { createInMemoryDriver, createStores } from "@vercel/factory/storage";
|
|
661
|
+
* import {
|
|
662
|
+
* defaultTaskWorkflow,
|
|
663
|
+
* defineWorkflow,
|
|
664
|
+
* defineWorkflows,
|
|
665
|
+
* } from "@vercel/factory/workflows";
|
|
666
|
+
* import { z } from "zod";
|
|
667
|
+
*
|
|
668
|
+
* const greeting = defineWorkflow({
|
|
669
|
+
* id: "greeting",
|
|
670
|
+
* version: 1,
|
|
671
|
+
* input: z.object({ name: z.string() }),
|
|
672
|
+
* output: z.object({ message: z.string() }),
|
|
673
|
+
* });
|
|
674
|
+
* const stores = createStores({
|
|
675
|
+
* driver: createInMemoryDriver(),
|
|
676
|
+
* workflows: defineWorkflows([defaultTaskWorkflow, greeting]),
|
|
677
|
+
* });
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
482
680
|
declare function createStores(options: CreateStoresOptions): FactoryStores;
|
|
483
681
|
//#endregion
|
|
484
|
-
export { AgentSessionBinding, AnswerEventConflictError, AnswerQuestionInput, AnswerQuestionResult, AppendReceiptInput, ApprovalDecision, ApprovalError, BudgetNoticeStampResult, CacheContinuationContextInput, ChangeAssociationError, ChangePatch, ChangeTransitionArguments, ChangeTransitionOptions, CreateChangeInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DecideSignalInput, EnsureBudgetInput, FactoryStores, ImageInputError, OutboxEventConflictError, RecordTaskExecutionInput, RecordTaskUsageInput, RejectionDecision, RetryOutboxMessageInput, RootTaskOrigin, SessionConflictError, SettleTaskInput, SettlementError, StepUsage, TaskExecutionFence, TaskPatch, TaskTransitionArguments, TaskTransitionOptions, createStores };
|
|
682
|
+
export { AgentSessionBinding, AnswerEventConflictError, AnswerQuestionInput, AnswerQuestionResult, AppendReceiptInput, ApprovalDecision, ApprovalError, BudgetNoticeStampResult, CacheContinuationContextInput, ChangeAssociationError, ChangePatch, ChangeTransitionArguments, ChangeTransitionOptions, CreateChangeInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DecideSignalInput, EnsureBudgetInput, FactoryStores, ImageInputError, OutboxEventConflictError, RecordTaskExecutionInput, RecordTaskUsageInput, RejectionDecision, RetryOutboxMessageInput, RootTaskOrigin, SessionConflictError, SettleTaskInput, SettlementError, StepUsage, TaskAttemptConflictError, TaskExecutionFence, TaskPatch, TaskTransitionArguments, TaskTransitionOptions, UpdateChangeEvidenceInput, createStores };
|
|
485
683
|
//# sourceMappingURL=engine.d.mts.map
|
package/dist/store/engine.mjs
CHANGED
|
@@ -139,6 +139,19 @@ const stepUsageSchema = z.strictObject({
|
|
|
139
139
|
cacheReadTokens: z.number().int().nonnegative().optional().describe("Cached input tokens read for this step."),
|
|
140
140
|
cacheWriteTokens: z.number().int().nonnegative().optional().describe("Input tokens written to cache for this step.")
|
|
141
141
|
});
|
|
142
|
+
/** Error raised when a Task operation loses its exact-attempt concurrency fence. */
|
|
143
|
+
var TaskAttemptConflictError = class extends Error {
|
|
144
|
+
taskId;
|
|
145
|
+
actualAttempt;
|
|
146
|
+
expectedAttempt;
|
|
147
|
+
constructor(taskId, actualAttempt, expectedAttempt) {
|
|
148
|
+
super(`task ${taskId} is on attempt ${actualAttempt}, not ${expectedAttempt}`);
|
|
149
|
+
this.taskId = taskId;
|
|
150
|
+
this.actualAttempt = actualAttempt;
|
|
151
|
+
this.expectedAttempt = expectedAttempt;
|
|
152
|
+
this.name = "TaskAttemptConflictError";
|
|
153
|
+
}
|
|
154
|
+
};
|
|
142
155
|
/** Error raised when an idempotent session operation conflicts with its original intent. */
|
|
143
156
|
var SessionConflictError = class extends Error {};
|
|
144
157
|
function sameStoredValue(left, right) {
|
|
@@ -170,7 +183,7 @@ function needsLifecycleCommunication(task, state, phase) {
|
|
|
170
183
|
if (state === "dispatched") return phase === "retry";
|
|
171
184
|
if (state === "failed" || state === "cancelled") return true;
|
|
172
185
|
if (state !== "succeeded") return false;
|
|
173
|
-
return task.question === void 0
|
|
186
|
+
return task.question === void 0;
|
|
174
187
|
}
|
|
175
188
|
const changeReceiptStates = {
|
|
176
189
|
verifying: "verifying",
|
|
@@ -219,7 +232,47 @@ async function nextTransientRetry(error, attempt, startedAt) {
|
|
|
219
232
|
await new Promise((resolve) => setTimeout(resolve, nextDelayMs));
|
|
220
233
|
return attempt + 1;
|
|
221
234
|
}
|
|
222
|
-
/**
|
|
235
|
+
/**
|
|
236
|
+
* Creates the validating Factory store engine over a deliberately policy-free driver.
|
|
237
|
+
*
|
|
238
|
+
* @remarks
|
|
239
|
+
* The returned stores validate entities on read and write, enforce legal transitions and
|
|
240
|
+
* compare-and-swap fences, and emit immutable receipts. Construct one configured instance per
|
|
241
|
+
* runtime and share it with launchers, hooks, and tools. A custom workflow must be registered on
|
|
242
|
+
* every process that may read its Tasks; omission is a configuration error rather than permission
|
|
243
|
+
* to select a newer workflow version. Supplying `workflows` replaces the default registry, so
|
|
244
|
+
* include `defaultTaskWorkflow` when generic `task@1` work, Factory tools, or human questions may
|
|
245
|
+
* be created.
|
|
246
|
+
*
|
|
247
|
+
* Wakeups run after their source write commits. Notification failure therefore cannot roll the
|
|
248
|
+
* mutation back; reconciliation must be able to rediscover canonical state.
|
|
249
|
+
*
|
|
250
|
+
* @param options - Persistence and exact workflow/message configuration for this runtime.
|
|
251
|
+
* @returns The complete set of validating Factory stores backed by `options.driver`.
|
|
252
|
+
* @see The version-matched `docs/getting-started.md` shipped with the package.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* import { createInMemoryDriver, createStores } from "@vercel/factory/storage";
|
|
257
|
+
* import {
|
|
258
|
+
* defaultTaskWorkflow,
|
|
259
|
+
* defineWorkflow,
|
|
260
|
+
* defineWorkflows,
|
|
261
|
+
* } from "@vercel/factory/workflows";
|
|
262
|
+
* import { z } from "zod";
|
|
263
|
+
*
|
|
264
|
+
* const greeting = defineWorkflow({
|
|
265
|
+
* id: "greeting",
|
|
266
|
+
* version: 1,
|
|
267
|
+
* input: z.object({ name: z.string() }),
|
|
268
|
+
* output: z.object({ message: z.string() }),
|
|
269
|
+
* });
|
|
270
|
+
* const stores = createStores({
|
|
271
|
+
* driver: createInMemoryDriver(),
|
|
272
|
+
* workflows: defineWorkflows([defaultTaskWorkflow, greeting]),
|
|
273
|
+
* });
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
223
276
|
function createStores(options) {
|
|
224
277
|
const { driver } = options;
|
|
225
278
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -794,6 +847,9 @@ function createStores(options) {
|
|
|
794
847
|
}
|
|
795
848
|
},
|
|
796
849
|
sessions: {
|
|
850
|
+
async getByOperation(owner, operationId) {
|
|
851
|
+
return stores.sessions.get(derivedId("ses", JSON.stringify([owner, operationId])));
|
|
852
|
+
},
|
|
797
853
|
async create({ operationId, ...input }) {
|
|
798
854
|
if (operationId.trim().length === 0 || operationId.length > 200) throw new Error("operationId must contain 1–200 characters");
|
|
799
855
|
const id = derivedId("ses", JSON.stringify([input.owner, operationId]));
|
|
@@ -1104,20 +1160,44 @@ function createStores(options) {
|
|
|
1104
1160
|
}
|
|
1105
1161
|
},
|
|
1106
1162
|
repositories: {
|
|
1163
|
+
async setModelDefaults(id, input) {
|
|
1164
|
+
const existing = await mustRead("repositories", id, repositorySchema);
|
|
1165
|
+
if ((existing.value.modelDefaults?.revision ?? 0) !== input.expectedRevision) throw new VersionConflictError("repositories", id, existing.version);
|
|
1166
|
+
const next = repositorySchema.parse({
|
|
1167
|
+
...existing.value,
|
|
1168
|
+
modelDefaults: {
|
|
1169
|
+
revision: input.expectedRevision + 1,
|
|
1170
|
+
agents: input.agents
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
await driver.update({
|
|
1174
|
+
collection: "repositories",
|
|
1175
|
+
id,
|
|
1176
|
+
value: next,
|
|
1177
|
+
expectedVersion: existing.version
|
|
1178
|
+
});
|
|
1179
|
+
return next;
|
|
1180
|
+
},
|
|
1107
1181
|
async put(repository) {
|
|
1108
|
-
|
|
1182
|
+
let parsed = repositorySchema.parse(repository);
|
|
1109
1183
|
const existing = await read("repositories", parsed.id, repositorySchema);
|
|
1110
1184
|
if (existing === null) await driver.insert({
|
|
1111
1185
|
collection: "repositories",
|
|
1112
1186
|
id: parsed.id,
|
|
1113
1187
|
value: parsed
|
|
1114
1188
|
});
|
|
1115
|
-
else
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1189
|
+
else {
|
|
1190
|
+
parsed = repositorySchema.parse({
|
|
1191
|
+
...parsed,
|
|
1192
|
+
modelDefaults: existing.value.modelDefaults
|
|
1193
|
+
});
|
|
1194
|
+
await driver.update({
|
|
1195
|
+
collection: "repositories",
|
|
1196
|
+
id: parsed.id,
|
|
1197
|
+
value: parsed,
|
|
1198
|
+
expectedVersion: existing.version
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1121
1201
|
return parsed;
|
|
1122
1202
|
},
|
|
1123
1203
|
async get(id) {
|
|
@@ -1158,6 +1238,9 @@ function createStores(options) {
|
|
|
1158
1238
|
const record = await read("signals", id, signalSchema);
|
|
1159
1239
|
return record === null ? null : record.value;
|
|
1160
1240
|
},
|
|
1241
|
+
async getByDelivery({ source, deliveryId }) {
|
|
1242
|
+
return stores.signals.get(derivedId("sig", `${source}:${deliveryId}`));
|
|
1243
|
+
},
|
|
1161
1244
|
async beginTriage(id, options) {
|
|
1162
1245
|
const existing = await mustRead("signals", id, signalSchema);
|
|
1163
1246
|
const current = existing.value;
|
|
@@ -1246,6 +1329,28 @@ function createStores(options) {
|
|
|
1246
1329
|
}
|
|
1247
1330
|
},
|
|
1248
1331
|
tasks: {
|
|
1332
|
+
async pinModel(id, input) {
|
|
1333
|
+
for (let retry = 0; retry < 5; retry++) {
|
|
1334
|
+
const existing = await mustRead("tasks", id, storedTaskSchema);
|
|
1335
|
+
if (existing.value.attempt !== input.attempt) throw new Error("Cannot select a model for a replaced Task attempt");
|
|
1336
|
+
if (existing.value.model !== void 0) {
|
|
1337
|
+
if (existing.value.model !== input.model) throw new Error("Task model cannot be replaced");
|
|
1338
|
+
return existing.value;
|
|
1339
|
+
}
|
|
1340
|
+
if (!["queued", "running"].includes(existing.value.state)) throw new Error("Select a model before work completes or waits");
|
|
1341
|
+
const next = taskSchema.parse({
|
|
1342
|
+
...existing.value,
|
|
1343
|
+
model: input.model
|
|
1344
|
+
});
|
|
1345
|
+
try {
|
|
1346
|
+
await updateTask(next, existing.version);
|
|
1347
|
+
return next;
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
if (!(error instanceof VersionConflictError) || retry === 4) throw error;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
throw new Error("Task model selection conflicted repeatedly");
|
|
1353
|
+
},
|
|
1249
1354
|
async create(value) {
|
|
1250
1355
|
const input = createTaskInputSchema.parse(value);
|
|
1251
1356
|
const timestamp = now();
|
|
@@ -1323,7 +1428,7 @@ function createStores(options) {
|
|
|
1323
1428
|
const existing = await mustRead("tasks", id, storedTaskSchema);
|
|
1324
1429
|
const task = existing.value;
|
|
1325
1430
|
if (task.state !== "running") throw new InvalidTransitionError(task.state, "running", legalNextStates(taskTransitions, task.state));
|
|
1326
|
-
if (expectAttempt !== void 0 && task.attempt !== expectAttempt) throw new
|
|
1431
|
+
if (expectAttempt !== void 0 && task.attempt !== expectAttempt) throw new TaskAttemptConflictError(id, task.attempt, expectAttempt);
|
|
1327
1432
|
if (expectAttempt !== void 0 && task.execution) {
|
|
1328
1433
|
if (!sameStoredValue(task.execution, execution)) throw new Error(`Task ${id} already has a different execution for attempt ${expectAttempt}`);
|
|
1329
1434
|
return task;
|
|
@@ -1531,7 +1636,7 @@ function createStores(options) {
|
|
|
1531
1636
|
if (transitionOptions.patch !== void 0 && "usage" in transitionOptions.patch) throw new SettlementError(id, "usage is set only by recordUsage, not a transition");
|
|
1532
1637
|
let existing = await mustRead("tasks", id, storedTaskSchema);
|
|
1533
1638
|
if (transitionOptions.expectFrom !== void 0 && existing.value.state !== transitionOptions.expectFrom) throw new InvalidTransitionError(existing.value.state, to, legalNextStates(taskTransitions, existing.value.state));
|
|
1534
|
-
if (transitionOptions.expectAttempt !== void 0 && existing.value.attempt !== transitionOptions.expectAttempt) throw new
|
|
1639
|
+
if (transitionOptions.expectAttempt !== void 0 && existing.value.attempt !== transitionOptions.expectAttempt) throw new TaskAttemptConflictError(id, existing.value.attempt, transitionOptions.expectAttempt);
|
|
1535
1640
|
if (transitionOptions.expectExecution !== void 0 && !sameStoredValue(existing.value.execution, transitionOptions.expectExecution)) throw new Error(`Task ${id} execution has been replaced`);
|
|
1536
1641
|
if (transitionOptions.expectQuestionTaskId !== void 0 && existing.value.questionTaskId !== transitionOptions.expectQuestionTaskId) throw new Error(`task ${id} is no longer waiting for question ${transitionOptions.expectQuestionTaskId}`);
|
|
1537
1642
|
assertTransition(taskTransitions, {
|
|
@@ -1815,8 +1920,25 @@ function createStores(options) {
|
|
|
1815
1920
|
await projectSafely(() => projectChangeCatalog(change, 1));
|
|
1816
1921
|
return change;
|
|
1817
1922
|
} catch (error) {
|
|
1818
|
-
if (error instanceof RecordExistsError) {
|
|
1923
|
+
if (error instanceof RecordExistsError) for (;;) {
|
|
1819
1924
|
const existing = await mustRead("changes", change.id, changeSchema);
|
|
1925
|
+
if (change.taskId !== void 0) {
|
|
1926
|
+
if (existing.value.prUrl !== change.prUrl || existing.value.taskId !== void 0 && existing.value.taskId !== change.taskId) throw new ChangeAssociationError(change.taskId, change.repositoryId, "pull request is already recorded with a different URL or producing Task");
|
|
1927
|
+
if (existing.value.taskId === void 0) {
|
|
1928
|
+
const associated = changeSchema.parse({
|
|
1929
|
+
...existing.value,
|
|
1930
|
+
taskId: change.taskId,
|
|
1931
|
+
updatedAt: now()
|
|
1932
|
+
});
|
|
1933
|
+
try {
|
|
1934
|
+
await updateChange(associated, existing.version);
|
|
1935
|
+
return associated;
|
|
1936
|
+
} catch (conflict) {
|
|
1937
|
+
if (conflict instanceof VersionConflictError) continue;
|
|
1938
|
+
throw conflict;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1820
1942
|
await projectSafely(() => projectChangeCatalog(existing.value, existing.version));
|
|
1821
1943
|
return existing.value;
|
|
1822
1944
|
}
|
|
@@ -1827,6 +1949,18 @@ function createStores(options) {
|
|
|
1827
1949
|
const record = await read("changes", id, changeSchema);
|
|
1828
1950
|
return record === null ? null : record.value;
|
|
1829
1951
|
},
|
|
1952
|
+
async updateEvidence(id, input) {
|
|
1953
|
+
const existing = await mustRead("changes", id, changeSchema);
|
|
1954
|
+
if (!sameStoredValue(existing.value, input.expected)) throw new VersionConflictError("changes", id, existing.version);
|
|
1955
|
+
const next = changeSchema.parse({
|
|
1956
|
+
...existing.value,
|
|
1957
|
+
evidence: input.evidence,
|
|
1958
|
+
confidence: input.confidence,
|
|
1959
|
+
updatedAt: now()
|
|
1960
|
+
});
|
|
1961
|
+
await updateChange(next, existing.version);
|
|
1962
|
+
return next;
|
|
1963
|
+
},
|
|
1830
1964
|
async transition(id, to, ...args) {
|
|
1831
1965
|
const transitionOptions = args[0] ?? {};
|
|
1832
1966
|
const existing = await mustRead("changes", id, changeSchema);
|
|
@@ -2170,6 +2304,6 @@ function createStores(options) {
|
|
|
2170
2304
|
return stores;
|
|
2171
2305
|
}
|
|
2172
2306
|
//#endregion
|
|
2173
|
-
export { AnswerEventConflictError, ApprovalError, ChangeAssociationError, ImageInputError, OutboxEventConflictError, SessionConflictError, SettlementError, createStores };
|
|
2307
|
+
export { AnswerEventConflictError, ApprovalError, ChangeAssociationError, ImageInputError, OutboxEventConflictError, SessionConflictError, SettlementError, TaskAttemptConflictError, createStores };
|
|
2174
2308
|
|
|
2175
2309
|
//# sourceMappingURL=engine.mjs.map
|