@vercel/factory 0.0.17 → 0.0.18
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 +46 -0
- package/dist/api-contracts.d.mts +665 -715
- package/dist/api-contracts.mjs +91 -4
- package/dist/api-contracts.mjs.map +1 -1
- package/dist/api-task-graph.mjs +14 -5
- package/dist/api-task-graph.mjs.map +1 -1
- package/dist/api.d.mts +58 -11
- package/dist/api.mjs +715 -399
- package/dist/api.mjs.map +1 -1
- package/dist/approval-access.d.mts +1 -1
- package/dist/approval-contracts.d.mts +3 -4
- package/dist/approval-contracts.mjs +1 -2
- package/dist/approval-contracts.mjs.map +1 -1
- package/dist/blob/index.d.mts +1 -1
- package/dist/budget.mjs +20 -3
- package/dist/budget.mjs.map +1 -1
- package/dist/change-verification/brief.d.mts +1 -1
- package/dist/change-verification/dispatch.mjs +8 -1
- package/dist/change-verification/dispatch.mjs.map +1 -1
- package/dist/changes/eve-record-change.mjs +4 -1
- package/dist/changes/eve-record-change.mjs.map +1 -1
- package/dist/client-stream.d.mts +19 -2
- package/dist/client-stream.mjs +115 -53
- package/dist/client-stream.mjs.map +1 -1
- package/dist/client-transcript.d.mts +25 -2
- package/dist/client-transcript.mjs +95 -13
- package/dist/client-transcript.mjs.map +1 -1
- package/dist/client.d.mts +145 -19
- package/dist/client.mjs +89 -16
- 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 -2
- package/dist/code-review/eve-post-review.mjs +8 -0
- package/dist/code-review/eve-post-review.mjs.map +1 -1
- package/dist/code-review/github-reporter.d.mts +6 -2
- package/dist/code-review/github-reporter.mjs +86 -35
- package/dist/code-review/github-reporter.mjs.map +1 -1
- package/dist/code-review/preparation.d.mts +2 -1
- package/dist/code-review/preparation.mjs +2 -1
- package/dist/code-review/preparation.mjs.map +1 -1
- package/dist/code-review/stall.mjs +11 -2
- package/dist/code-review/stall.mjs.map +1 -1
- package/dist/connectors/github.d.mts +1 -1
- package/dist/connectors/index.d.mts +1 -1
- package/dist/connectors/slack.d.mts +1 -1
- package/dist/delivery-metrics/metrics.d.mts +1 -1
- package/dist/dispatch.mjs +3 -3
- package/dist/dispatch.mjs.map +1 -1
- package/dist/eve/index.d.mts +1 -1
- package/dist/eve/index.mjs +8 -1
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/task-execution.d.mts +4 -4
- package/dist/eve/transcript.d.mts +3 -1
- package/dist/eve/transcript.mjs.map +1 -1
- package/dist/finding-remediation/admission.d.mts +1 -1
- package/dist/finding-remediation/brief.d.mts +1 -1
- package/dist/github-app.d.mts +1 -1
- package/dist/github-publication.d.mts +1 -1
- package/dist/github.d.mts +9 -1
- package/dist/github.mjs +29 -11
- package/dist/github.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/intake-contracts.d.mts +1 -0
- package/dist/intake-contracts.mjs +2 -1
- package/dist/intake-contracts.mjs.map +1 -1
- package/dist/integrations/github-issues.d.mts +53 -0
- package/dist/integrations/github-issues.mjs +170 -0
- package/dist/integrations/github-issues.mjs.map +1 -0
- package/dist/integrations/github.d.mts +2 -1
- package/dist/integrations/github.mjs +2 -1
- package/dist/multi-repository-changes/coordination.mjs +1 -1
- package/dist/planning/reconcile.mjs +15 -2
- package/dist/planning/reconcile.mjs.map +1 -1
- package/dist/postgres/index.d.mts +2 -1
- package/dist/postgres/index.mjs +11 -5
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/postgres/indexed-codec.mjs +137 -0
- package/dist/postgres/indexed-codec.mjs.map +1 -0
- package/dist/postgres/indexed-schema.mjs +99 -0
- package/dist/postgres/indexed-schema.mjs.map +1 -0
- package/dist/postgres/indexed.d.mts +28 -0
- package/dist/postgres/indexed.mjs +277 -0
- package/dist/postgres/indexed.mjs.map +1 -0
- package/dist/postgres/record-predicates.mjs +23 -0
- package/dist/postgres/record-predicates.mjs.map +1 -0
- package/dist/presets/software-development/deepsec-brief.d.mts +1 -1
- package/dist/presets/software-development/dispatch.mjs +1 -1
- package/dist/presets/software-development/dispatch.mjs.map +1 -1
- package/dist/presets/software-development/recovery.mjs +9 -2
- package/dist/presets/software-development/recovery.mjs.map +1 -1
- package/dist/pull-requests/eve-fetch.d.mts +4 -1
- package/dist/pull-requests/eve-fetch.mjs +9 -0
- package/dist/pull-requests/eve-fetch.mjs.map +1 -1
- package/dist/pull-requests/github-publisher.d.mts +1 -1
- package/dist/pull-requests/github-publisher.mjs +19 -7
- package/dist/pull-requests/github-publisher.mjs.map +1 -1
- package/dist/repositories.mjs +20 -13
- package/dist/repositories.mjs.map +1 -1
- package/dist/sandbox/execution-run-ledger.d.mts +2 -2
- package/dist/sandbox/execution-run-ledger.mjs.map +1 -1
- package/dist/sandbox/execution.d.mts +2 -2
- package/dist/sandbox/execution.mjs.map +1 -1
- package/dist/schema/catalog.mjs +1 -1
- package/dist/schema/change.d.mts +1 -1
- package/dist/schema/communication.d.mts +2 -2
- package/dist/schema/factory-config.d.mts +1 -1
- package/dist/schema/outbox.d.mts +6 -6
- package/dist/schema/session-summary.d.mts +218 -0
- package/dist/schema/session-summary.mjs +68 -0
- package/dist/schema/session-summary.mjs.map +1 -0
- package/dist/schema/session.d.mts +39 -59
- package/dist/schema/session.mjs +6 -6
- package/dist/schema/session.mjs.map +1 -1
- package/dist/schema/task-graph.d.mts +24 -24
- package/dist/schema/transcript.d.mts +141 -3
- package/dist/schema/transcript.mjs +19 -3
- package/dist/schema/transcript.mjs.map +1 -1
- package/dist/schema/transitions.d.mts +1 -1
- package/dist/schema/transitions.mjs +1 -1
- package/dist/schema/transitions.mjs.map +1 -1
- package/dist/signals.d.mts +1 -1
- package/dist/stall.mjs +10 -2
- package/dist/stall.mjs.map +1 -1
- package/dist/storage.d.mts +10 -1
- package/dist/storage.mjs +7 -1
- package/dist/store/approvals.d.mts +14 -0
- package/dist/store/approvals.mjs +78 -0
- package/dist/store/approvals.mjs.map +1 -0
- package/dist/store/conversations.d.mts +59 -0
- package/dist/store/conversations.mjs +549 -0
- package/dist/store/conversations.mjs.map +1 -0
- package/dist/store/driver.d.mts +1 -1
- package/dist/store/driver.mjs.map +1 -1
- package/dist/store/engine.d.mts +38 -16
- package/dist/store/engine.mjs +229 -106
- package/dist/store/engine.mjs.map +1 -1
- package/dist/store/indexed-engine.d.mts +57 -0
- package/dist/store/indexed-engine.mjs +155 -0
- package/dist/store/indexed-engine.mjs.map +1 -0
- package/dist/store/indexed-memory.d.mts +7 -0
- package/dist/store/indexed-memory.mjs +200 -0
- package/dist/store/indexed-memory.mjs.map +1 -0
- package/dist/store/indexed-projections.d.mts +280 -0
- package/dist/store/indexed-projections.mjs +481 -0
- package/dist/store/indexed-projections.mjs.map +1 -0
- package/dist/store/indexed.d.mts +101 -0
- package/dist/store/indexed.mjs +19 -0
- package/dist/store/indexed.mjs.map +1 -0
- package/dist/store/iterate.d.mts +9 -0
- package/dist/store/iterate.mjs +23 -0
- package/dist/store/iterate.mjs.map +1 -0
- package/dist/store/query-scope.mjs +62 -0
- package/dist/store/query-scope.mjs.map +1 -0
- package/dist/store/query.d.mts +32 -0
- package/dist/store/query.mjs +103 -0
- package/dist/store/query.mjs.map +1 -0
- package/dist/store/session-history.mjs +24 -0
- package/dist/store/session-history.mjs.map +1 -0
- package/dist/store/slack-pr-notifications.d.mts +2 -2
- package/dist/store/slack-pr-notifications.mjs.map +1 -1
- package/dist/store/state.d.mts +26 -0
- package/dist/store/state.mjs +95 -0
- package/dist/store/state.mjs.map +1 -0
- package/dist/store/task-effects.d.mts +1 -1
- package/dist/store/task-graphs.d.mts +1 -1
- package/dist/store/task-messages.d.mts +1 -1
- package/dist/store/task-work.d.mts +1 -1
- package/dist/store/task-work.mjs +1 -1
- package/dist/store/transcripts.mjs +308 -0
- package/dist/store/transcripts.mjs.map +1 -0
- package/dist/sweep.d.mts +1 -1
- package/dist/sweep.mjs +12 -7
- package/dist/sweep.mjs.map +1 -1
- package/dist/task-communication.mjs +15 -1
- package/dist/task-communication.mjs.map +1 -1
- package/dist/task-protocols.d.mts +3 -3
- package/dist/tasks.d.mts +1 -1
- package/dist/vercel.d.mts +1 -1
- package/dist/work-triage/find-existing-work.d.mts +1 -1
- package/dist/work-triage/find-existing-work.mjs +12 -2
- package/dist/work-triage/find-existing-work.mjs.map +1 -1
- package/docs/index.md +4 -0
- package/docs/recipes/persistence-recovery.md +15 -9
- package/docs/recipes/postgres-indexed-engine.md +108 -0
- package/docs/recipes/postgres-indexed-storage.md +145 -0
- package/package.json +3 -1
package/dist/store/engine.mjs
CHANGED
|
@@ -5,12 +5,12 @@ import { budgetSchema } from "../schema/budget.mjs";
|
|
|
5
5
|
import { parseAgentRouteBinding } from "../schema/agent-route.mjs";
|
|
6
6
|
import { jsonValueSchema, taskWorkSchema } from "../schema/work.mjs";
|
|
7
7
|
import { taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema } from "../schema/task.mjs";
|
|
8
|
+
import { RecordExistsError, RecordNotFoundError, TransientStoreError, VersionConflictError } from "./driver.mjs";
|
|
8
9
|
import { InvalidTransitionError, assertTransition, changeInitialState, changeTransitions, legalNextStates, signalOutcomeTransitions, signalTriageTransitions, taskInitialState, taskTransitions } from "../schema/transitions.mjs";
|
|
9
10
|
import { costSchema, receiptSchema } from "../schema/receipt.mjs";
|
|
10
11
|
import { imageAttachmentSchema, imageAttachmentsSchema, imageIdSchema, imageUploadSchema } from "../image-input.mjs";
|
|
11
12
|
import { transcriptSnapshotSchema } from "../schema/transcript.mjs";
|
|
12
13
|
import { routeTaskWork } from "../agent-routes.mjs";
|
|
13
|
-
import { RecordExistsError, RecordNotFoundError, TransientStoreError, VersionConflictError } from "./driver.mjs";
|
|
14
14
|
import { answerEventSchema } from "../schema/answer.mjs";
|
|
15
15
|
import { taskGraphDependenciesSchema } from "../schema/task-graph.mjs";
|
|
16
16
|
import { defineTaskMessageProtocols } from "../schema/task-message.mjs";
|
|
@@ -18,17 +18,22 @@ import { createTaskGraphStore } from "./task-graphs.mjs";
|
|
|
18
18
|
import { createTaskMessageStore } from "./task-messages.mjs";
|
|
19
19
|
import { createTaskWorkStore } from "./task-work.mjs";
|
|
20
20
|
import { createTaskEffectStore } from "./task-effects.mjs";
|
|
21
|
+
import { pageRecords, recordQuerySchema } from "./query.mjs";
|
|
22
|
+
import { changeSchema } from "../schema/change.mjs";
|
|
23
|
+
import { agentSessionIdSchema, agentSessionSchema } from "../schema/session.mjs";
|
|
24
|
+
import { signalSchema } from "../schema/signal.mjs";
|
|
25
|
+
import { communicationEventSchema } from "../schema/communication.mjs";
|
|
26
|
+
import { sessionContinuationRecordSchema, sessionNotificationRecordSchema } from "./session-history.mjs";
|
|
27
|
+
import { factoryIndexKey } from "./indexed-projections.mjs";
|
|
28
|
+
import { recordQueryScope } from "./query-scope.mjs";
|
|
21
29
|
import { defaultTaskWorkflow, defineWorkflows } from "../workflows.mjs";
|
|
22
30
|
import { createWorkNotifier } from "./work-notifications.mjs";
|
|
23
|
-
import { communicationEventSchema } from "../schema/communication.mjs";
|
|
24
|
-
import { agentSessionIdSchema, agentSessionSchema } from "../schema/session.mjs";
|
|
25
31
|
import { externalSessionSchema, storedSessionSchema } from "../schema/stored-session.mjs";
|
|
26
|
-
import { changeSchema } from "../schema/change.mjs";
|
|
27
32
|
import { outboxMessageSchema, outboxQueueSchema } from "../schema/outbox.mjs";
|
|
28
33
|
import { storedTaskSchema } from "../schema/stored-task.mjs";
|
|
29
34
|
import { changeCatalogSchema, externalSessionCatalogSchema, sessionCatalogSchema, taskCatalogSchema, taskRouteSchema } from "../schema/catalog.mjs";
|
|
35
|
+
import { pageTranscriptSnapshot } from "./transcripts.mjs";
|
|
30
36
|
import { questionRouteSchema } from "../schema/question-route.mjs";
|
|
31
|
-
import { signalSchema } from "../schema/signal.mjs";
|
|
32
37
|
import { createHash, randomUUID } from "node:crypto";
|
|
33
38
|
import { z } from "zod";
|
|
34
39
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -177,9 +182,10 @@ const taskReceiptStates = {
|
|
|
177
182
|
needs_human: "needs_human",
|
|
178
183
|
cancelled: "cancelled"
|
|
179
184
|
};
|
|
180
|
-
function needsLifecycleCommunication(task, state, phase) {
|
|
185
|
+
function needsLifecycleCommunication(task, state, phase, parent, includeChild) {
|
|
181
186
|
if (state === "needs_human") return task.question !== void 0;
|
|
182
|
-
|
|
187
|
+
const isConversationDelegation = task.parentTaskId !== void 0 && parent?.work.route?.id === "conversation";
|
|
188
|
+
if (task.parentTaskId !== void 0 && !isConversationDelegation && !includeChild?.(task, state)) return false;
|
|
183
189
|
if (state === "dispatched") return phase === "retry";
|
|
184
190
|
if (state === "failed" || state === "cancelled") return true;
|
|
185
191
|
if (state !== "succeeded") return false;
|
|
@@ -274,6 +280,9 @@ async function nextTransientRetry(error, attempt, startedAt) {
|
|
|
274
280
|
* ```
|
|
275
281
|
*/
|
|
276
282
|
function createStores(options) {
|
|
283
|
+
return createStoreEngine(options);
|
|
284
|
+
}
|
|
285
|
+
function createStoreEngine(options, indexed) {
|
|
277
286
|
const { driver } = options;
|
|
278
287
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
279
288
|
const workflows = options.workflows ?? defineWorkflows([defaultTaskWorkflow]);
|
|
@@ -301,8 +310,41 @@ function createStores(options) {
|
|
|
301
310
|
if (record === null) throw new RecordNotFoundError(collection, id);
|
|
302
311
|
return record;
|
|
303
312
|
}
|
|
313
|
+
async function listPage(collections, schema, input, catalog) {
|
|
314
|
+
const query = recordQuerySchema.parse(input);
|
|
315
|
+
if (indexed) {
|
|
316
|
+
const page = await indexed.queryRecords({
|
|
317
|
+
sources: collections.map((collection) => ({
|
|
318
|
+
collection,
|
|
319
|
+
index: factoryIndexKey(recordQueryScope(collection, query))
|
|
320
|
+
})),
|
|
321
|
+
...query
|
|
322
|
+
});
|
|
323
|
+
return {
|
|
324
|
+
items: page.entries.map((entry) => {
|
|
325
|
+
if (entry.collection === "sessions") storedSessionSchema.parse(entry.record.value);
|
|
326
|
+
if (entry.collection === "externalSessions") externalSessionSchema.parse(entry.record.value);
|
|
327
|
+
return schema.parse(entry.record.value);
|
|
328
|
+
}),
|
|
329
|
+
nextCursor: page.nextCursor
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const values = catalog ? await catalog() : (await Promise.all(collections.map((collection) => listAll(collection, schema)))).flat();
|
|
333
|
+
return pageRecords(collections.join(":"), values, query, (value) => {
|
|
334
|
+
const key = z.object({
|
|
335
|
+
id: z.string().optional(),
|
|
336
|
+
receiptId: z.string().optional()
|
|
337
|
+
}).parse(value);
|
|
338
|
+
return z.string().min(1).parse(key.id ?? key.receiptId);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
304
341
|
async function listAll(collection, schema) {
|
|
305
|
-
|
|
342
|
+
if (indexed) throw new Error("Indexed stores require bounded queries instead of collection lists");
|
|
343
|
+
return (await driver.list(collection)).map((entry) => {
|
|
344
|
+
if (collection === "sessions") storedSessionSchema.parse(entry.record.value);
|
|
345
|
+
if (collection === "externalSessions") externalSessionSchema.parse(entry.record.value);
|
|
346
|
+
return schema.parse(entry.record.value);
|
|
347
|
+
});
|
|
306
348
|
}
|
|
307
349
|
async function forEachConcurrent(values, limit, operation) {
|
|
308
350
|
let next = 0;
|
|
@@ -313,12 +355,10 @@ function createStores(options) {
|
|
|
313
355
|
const catalogShards = "0123456789abcdef".split("");
|
|
314
356
|
const shardFor = (id) => createHash("sha256").update(id).digest("hex")[0];
|
|
315
357
|
function sessionSummary(session) {
|
|
316
|
-
return agentSessionSchema.parse(
|
|
317
|
-
...session,
|
|
318
|
-
continuations: session.continuations?.map(({ context: _context, ...continuation }) => continuation)
|
|
319
|
-
});
|
|
358
|
+
return agentSessionSchema.parse(session);
|
|
320
359
|
}
|
|
321
360
|
async function projectCatalogEntry(collection, prefix, schema, value, sourceVersion) {
|
|
361
|
+
if (indexed) return;
|
|
322
362
|
const id = `${prefix}-${shardFor(value.id)}`;
|
|
323
363
|
for (;;) {
|
|
324
364
|
const existing = await driver.get(collection, id);
|
|
@@ -360,6 +400,7 @@ function createStores(options) {
|
|
|
360
400
|
const projectSessionCatalog = (value, sourceVersion) => projectCatalogEntry("sessionCatalogs", "sessions", sessionCatalogSchema, sessionSummary(value), sourceVersion);
|
|
361
401
|
const projectExternalSessionCatalog = (value, sourceVersion) => projectCatalogEntry("externalSessionCatalogs", "external-sessions", externalSessionCatalogSchema, sessionSummary(value), sourceVersion);
|
|
362
402
|
async function markCatalogRepair(collection, prefix, schema, sourceId, sourceVersion) {
|
|
403
|
+
if (indexed) return;
|
|
363
404
|
const id = `${prefix}-${shardFor(sourceId)}`;
|
|
364
405
|
for (;;) {
|
|
365
406
|
const existing = await driver.get(collection, id);
|
|
@@ -400,6 +441,7 @@ function createStores(options) {
|
|
|
400
441
|
const markSessionCatalogRepair = (id, sourceVersion) => markCatalogRepair("sessionCatalogs", "sessions", sessionCatalogSchema, id, sourceVersion);
|
|
401
442
|
const markExternalSessionCatalogRepair = (id, sourceVersion) => markCatalogRepair("externalSessionCatalogs", "external-sessions", externalSessionCatalogSchema, id, sourceVersion);
|
|
402
443
|
async function projectTaskRoute(value, sourceVersion) {
|
|
444
|
+
if (indexed) return;
|
|
403
445
|
if (value.replyTo.channel !== "api") return;
|
|
404
446
|
const id = derivedId("task-route", JSON.stringify([value.replyTo.channel, value.replyTo.address]));
|
|
405
447
|
for (;;) {
|
|
@@ -438,6 +480,7 @@ function createStores(options) {
|
|
|
438
480
|
}
|
|
439
481
|
}
|
|
440
482
|
async function markTaskRouteRepair(value, sourceVersion) {
|
|
483
|
+
if (indexed) return;
|
|
441
484
|
if (value.replyTo.channel !== "api") return;
|
|
442
485
|
const id = derivedId("task-route", JSON.stringify([value.replyTo.channel, value.replyTo.address]));
|
|
443
486
|
for (;;) {
|
|
@@ -488,6 +531,7 @@ function createStores(options) {
|
|
|
488
531
|
}
|
|
489
532
|
}
|
|
490
533
|
async function catalogValues(source, collection, prefix, sourceSchema, catalogSchema, project, fallbackValue = (value) => value) {
|
|
534
|
+
if (indexed) throw new Error("Indexed stores require bounded queries instead of catalogs");
|
|
491
535
|
let catalogs = await Promise.all(catalogShards.map((shard) => driver.get(collection, `${prefix}-${shard}`)));
|
|
492
536
|
if (catalogs.some((catalog) => catalog === null)) {
|
|
493
537
|
const entries = await driver.list(source);
|
|
@@ -650,7 +694,8 @@ function createStores(options) {
|
|
|
650
694
|
reason: reason ?? `rejected by ${by}`,
|
|
651
695
|
attempt: task.attempt
|
|
652
696
|
});
|
|
653
|
-
|
|
697
|
+
const rejectionParent = next.parentTaskId ? await stores.tasks.get(next.parentTaskId) : null;
|
|
698
|
+
if (needsLifecycleCommunication(next, "cancelled", "approval", rejectionParent, options.includeChildTaskCommunication)) next = taskSchema.parse({
|
|
654
699
|
...next,
|
|
655
700
|
pendingCommunicationReceiptIds: [.../* @__PURE__ */ new Set([...next.pendingCommunicationReceiptIds ?? [], cancelled.receiptId])]
|
|
656
701
|
});
|
|
@@ -698,6 +743,41 @@ function createStores(options) {
|
|
|
698
743
|
}
|
|
699
744
|
}
|
|
700
745
|
}
|
|
746
|
+
async function commitLatestContinuation(session) {
|
|
747
|
+
if (!session.latestContinuationId) return null;
|
|
748
|
+
for (;;) {
|
|
749
|
+
const record = await mustRead("sessionContinuations", session.latestContinuationId, sessionContinuationRecordSchema);
|
|
750
|
+
if (record.value.sessionId !== session.id) throw new SessionConflictError("Continuation belongs to another session");
|
|
751
|
+
if (record.value.committed) return record.value;
|
|
752
|
+
try {
|
|
753
|
+
const next = {
|
|
754
|
+
...record.value,
|
|
755
|
+
committed: true
|
|
756
|
+
};
|
|
757
|
+
await driver.update({
|
|
758
|
+
collection: "sessionContinuations",
|
|
759
|
+
id: record.value.id,
|
|
760
|
+
value: next,
|
|
761
|
+
expectedVersion: record.version
|
|
762
|
+
});
|
|
763
|
+
return next;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
if (!(error instanceof VersionConflictError)) throw error;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function withinSession(id, query, committed) {
|
|
770
|
+
const parsed = recordQuerySchema.parse(query);
|
|
771
|
+
const alternatives = parsed.match === void 0 ? [{}] : Array.isArray(parsed.match) ? parsed.match : [parsed.match];
|
|
772
|
+
return {
|
|
773
|
+
...parsed,
|
|
774
|
+
match: alternatives.map((match) => ({
|
|
775
|
+
...match,
|
|
776
|
+
sessionId: id,
|
|
777
|
+
...committed === void 0 ? {} : { committed }
|
|
778
|
+
}))
|
|
779
|
+
};
|
|
780
|
+
}
|
|
701
781
|
const stores = {
|
|
702
782
|
driverName: driver.name,
|
|
703
783
|
workflowRecords: {
|
|
@@ -752,6 +832,21 @@ function createStores(options) {
|
|
|
752
832
|
}
|
|
753
833
|
}),
|
|
754
834
|
transcripts: {
|
|
835
|
+
async head(execution) {
|
|
836
|
+
const snapshot = await stores.transcripts.get(execution);
|
|
837
|
+
return snapshot === null ? null : {
|
|
838
|
+
...snapshot,
|
|
839
|
+
blocks: [],
|
|
840
|
+
blockCount: snapshot.blocks.length
|
|
841
|
+
};
|
|
842
|
+
},
|
|
843
|
+
async page(execution, input) {
|
|
844
|
+
const snapshot = await stores.transcripts.get(execution);
|
|
845
|
+
return snapshot === null ? null : pageTranscriptSnapshot(execution, snapshot, input);
|
|
846
|
+
},
|
|
847
|
+
async block(execution, blockId) {
|
|
848
|
+
return (await stores.transcripts.get(execution))?.blocks.find((block) => block.id === blockId) ?? null;
|
|
849
|
+
},
|
|
755
850
|
async get(execution) {
|
|
756
851
|
const record = await read("transcripts", derivedId("transcript", JSON.stringify([execution.provider, execution.sessionId])), storedTranscriptSchema);
|
|
757
852
|
if (record === null) return null;
|
|
@@ -874,8 +969,8 @@ function createStores(options) {
|
|
|
874
969
|
const record = await mustRead("sessions", id, storedSessionSchema);
|
|
875
970
|
await projectSafely(() => projectSessionCatalog(record.value, record.version));
|
|
876
971
|
const existing = record.value;
|
|
877
|
-
const { createdAt: _at, execution: _execution, taskId: _task,
|
|
878
|
-
const { createdAt: _newAt,
|
|
972
|
+
const { createdAt: _at, execution: _execution, taskId: _task, latestNotificationId: _notification, latestContinuationId: _continuation, ...intent } = existing;
|
|
973
|
+
const { createdAt: _newAt, ...nextIntent } = candidate;
|
|
879
974
|
if (!sameStoredValue(intent, nextIntent)) throw new SessionConflictError("Session operationId was reused with different input");
|
|
880
975
|
return existing;
|
|
881
976
|
}
|
|
@@ -955,24 +1050,66 @@ function createStores(options) {
|
|
|
955
1050
|
return existing.value;
|
|
956
1051
|
}
|
|
957
1052
|
},
|
|
1053
|
+
async getContinuation(id, taskId) {
|
|
1054
|
+
const record = (await listPage(["sessionContinuations"], sessionContinuationRecordSchema, {
|
|
1055
|
+
limit: 1,
|
|
1056
|
+
match: {
|
|
1057
|
+
sessionId: id,
|
|
1058
|
+
taskId
|
|
1059
|
+
}
|
|
1060
|
+
})).items[0];
|
|
1061
|
+
if (!record) return null;
|
|
1062
|
+
if (record.committed) return record.continuation;
|
|
1063
|
+
return (await stores.sessions.get(id))?.latestContinuationId === record.id ? record.continuation : null;
|
|
1064
|
+
},
|
|
1065
|
+
async getContinuationByOperation(id, operationId) {
|
|
1066
|
+
const key = derivedId("scont", JSON.stringify([id, operationId]));
|
|
1067
|
+
const record = await read("sessionContinuations", key, sessionContinuationRecordSchema);
|
|
1068
|
+
if (!record) return null;
|
|
1069
|
+
if (record.value.sessionId !== id) throw new SessionConflictError("Continuation belongs to another session");
|
|
1070
|
+
if (record.value.committed) return record.value.continuation;
|
|
1071
|
+
return (await stores.sessions.get(id))?.latestContinuationId === key ? record.value.continuation : null;
|
|
1072
|
+
},
|
|
1073
|
+
async listContinuations(id, query) {
|
|
1074
|
+
await commitLatestContinuation((await mustRead("sessions", id, storedSessionSchema)).value);
|
|
1075
|
+
const page = await listPage(["sessionContinuations"], sessionContinuationRecordSchema, withinSession(id, query, true));
|
|
1076
|
+
return {
|
|
1077
|
+
items: page.items.map((record) => record.continuation),
|
|
1078
|
+
nextCursor: page.nextCursor
|
|
1079
|
+
};
|
|
1080
|
+
},
|
|
1081
|
+
async listNotifications(id, query) {
|
|
1082
|
+
const page = await listPage(["sessionNotifications"], sessionNotificationRecordSchema, withinSession(id, query));
|
|
1083
|
+
return {
|
|
1084
|
+
items: page.items.map((record) => record.event),
|
|
1085
|
+
nextCursor: page.nextCursor
|
|
1086
|
+
};
|
|
1087
|
+
},
|
|
958
1088
|
async cacheContinuationContext(id, input) {
|
|
959
1089
|
const { taskId, context, attachments } = input;
|
|
1090
|
+
const session = await mustRead("sessions", id, storedSessionSchema);
|
|
1091
|
+
const continuation = await stores.sessions.getContinuation(id, taskId);
|
|
1092
|
+
if (!continuation) throw new SessionConflictError("Task is not a conversation continuation");
|
|
1093
|
+
await stores.images.validate(session.value.owner, attachments ?? []);
|
|
1094
|
+
const key = derivedId("scont", JSON.stringify([id, continuation.operationId]));
|
|
960
1095
|
for (;;) {
|
|
961
|
-
const record = await mustRead("
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if (continuation.context !== void 0) return continuation.context;
|
|
965
|
-
await stores.images.validate(record.value.owner, attachments ?? []);
|
|
966
|
-
const next = storedSessionSchema.parse({
|
|
1096
|
+
const record = await mustRead("sessionContinuations", key, sessionContinuationRecordSchema);
|
|
1097
|
+
if (record.value.continuation.context !== void 0) return record.value.continuation.context;
|
|
1098
|
+
const next = sessionContinuationRecordSchema.parse({
|
|
967
1099
|
...record.value,
|
|
968
|
-
|
|
969
|
-
...
|
|
1100
|
+
continuation: {
|
|
1101
|
+
...record.value.continuation,
|
|
970
1102
|
context,
|
|
971
1103
|
...attachments?.length ? { contextAttachments: attachments } : {}
|
|
972
|
-
}
|
|
1104
|
+
}
|
|
973
1105
|
});
|
|
974
1106
|
try {
|
|
975
|
-
await
|
|
1107
|
+
await driver.update({
|
|
1108
|
+
collection: "sessionContinuations",
|
|
1109
|
+
id: key,
|
|
1110
|
+
value: next,
|
|
1111
|
+
expectedVersion: record.version
|
|
1112
|
+
});
|
|
976
1113
|
return context;
|
|
977
1114
|
} catch (error) {
|
|
978
1115
|
if (!(error instanceof VersionConflictError)) throw error;
|
|
@@ -981,16 +1118,19 @@ function createStores(options) {
|
|
|
981
1118
|
},
|
|
982
1119
|
async continue(id, input) {
|
|
983
1120
|
if (input.operationId.trim().length === 0 || input.operationId.length > 200) throw new SessionConflictError("operationId must contain 1–200 characters");
|
|
1121
|
+
const continuationId = derivedId("scont", JSON.stringify([id, input.operationId]));
|
|
984
1122
|
const dedupeKey = `session:${id}:follow-up:${input.operationId}`;
|
|
985
1123
|
for (;;) {
|
|
986
1124
|
const record = await mustRead("sessions", id, storedSessionSchema);
|
|
987
1125
|
const session = record.value;
|
|
1126
|
+
const latestRecord = await commitLatestContinuation(session);
|
|
988
1127
|
await stores.images.validate(session.owner, input.attachments ?? []);
|
|
989
1128
|
if (session.repositoryId === void 0 || session.taskId === void 0) throw new SessionConflictError("Follow-up work requires a repository-bound task conversation");
|
|
990
1129
|
const parent = (await mustRead("tasks", input.parentTaskId, storedTaskSchema)).value;
|
|
991
1130
|
if (parent.id !== session.taskId && !(parent.replyTo.channel === "api" && parent.replyTo.address === id)) throw new SessionConflictError("Follow-up parent is outside this conversation");
|
|
992
1131
|
if (parent.repositoryIds.length !== 1 || parent.repositoryIds[0] !== session.repositoryId) throw new SessionConflictError("Follow-up repository differs from its conversation");
|
|
993
|
-
const
|
|
1132
|
+
const previousRecord = await read("sessionContinuations", continuationId, sessionContinuationRecordSchema);
|
|
1133
|
+
const previous = previousRecord?.value.continuation;
|
|
994
1134
|
if (previous !== void 0 && !sameStoredValue({
|
|
995
1135
|
operationId: input.operationId,
|
|
996
1136
|
parentTaskId: input.parentTaskId,
|
|
@@ -1014,14 +1154,14 @@ function createStores(options) {
|
|
|
1014
1154
|
createdAt: now()
|
|
1015
1155
|
};
|
|
1016
1156
|
let next = session;
|
|
1017
|
-
if (
|
|
1157
|
+
if (!previousRecord?.value.committed) {
|
|
1018
1158
|
if (![
|
|
1019
1159
|
"succeeded",
|
|
1020
1160
|
"failed",
|
|
1021
1161
|
"cancelled",
|
|
1022
1162
|
"verifying"
|
|
1023
1163
|
].includes(parent.state)) throw new SessionConflictError("Wait for this task or answer its pending question before starting a follow-up");
|
|
1024
|
-
const latest =
|
|
1164
|
+
const latest = latestRecord?.continuation;
|
|
1025
1165
|
if (latest !== void 0 && latest.taskId !== parent.id) {
|
|
1026
1166
|
let ancestor = parent;
|
|
1027
1167
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1039,12 +1179,27 @@ function createStores(options) {
|
|
|
1039
1179
|
}
|
|
1040
1180
|
next = storedSessionSchema.parse({
|
|
1041
1181
|
...session,
|
|
1042
|
-
|
|
1182
|
+
latestContinuationId: continuationId
|
|
1043
1183
|
});
|
|
1184
|
+
if (previousRecord && previousRecord.value.previousId !== session.latestContinuationId) throw new SessionConflictError("Conversation changed before this follow-up was admitted; refresh and use a new operationId");
|
|
1044
1185
|
try {
|
|
1186
|
+
if (!previousRecord) await driver.insert({
|
|
1187
|
+
collection: "sessionContinuations",
|
|
1188
|
+
id: continuationId,
|
|
1189
|
+
value: sessionContinuationRecordSchema.parse({
|
|
1190
|
+
id: continuationId,
|
|
1191
|
+
sessionId: id,
|
|
1192
|
+
taskId: continuation.taskId,
|
|
1193
|
+
previousId: session.latestContinuationId,
|
|
1194
|
+
committed: false,
|
|
1195
|
+
createdAt: continuation.createdAt,
|
|
1196
|
+
continuation
|
|
1197
|
+
})
|
|
1198
|
+
});
|
|
1045
1199
|
await updateSession(next, record.version);
|
|
1200
|
+
await commitLatestContinuation(next);
|
|
1046
1201
|
} catch (error) {
|
|
1047
|
-
if (error instanceof VersionConflictError) continue;
|
|
1202
|
+
if (error instanceof VersionConflictError || error instanceof RecordExistsError) continue;
|
|
1048
1203
|
throw error;
|
|
1049
1204
|
}
|
|
1050
1205
|
}
|
|
@@ -1099,11 +1254,8 @@ function createStores(options) {
|
|
|
1099
1254
|
if (records.length > 1) throw new SessionConflictError("Session ID exists in multiple collections");
|
|
1100
1255
|
return records[0]?.value ?? null;
|
|
1101
1256
|
},
|
|
1102
|
-
|
|
1103
|
-
return (
|
|
1104
|
-
},
|
|
1105
|
-
async summaries() {
|
|
1106
|
-
return (await Promise.all([catalogValues("sessions", "sessionCatalogs", "sessions", storedSessionSchema, sessionCatalogSchema, projectSessionCatalog, sessionSummary), catalogValues("externalSessions", "externalSessionCatalogs", "external-sessions", externalSessionSchema, externalSessionCatalogSchema, projectExternalSessionCatalog, sessionSummary)])).flat();
|
|
1257
|
+
list(query) {
|
|
1258
|
+
return listPage(["sessions", "externalSessions"], agentSessionSchema, query);
|
|
1107
1259
|
},
|
|
1108
1260
|
async bind(id, reference) {
|
|
1109
1261
|
const external = await read("externalSessions", id, externalSessionSchema);
|
|
@@ -1139,16 +1291,36 @@ function createStores(options) {
|
|
|
1139
1291
|
async notify(input) {
|
|
1140
1292
|
const event = communicationEventSchema.parse(input);
|
|
1141
1293
|
if (event.replyTo.channel !== "api") throw new SessionConflictError("Expected an API notification");
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1294
|
+
const id = agentSessionIdSchema.parse(event.replyTo.address);
|
|
1295
|
+
await mustRead("sessions", id, storedSessionSchema);
|
|
1296
|
+
const key = derivedId("snotice", JSON.stringify([id, event.eventId]));
|
|
1297
|
+
let notification = await read("sessionNotifications", key, sessionNotificationRecordSchema);
|
|
1298
|
+
if (!notification) {
|
|
1299
|
+
try {
|
|
1300
|
+
await driver.insert({
|
|
1301
|
+
collection: "sessionNotifications",
|
|
1302
|
+
id: key,
|
|
1303
|
+
value: sessionNotificationRecordSchema.parse({
|
|
1304
|
+
id: key,
|
|
1305
|
+
sessionId: id,
|
|
1306
|
+
createdAt: now(),
|
|
1307
|
+
event
|
|
1308
|
+
})
|
|
1309
|
+
});
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
if (!(error instanceof RecordExistsError)) throw error;
|
|
1148
1312
|
}
|
|
1313
|
+
notification = await mustRead("sessionNotifications", key, sessionNotificationRecordSchema);
|
|
1314
|
+
}
|
|
1315
|
+
if (!sameStoredValue(notification.value.event, event)) throw new SessionConflictError("Notification eventId was reused");
|
|
1316
|
+
for (;;) {
|
|
1317
|
+
const record = await mustRead("sessions", id, storedSessionSchema);
|
|
1318
|
+
if (record.value.latestNotificationId === key) return;
|
|
1319
|
+
const latest = record.value.latestNotificationId ? await mustRead("sessionNotifications", record.value.latestNotificationId, sessionNotificationRecordSchema) : null;
|
|
1320
|
+
if (latest && (latest.value.createdAt > notification.value.createdAt || latest.value.createdAt === notification.value.createdAt && latest.value.id > key)) return;
|
|
1149
1321
|
const next = storedSessionSchema.parse({
|
|
1150
1322
|
...record.value,
|
|
1151
|
-
|
|
1323
|
+
latestNotificationId: key
|
|
1152
1324
|
});
|
|
1153
1325
|
try {
|
|
1154
1326
|
await updateSession(next, record.version);
|
|
@@ -1204,8 +1376,8 @@ function createStores(options) {
|
|
|
1204
1376
|
const record = await read("repositories", id, repositorySchema);
|
|
1205
1377
|
return record === null ? null : record.value;
|
|
1206
1378
|
},
|
|
1207
|
-
list() {
|
|
1208
|
-
return
|
|
1379
|
+
list(query) {
|
|
1380
|
+
return listPage(["repositories"], repositorySchema, query);
|
|
1209
1381
|
}
|
|
1210
1382
|
},
|
|
1211
1383
|
signals: {
|
|
@@ -1324,8 +1496,8 @@ function createStores(options) {
|
|
|
1324
1496
|
});
|
|
1325
1497
|
return next;
|
|
1326
1498
|
},
|
|
1327
|
-
list() {
|
|
1328
|
-
return
|
|
1499
|
+
list(query) {
|
|
1500
|
+
return listPage(["signals"], signalSchema, query);
|
|
1329
1501
|
}
|
|
1330
1502
|
},
|
|
1331
1503
|
tasks: {
|
|
@@ -1743,7 +1915,8 @@ function createStores(options) {
|
|
|
1743
1915
|
...next,
|
|
1744
1916
|
failureReceiptId: receipt.receiptId
|
|
1745
1917
|
});
|
|
1746
|
-
|
|
1918
|
+
const transitionParent = next.parentTaskId ? await stores.tasks.get(next.parentTaskId) : null;
|
|
1919
|
+
if (needsLifecycleCommunication(next, receiptState, transitionOptions.phase, transitionParent, options.includeChildTaskCommunication)) next = taskSchema.parse({
|
|
1747
1920
|
...next,
|
|
1748
1921
|
pendingCommunicationReceiptIds: [...next.pendingCommunicationReceiptIds ?? [], receipt.receiptId]
|
|
1749
1922
|
});
|
|
@@ -1828,60 +2001,10 @@ function createStores(options) {
|
|
|
1828
2001
|
}
|
|
1829
2002
|
return question;
|
|
1830
2003
|
},
|
|
1831
|
-
async
|
|
1832
|
-
const
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
if (route === null) {
|
|
1836
|
-
discovered = (await stores.tasks.list()).filter((task) => task.replyTo.channel === replyTo.channel && task.replyTo.address === replyTo.address);
|
|
1837
|
-
for (const task of discovered) {
|
|
1838
|
-
const record = await mustRead("tasks", task.id, storedTaskSchema);
|
|
1839
|
-
await projectSafely(() => projectTaskRoute(record.value, record.version));
|
|
1840
|
-
}
|
|
1841
|
-
if (discovered.length === 0) {
|
|
1842
|
-
const empty = taskRouteSchema.parse({
|
|
1843
|
-
schemaVersion: 1,
|
|
1844
|
-
id,
|
|
1845
|
-
replyTo,
|
|
1846
|
-
entries: [],
|
|
1847
|
-
updatedAt: now()
|
|
1848
|
-
});
|
|
1849
|
-
await projectSafely(async () => {
|
|
1850
|
-
try {
|
|
1851
|
-
await driver.insert({
|
|
1852
|
-
collection: "taskRoutes",
|
|
1853
|
-
id,
|
|
1854
|
-
value: empty
|
|
1855
|
-
});
|
|
1856
|
-
} catch (error) {
|
|
1857
|
-
if (!(error instanceof RecordExistsError)) throw error;
|
|
1858
|
-
}
|
|
1859
|
-
});
|
|
1860
|
-
}
|
|
1861
|
-
route = await read("taskRoutes", id, taskRouteSchema);
|
|
1862
|
-
}
|
|
1863
|
-
if (route === null) return discovered;
|
|
1864
|
-
const values = new Map(route.value.entries.map((entry) => [entry.value.id, entry.value]));
|
|
1865
|
-
await forEachConcurrent(route.value.repairs, 16, async (repair) => {
|
|
1866
|
-
const repairTaskId = taskIdSchema.parse(repair.id);
|
|
1867
|
-
const record = await driver.get("tasks", repairTaskId);
|
|
1868
|
-
if (record === null) {
|
|
1869
|
-
values.delete(repairTaskId);
|
|
1870
|
-
return;
|
|
1871
|
-
}
|
|
1872
|
-
const canonical = storedTaskSchema.parse(record.value);
|
|
1873
|
-
if (canonical.replyTo.channel === replyTo.channel && canonical.replyTo.address === replyTo.address) values.set(canonical.id, canonical);
|
|
1874
|
-
else values.delete(canonical.id);
|
|
1875
|
-
await projectSafely(() => projectTaskRoute(canonical, record.version));
|
|
1876
|
-
});
|
|
1877
|
-
const tasks = [...values.values()];
|
|
1878
|
-
for (const task of tasks) validateTaskWork(task);
|
|
1879
|
-
return tasks;
|
|
1880
|
-
},
|
|
1881
|
-
async list() {
|
|
1882
|
-
const tasks = await catalogValues("tasks", "taskCatalogs", "tasks", storedTaskSchema, taskCatalogSchema, projectTaskCatalog);
|
|
1883
|
-
for (const task of tasks) validateTaskWork(task);
|
|
1884
|
-
return tasks;
|
|
2004
|
+
async list(query) {
|
|
2005
|
+
const page = await listPage(["tasks"], storedTaskSchema, query, () => catalogValues("tasks", "taskCatalogs", "tasks", storedTaskSchema, taskCatalogSchema, projectTaskCatalog));
|
|
2006
|
+
for (const task of page.items) validateTaskWork(task);
|
|
2007
|
+
return page;
|
|
1885
2008
|
}
|
|
1886
2009
|
},
|
|
1887
2010
|
changes: {
|
|
@@ -1994,8 +2117,8 @@ function createStores(options) {
|
|
|
1994
2117
|
await updateChange(next, existing.version);
|
|
1995
2118
|
return next;
|
|
1996
2119
|
},
|
|
1997
|
-
list() {
|
|
1998
|
-
return catalogValues("changes", "changeCatalogs", "changes", changeSchema, changeCatalogSchema, projectChangeCatalog);
|
|
2120
|
+
list(query) {
|
|
2121
|
+
return listPage(["changes"], changeSchema, query, () => catalogValues("changes", "changeCatalogs", "changes", changeSchema, changeCatalogSchema, projectChangeCatalog));
|
|
1999
2122
|
}
|
|
2000
2123
|
},
|
|
2001
2124
|
receipts: {
|
|
@@ -2010,8 +2133,8 @@ function createStores(options) {
|
|
|
2010
2133
|
const record = await read("receipts", id, receiptSchema);
|
|
2011
2134
|
return record === null ? null : record.value;
|
|
2012
2135
|
},
|
|
2013
|
-
list() {
|
|
2014
|
-
return
|
|
2136
|
+
list(query) {
|
|
2137
|
+
return listPage(["receipts"], receiptSchema, query);
|
|
2015
2138
|
}
|
|
2016
2139
|
},
|
|
2017
2140
|
budgets: {
|
|
@@ -2304,6 +2427,6 @@ function createStores(options) {
|
|
|
2304
2427
|
return stores;
|
|
2305
2428
|
}
|
|
2306
2429
|
//#endregion
|
|
2307
|
-
export { AnswerEventConflictError, ApprovalError, ChangeAssociationError, ImageInputError, OutboxEventConflictError, SessionConflictError, SettlementError, TaskAttemptConflictError, createStores };
|
|
2430
|
+
export { AnswerEventConflictError, ApprovalError, ChangeAssociationError, ImageInputError, OutboxEventConflictError, SessionConflictError, SettlementError, TaskAttemptConflictError, createStoreEngine, createStores };
|
|
2308
2431
|
|
|
2309
2432
|
//# sourceMappingURL=engine.mjs.map
|