@wopr-network/defcon 1.11.0 → 1.13.0

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 (32) hide show
  1. package/dist/src/execution/cli.js +42 -5
  2. package/dist/src/litestream/cli-integration.test.d.ts +1 -0
  3. package/dist/src/litestream/cli-integration.test.js +35 -0
  4. package/dist/src/litestream/manager.d.ts +23 -0
  5. package/dist/src/litestream/manager.js +123 -0
  6. package/dist/src/litestream/manager.test.d.ts +1 -0
  7. package/dist/src/litestream/manager.test.js +46 -0
  8. package/dist/src/repositories/drizzle/domain-event.repo.d.ts +1 -0
  9. package/dist/src/repositories/drizzle/domain-event.repo.js +4 -1
  10. package/dist/src/repositories/drizzle/entity-snapshot.repo.d.ts +14 -0
  11. package/dist/src/repositories/drizzle/entity-snapshot.repo.js +68 -0
  12. package/dist/src/repositories/drizzle/index.d.ts +1 -0
  13. package/dist/src/repositories/drizzle/index.js +1 -0
  14. package/dist/src/repositories/drizzle/schema.d.ts +329 -0
  15. package/dist/src/repositories/drizzle/schema.js +23 -0
  16. package/dist/src/repositories/event-sourced/entity.repo.d.ts +29 -0
  17. package/dist/src/repositories/event-sourced/entity.repo.js +84 -0
  18. package/dist/src/repositories/event-sourced/index.d.ts +3 -0
  19. package/dist/src/repositories/event-sourced/index.js +3 -0
  20. package/dist/src/repositories/event-sourced/invocation.repo.d.ts +20 -0
  21. package/dist/src/repositories/event-sourced/invocation.repo.js +61 -0
  22. package/dist/src/repositories/event-sourced/replay.d.ts +11 -0
  23. package/dist/src/repositories/event-sourced/replay.js +135 -0
  24. package/dist/src/repositories/interfaces.d.ts +11 -0
  25. package/drizzle/0015_clean_redwing.sql +11 -0
  26. package/drizzle/0016_hesitant_bill_hollister.sql +22 -0
  27. package/drizzle/0017_hesitant_bill_hollister.sql +22 -0
  28. package/drizzle/0018_volatile_reavers.sql +1 -0
  29. package/drizzle/meta/0016_snapshot.json +1316 -0
  30. package/drizzle/meta/0018_snapshot.json +1351 -0
  31. package/drizzle/meta/_journal.json +15 -1
  32. package/package.json +1 -1
@@ -2149,3 +2149,332 @@ export declare const domainEvents: import("drizzle-orm/sqlite-core").SQLiteTable
2149
2149
  };
2150
2150
  dialect: "sqlite";
2151
2151
  }>;
2152
+ export declare const entitySnapshots: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2153
+ name: "entity_snapshots";
2154
+ schema: undefined;
2155
+ columns: {
2156
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2157
+ name: "id";
2158
+ tableName: "entity_snapshots";
2159
+ dataType: "string";
2160
+ columnType: "SQLiteText";
2161
+ data: string;
2162
+ driverParam: string;
2163
+ notNull: true;
2164
+ hasDefault: false;
2165
+ isPrimaryKey: true;
2166
+ isAutoincrement: false;
2167
+ hasRuntimeDefault: false;
2168
+ enumValues: [string, ...string[]];
2169
+ baseColumn: never;
2170
+ identity: undefined;
2171
+ generated: undefined;
2172
+ }, {}, {
2173
+ length: number | undefined;
2174
+ }>;
2175
+ entityId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2176
+ name: "entity_id";
2177
+ tableName: "entity_snapshots";
2178
+ dataType: "string";
2179
+ columnType: "SQLiteText";
2180
+ data: string;
2181
+ driverParam: string;
2182
+ notNull: true;
2183
+ hasDefault: false;
2184
+ isPrimaryKey: false;
2185
+ isAutoincrement: false;
2186
+ hasRuntimeDefault: false;
2187
+ enumValues: [string, ...string[]];
2188
+ baseColumn: never;
2189
+ identity: undefined;
2190
+ generated: undefined;
2191
+ }, {}, {
2192
+ length: number | undefined;
2193
+ }>;
2194
+ sequence: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2195
+ name: "sequence";
2196
+ tableName: "entity_snapshots";
2197
+ dataType: "number";
2198
+ columnType: "SQLiteInteger";
2199
+ data: number;
2200
+ driverParam: number;
2201
+ notNull: true;
2202
+ hasDefault: false;
2203
+ isPrimaryKey: false;
2204
+ isAutoincrement: false;
2205
+ hasRuntimeDefault: false;
2206
+ enumValues: undefined;
2207
+ baseColumn: never;
2208
+ identity: undefined;
2209
+ generated: undefined;
2210
+ }, {}, {}>;
2211
+ state: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2212
+ name: "state";
2213
+ tableName: "entity_snapshots";
2214
+ dataType: "string";
2215
+ columnType: "SQLiteText";
2216
+ data: string;
2217
+ driverParam: string;
2218
+ notNull: true;
2219
+ hasDefault: false;
2220
+ isPrimaryKey: false;
2221
+ isAutoincrement: false;
2222
+ hasRuntimeDefault: false;
2223
+ enumValues: [string, ...string[]];
2224
+ baseColumn: never;
2225
+ identity: undefined;
2226
+ generated: undefined;
2227
+ }, {}, {
2228
+ length: number | undefined;
2229
+ }>;
2230
+ flowId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2231
+ name: "flow_id";
2232
+ tableName: "entity_snapshots";
2233
+ dataType: "string";
2234
+ columnType: "SQLiteText";
2235
+ data: string;
2236
+ driverParam: string;
2237
+ notNull: true;
2238
+ hasDefault: false;
2239
+ isPrimaryKey: false;
2240
+ isAutoincrement: false;
2241
+ hasRuntimeDefault: false;
2242
+ enumValues: [string, ...string[]];
2243
+ baseColumn: never;
2244
+ identity: undefined;
2245
+ generated: undefined;
2246
+ }, {}, {
2247
+ length: number | undefined;
2248
+ }>;
2249
+ refs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2250
+ name: "refs";
2251
+ tableName: "entity_snapshots";
2252
+ dataType: "json";
2253
+ columnType: "SQLiteTextJson";
2254
+ data: unknown;
2255
+ driverParam: string;
2256
+ notNull: false;
2257
+ hasDefault: false;
2258
+ isPrimaryKey: false;
2259
+ isAutoincrement: false;
2260
+ hasRuntimeDefault: false;
2261
+ enumValues: undefined;
2262
+ baseColumn: never;
2263
+ identity: undefined;
2264
+ generated: undefined;
2265
+ }, {}, {}>;
2266
+ artifacts: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2267
+ name: "artifacts";
2268
+ tableName: "entity_snapshots";
2269
+ dataType: "json";
2270
+ columnType: "SQLiteTextJson";
2271
+ data: unknown;
2272
+ driverParam: string;
2273
+ notNull: false;
2274
+ hasDefault: false;
2275
+ isPrimaryKey: false;
2276
+ isAutoincrement: false;
2277
+ hasRuntimeDefault: false;
2278
+ enumValues: undefined;
2279
+ baseColumn: never;
2280
+ identity: undefined;
2281
+ generated: undefined;
2282
+ }, {}, {}>;
2283
+ claimedBy: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2284
+ name: "claimed_by";
2285
+ tableName: "entity_snapshots";
2286
+ dataType: "string";
2287
+ columnType: "SQLiteText";
2288
+ data: string;
2289
+ driverParam: string;
2290
+ notNull: false;
2291
+ hasDefault: false;
2292
+ isPrimaryKey: false;
2293
+ isAutoincrement: false;
2294
+ hasRuntimeDefault: false;
2295
+ enumValues: [string, ...string[]];
2296
+ baseColumn: never;
2297
+ identity: undefined;
2298
+ generated: undefined;
2299
+ }, {}, {
2300
+ length: number | undefined;
2301
+ }>;
2302
+ claimedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2303
+ name: "claimed_at";
2304
+ tableName: "entity_snapshots";
2305
+ dataType: "number";
2306
+ columnType: "SQLiteInteger";
2307
+ data: number;
2308
+ driverParam: number;
2309
+ notNull: false;
2310
+ hasDefault: false;
2311
+ isPrimaryKey: false;
2312
+ isAutoincrement: false;
2313
+ hasRuntimeDefault: false;
2314
+ enumValues: undefined;
2315
+ baseColumn: never;
2316
+ identity: undefined;
2317
+ generated: undefined;
2318
+ }, {}, {}>;
2319
+ flowVersion: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2320
+ name: "flow_version";
2321
+ tableName: "entity_snapshots";
2322
+ dataType: "number";
2323
+ columnType: "SQLiteInteger";
2324
+ data: number;
2325
+ driverParam: number;
2326
+ notNull: false;
2327
+ hasDefault: false;
2328
+ isPrimaryKey: false;
2329
+ isAutoincrement: false;
2330
+ hasRuntimeDefault: false;
2331
+ enumValues: undefined;
2332
+ baseColumn: never;
2333
+ identity: undefined;
2334
+ generated: undefined;
2335
+ }, {}, {}>;
2336
+ priority: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2337
+ name: "priority";
2338
+ tableName: "entity_snapshots";
2339
+ dataType: "number";
2340
+ columnType: "SQLiteInteger";
2341
+ data: number;
2342
+ driverParam: number;
2343
+ notNull: false;
2344
+ hasDefault: true;
2345
+ isPrimaryKey: false;
2346
+ isAutoincrement: false;
2347
+ hasRuntimeDefault: false;
2348
+ enumValues: undefined;
2349
+ baseColumn: never;
2350
+ identity: undefined;
2351
+ generated: undefined;
2352
+ }, {}, {}>;
2353
+ affinityWorkerId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2354
+ name: "affinity_worker_id";
2355
+ tableName: "entity_snapshots";
2356
+ dataType: "string";
2357
+ columnType: "SQLiteText";
2358
+ data: string;
2359
+ driverParam: string;
2360
+ notNull: false;
2361
+ hasDefault: false;
2362
+ isPrimaryKey: false;
2363
+ isAutoincrement: false;
2364
+ hasRuntimeDefault: false;
2365
+ enumValues: [string, ...string[]];
2366
+ baseColumn: never;
2367
+ identity: undefined;
2368
+ generated: undefined;
2369
+ }, {}, {
2370
+ length: number | undefined;
2371
+ }>;
2372
+ affinityRole: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2373
+ name: "affinity_role";
2374
+ tableName: "entity_snapshots";
2375
+ dataType: "string";
2376
+ columnType: "SQLiteText";
2377
+ data: string;
2378
+ driverParam: string;
2379
+ notNull: false;
2380
+ hasDefault: false;
2381
+ isPrimaryKey: false;
2382
+ isAutoincrement: false;
2383
+ hasRuntimeDefault: false;
2384
+ enumValues: [string, ...string[]];
2385
+ baseColumn: never;
2386
+ identity: undefined;
2387
+ generated: undefined;
2388
+ }, {}, {
2389
+ length: number | undefined;
2390
+ }>;
2391
+ affinityExpiresAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2392
+ name: "affinity_expires_at";
2393
+ tableName: "entity_snapshots";
2394
+ dataType: "number";
2395
+ columnType: "SQLiteInteger";
2396
+ data: number;
2397
+ driverParam: number;
2398
+ notNull: false;
2399
+ hasDefault: false;
2400
+ isPrimaryKey: false;
2401
+ isAutoincrement: false;
2402
+ hasRuntimeDefault: false;
2403
+ enumValues: undefined;
2404
+ baseColumn: never;
2405
+ identity: undefined;
2406
+ generated: undefined;
2407
+ }, {}, {}>;
2408
+ createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2409
+ name: "created_at";
2410
+ tableName: "entity_snapshots";
2411
+ dataType: "number";
2412
+ columnType: "SQLiteInteger";
2413
+ data: number;
2414
+ driverParam: number;
2415
+ notNull: false;
2416
+ hasDefault: false;
2417
+ isPrimaryKey: false;
2418
+ isAutoincrement: false;
2419
+ hasRuntimeDefault: false;
2420
+ enumValues: undefined;
2421
+ baseColumn: never;
2422
+ identity: undefined;
2423
+ generated: undefined;
2424
+ }, {}, {}>;
2425
+ updatedAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2426
+ name: "updated_at";
2427
+ tableName: "entity_snapshots";
2428
+ dataType: "number";
2429
+ columnType: "SQLiteInteger";
2430
+ data: number;
2431
+ driverParam: number;
2432
+ notNull: false;
2433
+ hasDefault: false;
2434
+ isPrimaryKey: false;
2435
+ isAutoincrement: false;
2436
+ hasRuntimeDefault: false;
2437
+ enumValues: undefined;
2438
+ baseColumn: never;
2439
+ identity: undefined;
2440
+ generated: undefined;
2441
+ }, {}, {}>;
2442
+ snapshotAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2443
+ name: "snapshot_at";
2444
+ tableName: "entity_snapshots";
2445
+ dataType: "number";
2446
+ columnType: "SQLiteInteger";
2447
+ data: number;
2448
+ driverParam: number;
2449
+ notNull: true;
2450
+ hasDefault: false;
2451
+ isPrimaryKey: false;
2452
+ isAutoincrement: false;
2453
+ hasRuntimeDefault: false;
2454
+ enumValues: undefined;
2455
+ baseColumn: never;
2456
+ identity: undefined;
2457
+ generated: undefined;
2458
+ }, {}, {}>;
2459
+ parentEntityId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
2460
+ name: "parent_entity_id";
2461
+ tableName: "entity_snapshots";
2462
+ dataType: "string";
2463
+ columnType: "SQLiteText";
2464
+ data: string;
2465
+ driverParam: string;
2466
+ notNull: false;
2467
+ hasDefault: false;
2468
+ isPrimaryKey: false;
2469
+ isAutoincrement: false;
2470
+ hasRuntimeDefault: false;
2471
+ enumValues: [string, ...string[]];
2472
+ baseColumn: never;
2473
+ identity: undefined;
2474
+ generated: undefined;
2475
+ }, {}, {
2476
+ length: number | undefined;
2477
+ }>;
2478
+ };
2479
+ dialect: "sqlite";
2480
+ }>;
@@ -173,3 +173,26 @@ export const domainEvents = sqliteTable("domain_events", {
173
173
  entitySeqIdx: uniqueIndex("domain_events_entity_seq_idx").on(table.entityId, table.sequence),
174
174
  typeIdx: index("domain_events_type_idx").on(table.type, table.emittedAt),
175
175
  }));
176
+ export const entitySnapshots = sqliteTable("entity_snapshots", {
177
+ id: text("id").primaryKey(),
178
+ entityId: text("entity_id").notNull(),
179
+ sequence: integer("sequence").notNull(),
180
+ state: text("state").notNull(),
181
+ flowId: text("flow_id").notNull(),
182
+ refs: text("refs", { mode: "json" }),
183
+ artifacts: text("artifacts", { mode: "json" }),
184
+ claimedBy: text("claimed_by"),
185
+ claimedAt: integer("claimed_at"),
186
+ flowVersion: integer("flow_version"),
187
+ priority: integer("priority").default(0),
188
+ affinityWorkerId: text("affinity_worker_id"),
189
+ affinityRole: text("affinity_role"),
190
+ affinityExpiresAt: integer("affinity_expires_at"),
191
+ createdAt: integer("created_at"),
192
+ updatedAt: integer("updated_at"),
193
+ snapshotAt: integer("snapshot_at").notNull(),
194
+ parentEntityId: text("parent_entity_id"),
195
+ }, (table) => ({
196
+ entitySeqUnique: uniqueIndex("entity_snapshots_entity_seq_idx").on(table.entityId, table.sequence),
197
+ entityLatestIdx: index("entity_snapshots_entity_latest_idx").on(table.entityId, table.snapshotAt),
198
+ }));
@@ -0,0 +1,29 @@
1
+ import type { Artifacts, Entity, IDomainEventRepository, IEntityRepository, IEntitySnapshotRepository, Refs } from "../interfaces.js";
2
+ export declare class EventSourcedEntityRepository implements IEntityRepository {
3
+ private readonly mutable;
4
+ private readonly domainEvents;
5
+ private readonly snapshots;
6
+ private readonly snapshotInterval;
7
+ constructor(mutable: IEntityRepository, domainEvents: IDomainEventRepository, snapshots: IEntitySnapshotRepository, snapshotInterval?: number);
8
+ create(flowId: string, initialState: string, refs?: Refs, flowVersion?: number): Promise<Entity>;
9
+ get(id: string): Promise<Entity | null>;
10
+ findByFlowAndState(flowId: string, state: string, limit?: number): Promise<Entity[]>;
11
+ hasAnyInFlowAndState(flowId: string, stateNames: string[]): Promise<boolean>;
12
+ transition(id: string, toState: string, trigger: string, artifacts?: Partial<Artifacts>): Promise<Entity>;
13
+ updateArtifacts(id: string, artifacts: Partial<Artifacts>): Promise<void>;
14
+ claim(flowId: string, state: string, agentId: string): Promise<Entity | null>;
15
+ claimById(entityId: string, agentId: string): Promise<Entity | null>;
16
+ release(entityId: string, agentId: string): Promise<void>;
17
+ reapExpired(ttlMs: number): Promise<string[]>;
18
+ setAffinity(entityId: string, workerId: string, role: string, expiresAt: Date): Promise<void>;
19
+ clearExpiredAffinity(): Promise<string[]>;
20
+ appendSpawnedChild(parentId: string, entry: {
21
+ childId: string;
22
+ childFlow: string;
23
+ spawnedAt: string;
24
+ }): Promise<void>;
25
+ findByParentId(parentEntityId: string): Promise<Entity[]>;
26
+ cancelEntity(entityId: string): Promise<void>;
27
+ resetEntity(entityId: string, targetState: string): Promise<Entity>;
28
+ updateFlowVersion(entityId: string, version: number): Promise<void>;
29
+ }
@@ -0,0 +1,84 @@
1
+ import { replayEntity } from "./replay.js";
2
+ const DEFAULT_SNAPSHOT_INTERVAL = 10;
3
+ export class EventSourcedEntityRepository {
4
+ mutable;
5
+ domainEvents;
6
+ snapshots;
7
+ snapshotInterval;
8
+ constructor(mutable, domainEvents, snapshots, snapshotInterval) {
9
+ this.mutable = mutable;
10
+ this.domainEvents = domainEvents;
11
+ this.snapshots = snapshots;
12
+ this.snapshotInterval = snapshotInterval ?? DEFAULT_SNAPSHOT_INTERVAL;
13
+ }
14
+ async create(flowId, initialState, refs, flowVersion) {
15
+ return this.mutable.create(flowId, initialState, refs, flowVersion);
16
+ }
17
+ async get(id) {
18
+ const snapshot = await this.snapshots.loadLatest(id);
19
+ const afterSeq = snapshot?.sequence ?? 0;
20
+ const eventsAfterSnapshot = await this.domainEvents.list(id, { minSequence: afterSeq, limit: 10000 });
21
+ if (eventsAfterSnapshot.length === 0 && !snapshot) {
22
+ return this.mutable.get(id);
23
+ }
24
+ const entity = replayEntity(snapshot?.state ?? null, eventsAfterSnapshot, id);
25
+ if (!entity) {
26
+ return this.mutable.get(id);
27
+ }
28
+ if (eventsAfterSnapshot.length >= this.snapshotInterval) {
29
+ const lastEvent = eventsAfterSnapshot[eventsAfterSnapshot.length - 1];
30
+ try {
31
+ await this.snapshots.save(id, lastEvent.sequence, entity);
32
+ }
33
+ catch {
34
+ // snapshot write failure is non-fatal — continue with in-memory entity
35
+ }
36
+ }
37
+ return entity;
38
+ }
39
+ async findByFlowAndState(flowId, state, limit) {
40
+ return this.mutable.findByFlowAndState(flowId, state, limit);
41
+ }
42
+ async hasAnyInFlowAndState(flowId, stateNames) {
43
+ return this.mutable.hasAnyInFlowAndState(flowId, stateNames);
44
+ }
45
+ async transition(id, toState, trigger, artifacts) {
46
+ return this.mutable.transition(id, toState, trigger, artifacts);
47
+ }
48
+ async updateArtifacts(id, artifacts) {
49
+ return this.mutable.updateArtifacts(id, artifacts);
50
+ }
51
+ async claim(flowId, state, agentId) {
52
+ return this.mutable.claim(flowId, state, agentId);
53
+ }
54
+ async claimById(entityId, agentId) {
55
+ return this.mutable.claimById(entityId, agentId);
56
+ }
57
+ async release(entityId, agentId) {
58
+ return this.mutable.release(entityId, agentId);
59
+ }
60
+ async reapExpired(ttlMs) {
61
+ return this.mutable.reapExpired(ttlMs);
62
+ }
63
+ async setAffinity(entityId, workerId, role, expiresAt) {
64
+ return this.mutable.setAffinity(entityId, workerId, role, expiresAt);
65
+ }
66
+ async clearExpiredAffinity() {
67
+ return this.mutable.clearExpiredAffinity();
68
+ }
69
+ async appendSpawnedChild(parentId, entry) {
70
+ return this.mutable.appendSpawnedChild(parentId, entry);
71
+ }
72
+ async findByParentId(parentEntityId) {
73
+ return this.mutable.findByParentId(parentEntityId);
74
+ }
75
+ async cancelEntity(entityId) {
76
+ return this.mutable.cancelEntity(entityId);
77
+ }
78
+ async resetEntity(entityId, targetState) {
79
+ return this.mutable.resetEntity(entityId, targetState);
80
+ }
81
+ async updateFlowVersion(entityId, version) {
82
+ return this.mutable.updateFlowVersion(entityId, version);
83
+ }
84
+ }
@@ -0,0 +1,3 @@
1
+ export { EventSourcedEntityRepository } from "./entity.repo.js";
2
+ export { EventSourcedInvocationRepository } from "./invocation.repo.js";
3
+ export { replayEntity, replayInvocation } from "./replay.js";
@@ -0,0 +1,3 @@
1
+ export { EventSourcedEntityRepository } from "./entity.repo.js";
2
+ export { EventSourcedInvocationRepository } from "./invocation.repo.js";
3
+ export { replayEntity, replayInvocation } from "./replay.js";
@@ -0,0 +1,20 @@
1
+ import type { Artifacts, IDomainEventRepository, IInvocationRepository, Invocation, Mode } from "../interfaces.js";
2
+ export declare class EventSourcedInvocationRepository implements IInvocationRepository {
3
+ private readonly mutable;
4
+ private readonly domainEvents;
5
+ constructor(mutable: IInvocationRepository, domainEvents: IDomainEventRepository);
6
+ create(entityId: string, stage: string, prompt: string, mode: Mode, ttlMs: number | undefined, context: Record<string, unknown> | undefined, agentRole: string | null): Promise<Invocation>;
7
+ get(id: string): Promise<Invocation | null>;
8
+ claim(invocationId: string, agentId: string): Promise<Invocation | null>;
9
+ complete(id: string, signal: string, artifacts?: Artifacts): Promise<Invocation>;
10
+ fail(id: string, error: string): Promise<Invocation>;
11
+ releaseClaim(id: string): Promise<void>;
12
+ findByEntity(entityId: string): Promise<Invocation[]>;
13
+ findUnclaimedWithAffinity(flowId: string, role: string, workerId: string): Promise<Invocation[]>;
14
+ findUnclaimedByFlow(flowId: string): Promise<Invocation[]>;
15
+ findByFlow(flowId: string): Promise<Invocation[]>;
16
+ reapExpired(): Promise<Invocation[]>;
17
+ findUnclaimedActive(flowId?: string): Promise<Invocation[]>;
18
+ countActiveByFlow(flowId: string): Promise<number>;
19
+ countPendingByFlow(flowId: string): Promise<number>;
20
+ }
@@ -0,0 +1,61 @@
1
+ import { replayInvocation } from "./replay.js";
2
+ export class EventSourcedInvocationRepository {
3
+ mutable;
4
+ domainEvents;
5
+ constructor(mutable, domainEvents) {
6
+ this.mutable = mutable;
7
+ this.domainEvents = domainEvents;
8
+ }
9
+ async create(entityId, stage, prompt, mode, ttlMs, context, agentRole) {
10
+ return this.mutable.create(entityId, stage, prompt, mode, ttlMs, context, agentRole);
11
+ }
12
+ async get(id) {
13
+ const mutableInv = await this.mutable.get(id);
14
+ if (!mutableInv)
15
+ return null;
16
+ const events = await this.domainEvents.list(mutableInv.entityId, { limit: 10000 });
17
+ const invocationEvents = events.filter((e) => {
18
+ const p = e.payload;
19
+ return p.invocationId === id;
20
+ });
21
+ if (invocationEvents.length === 0)
22
+ return mutableInv;
23
+ return replayInvocation(id, invocationEvents) ?? mutableInv;
24
+ }
25
+ async claim(invocationId, agentId) {
26
+ return this.mutable.claim(invocationId, agentId);
27
+ }
28
+ async complete(id, signal, artifacts) {
29
+ return this.mutable.complete(id, signal, artifacts);
30
+ }
31
+ async fail(id, error) {
32
+ return this.mutable.fail(id, error);
33
+ }
34
+ async releaseClaim(id) {
35
+ return this.mutable.releaseClaim(id);
36
+ }
37
+ async findByEntity(entityId) {
38
+ return this.mutable.findByEntity(entityId);
39
+ }
40
+ async findUnclaimedWithAffinity(flowId, role, workerId) {
41
+ return this.mutable.findUnclaimedWithAffinity(flowId, role, workerId);
42
+ }
43
+ async findUnclaimedByFlow(flowId) {
44
+ return this.mutable.findUnclaimedByFlow(flowId);
45
+ }
46
+ async findByFlow(flowId) {
47
+ return this.mutable.findByFlow(flowId);
48
+ }
49
+ async reapExpired() {
50
+ return this.mutable.reapExpired();
51
+ }
52
+ async findUnclaimedActive(flowId) {
53
+ return this.mutable.findUnclaimedActive(flowId);
54
+ }
55
+ async countActiveByFlow(flowId) {
56
+ return this.mutable.countActiveByFlow(flowId);
57
+ }
58
+ async countPendingByFlow(flowId) {
59
+ return this.mutable.countPendingByFlow(flowId);
60
+ }
61
+ }
@@ -0,0 +1,11 @@
1
+ import type { DomainEvent, Entity, Invocation } from "../interfaces.js";
2
+ /**
3
+ * Replay entity state from a snapshot (or null) plus subsequent domain events.
4
+ * Events must be ordered by sequence ascending.
5
+ */
6
+ export declare function replayEntity(snapshot: Entity | null, events: DomainEvent[], entityId: string): Entity | null;
7
+ /**
8
+ * Replay a single invocation's state from domain events.
9
+ * Filters events by invocationId in the payload.
10
+ */
11
+ export declare function replayInvocation(invocationId: string, events: DomainEvent[]): Invocation | null;