@bratsos/workflow-engine 0.1.0 → 0.2.1

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 (37) hide show
  1. package/README.md +274 -513
  2. package/dist/{chunk-7IITBLFY.js → chunk-NYKMT46J.js} +268 -25
  3. package/dist/chunk-NYKMT46J.js.map +1 -0
  4. package/dist/chunk-SPXBCZLB.js +17 -0
  5. package/dist/chunk-SPXBCZLB.js.map +1 -0
  6. package/dist/chunk-WZ533CPU.js +1108 -0
  7. package/dist/chunk-WZ533CPU.js.map +1 -0
  8. package/dist/{client-5vz5Vv4A.d.ts → client-D4PoxADF.d.ts} +3 -143
  9. package/dist/client.d.ts +3 -2
  10. package/dist/{index-DmR3E8D7.d.ts → index-DAzCfO1R.d.ts} +20 -1
  11. package/dist/index.d.ts +234 -601
  12. package/dist/index.js +46 -2034
  13. package/dist/index.js.map +1 -1
  14. package/dist/{interface-Cv22wvLG.d.ts → interface-MMqhfQQK.d.ts} +69 -2
  15. package/dist/kernel/index.d.ts +26 -0
  16. package/dist/kernel/index.js +3 -0
  17. package/dist/kernel/index.js.map +1 -0
  18. package/dist/kernel/testing/index.d.ts +44 -0
  19. package/dist/kernel/testing/index.js +85 -0
  20. package/dist/kernel/testing/index.js.map +1 -0
  21. package/dist/persistence/index.d.ts +2 -2
  22. package/dist/persistence/index.js +2 -1
  23. package/dist/persistence/prisma/index.d.ts +2 -2
  24. package/dist/persistence/prisma/index.js +2 -1
  25. package/dist/plugins-CPC-X0rR.d.ts +421 -0
  26. package/dist/ports-tU3rzPXJ.d.ts +245 -0
  27. package/dist/stage-BPw7m9Wx.d.ts +144 -0
  28. package/dist/testing/index.d.ts +23 -1
  29. package/dist/testing/index.js +156 -13
  30. package/dist/testing/index.js.map +1 -1
  31. package/package.json +11 -1
  32. package/skills/workflow-engine/SKILL.md +234 -348
  33. package/skills/workflow-engine/references/03-runtime-setup.md +111 -426
  34. package/skills/workflow-engine/references/05-persistence-setup.md +32 -0
  35. package/skills/workflow-engine/references/07-testing-patterns.md +141 -474
  36. package/skills/workflow-engine/references/08-common-patterns.md +125 -428
  37. package/dist/chunk-7IITBLFY.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import { createLogger } from './chunk-MUWP5SF2.js';
2
+ import { StaleVersionError } from './chunk-SPXBCZLB.js';
2
3
 
3
4
  // src/persistence/prisma/ai-logger.ts
4
5
  var logger = createLogger("AICallLogger");
@@ -158,7 +159,10 @@ var PrismaJobQueue = class {
158
159
  workflowRunId: options.workflowRunId,
159
160
  stageId: options.stageId,
160
161
  priority: options.priority ?? 5,
161
- payload: options.payload || {},
162
+ payload: {
163
+ ...options.payload,
164
+ _workflowId: options.workflowId
165
+ },
162
166
  status: this.enums.status("PENDING"),
163
167
  nextPollAt: options.scheduledFor
164
168
  }
@@ -180,7 +184,7 @@ var PrismaJobQueue = class {
180
184
  workflowRunId: job.workflowRunId,
181
185
  stageId: job.stageId,
182
186
  priority: job.priority ?? 5,
183
- payload: job.payload || {},
187
+ payload: { ...job.payload, _workflowId: job.workflowId },
184
188
  status: this.enums.status("PENDING")
185
189
  }
186
190
  })
@@ -219,7 +223,7 @@ var PrismaJobQueue = class {
219
223
  LIMIT 1
220
224
  FOR UPDATE SKIP LOCKED
221
225
  )
222
- RETURNING id, "workflowRunId", "stageId", priority, attempt, payload
226
+ RETURNING id, "workflowRunId", "stageId", priority, attempt, "maxAttempts", payload
223
227
  `;
224
228
  if (result.length === 0) {
225
229
  return null;
@@ -228,13 +232,17 @@ var PrismaJobQueue = class {
228
232
  logger2.debug(
229
233
  `Dequeued job ${job.id} (stage: ${job.stageId}, attempt: ${job.attempt})`
230
234
  );
235
+ const payload = job.payload;
236
+ const { _workflowId, ...rest } = payload;
231
237
  return {
232
238
  jobId: job.id,
233
239
  workflowRunId: job.workflowRunId,
240
+ workflowId: _workflowId ?? "",
234
241
  stageId: job.stageId,
235
242
  priority: job.priority,
236
243
  attempt: job.attempt,
237
- payload: job.payload
244
+ maxAttempts: job.maxAttempts,
245
+ payload: rest
238
246
  };
239
247
  } catch (error) {
240
248
  logger2.error("Error dequeuing job:", error);
@@ -287,13 +295,17 @@ var PrismaJobQueue = class {
287
295
  logger2.debug(
288
296
  `Dequeued job ${claimedJob.id} (stage: ${claimedJob.stageId}, attempt: ${claimedJob.attempt})`
289
297
  );
298
+ const claimedPayload = claimedJob.payload;
299
+ const { _workflowId: claimedWfId, ...claimedRest } = claimedPayload;
290
300
  return {
291
301
  jobId: claimedJob.id,
292
302
  workflowRunId: claimedJob.workflowRunId,
303
+ workflowId: claimedWfId ?? "",
293
304
  stageId: claimedJob.stageId,
294
305
  priority: claimedJob.priority,
295
306
  attempt: claimedJob.attempt,
296
- payload: claimedJob.payload
307
+ maxAttempts: claimedJob.maxAttempts,
308
+ payload: claimedRest
297
309
  };
298
310
  } catch (error) {
299
311
  logger2.error("Error dequeuing job:", error);
@@ -415,7 +427,14 @@ function createPrismaJobQueue(prisma, optionsOrWorkerId) {
415
427
  }
416
428
 
417
429
  // src/persistence/prisma/persistence.ts
418
- var PrismaWorkflowPersistence = class {
430
+ var IDEMPOTENCY_IN_PROGRESS_MARKER = {
431
+ __workflowEngineState: "in_progress"
432
+ };
433
+ function isInProgressResult(result) {
434
+ if (!result || typeof result !== "object") return false;
435
+ return result.__workflowEngineState === "in_progress";
436
+ }
437
+ var PrismaWorkflowPersistence = class _PrismaWorkflowPersistence {
419
438
  constructor(prisma, options = {}) {
420
439
  this.prisma = prisma;
421
440
  this.enums = createEnumHelper(prisma);
@@ -423,6 +442,17 @@ var PrismaWorkflowPersistence = class {
423
442
  }
424
443
  enums;
425
444
  databaseType;
445
+ async withTransaction(fn) {
446
+ if (typeof this.prisma.$transaction !== "function") {
447
+ return fn(this);
448
+ }
449
+ return this.prisma.$transaction(async (tx) => {
450
+ const txPersistence = new _PrismaWorkflowPersistence(tx, {
451
+ databaseType: this.databaseType
452
+ });
453
+ return fn(txPersistence);
454
+ });
455
+ }
426
456
  // ============================================================================
427
457
  // WorkflowRun Operations
428
458
  // ============================================================================
@@ -443,18 +473,33 @@ var PrismaWorkflowPersistence = class {
443
473
  return this.mapWorkflowRun(run);
444
474
  }
445
475
  async updateRun(id, data) {
446
- await this.prisma.workflowRun.update({
447
- where: { id },
476
+ const updateData = this.buildRunUpdateData(data);
477
+ if (data.expectedVersion === void 0) {
478
+ await this.prisma.workflowRun.update({
479
+ where: { id },
480
+ data: updateData
481
+ });
482
+ return;
483
+ }
484
+ const result = await this.prisma.workflowRun.updateMany({
485
+ where: { id, version: data.expectedVersion },
448
486
  data: {
449
- status: data.status ? this.enums.status(data.status) : void 0,
450
- startedAt: data.startedAt,
451
- completedAt: data.completedAt,
452
- duration: data.duration,
453
- output: data.output,
454
- totalCost: data.totalCost,
455
- totalTokens: data.totalTokens
487
+ ...updateData,
488
+ version: { increment: 1 }
456
489
  }
457
490
  });
491
+ if (result.count === 0) {
492
+ const current = await this.prisma.workflowRun.findUnique({
493
+ where: { id },
494
+ select: { version: true }
495
+ });
496
+ throw new StaleVersionError(
497
+ "WorkflowRun",
498
+ id,
499
+ data.expectedVersion,
500
+ current?.version ?? -1
501
+ );
502
+ }
458
503
  }
459
504
  async getRun(id) {
460
505
  const run = await this.prisma.workflowRun.findUnique({ where: { id } });
@@ -605,18 +650,79 @@ var PrismaWorkflowPersistence = class {
605
650
  return this.mapWorkflowStage(stage);
606
651
  }
607
652
  async updateStage(id, data) {
608
- await this.prisma.workflowStage.update({
609
- where: { id },
610
- data: this.buildStageUpdateData(data)
653
+ const updateData = this.buildStageUpdateData(data);
654
+ if (data.expectedVersion === void 0) {
655
+ await this.prisma.workflowStage.update({
656
+ where: { id },
657
+ data: updateData
658
+ });
659
+ return;
660
+ }
661
+ const result = await this.prisma.workflowStage.updateMany({
662
+ where: { id, version: data.expectedVersion },
663
+ data: {
664
+ ...updateData,
665
+ version: { increment: 1 }
666
+ }
611
667
  });
668
+ if (result.count === 0) {
669
+ const current = await this.prisma.workflowStage.findUnique({
670
+ where: { id },
671
+ select: { version: true }
672
+ });
673
+ throw new StaleVersionError(
674
+ "WorkflowStage",
675
+ id,
676
+ data.expectedVersion,
677
+ current?.version ?? -1
678
+ );
679
+ }
612
680
  }
613
681
  async updateStageByRunAndStageId(workflowRunId, stageId, data) {
614
- await this.prisma.workflowStage.update({
682
+ const updateData = this.buildStageUpdateData(data);
683
+ if (data.expectedVersion === void 0) {
684
+ await this.prisma.workflowStage.update({
685
+ where: {
686
+ workflowRunId_stageId: { workflowRunId, stageId }
687
+ },
688
+ data: updateData
689
+ });
690
+ return;
691
+ }
692
+ const result = await this.prisma.workflowStage.updateMany({
615
693
  where: {
616
- workflowRunId_stageId: { workflowRunId, stageId }
694
+ workflowRunId,
695
+ stageId,
696
+ version: data.expectedVersion
617
697
  },
618
- data: this.buildStageUpdateData(data)
698
+ data: {
699
+ ...updateData,
700
+ version: { increment: 1 }
701
+ }
619
702
  });
703
+ if (result.count === 0) {
704
+ const current = await this.prisma.workflowStage.findFirst({
705
+ where: { workflowRunId, stageId },
706
+ select: { id: true, version: true }
707
+ });
708
+ throw new StaleVersionError(
709
+ "WorkflowStage",
710
+ current?.id ?? `${workflowRunId}/${stageId}`,
711
+ data.expectedVersion,
712
+ current?.version ?? -1
713
+ );
714
+ }
715
+ }
716
+ buildRunUpdateData(data) {
717
+ return {
718
+ status: data.status ? this.enums.status(data.status) : void 0,
719
+ startedAt: data.startedAt,
720
+ completedAt: data.completedAt,
721
+ duration: data.duration,
722
+ output: data.output,
723
+ totalCost: data.totalCost,
724
+ totalTokens: data.totalTokens
725
+ };
620
726
  }
621
727
  buildStageUpdateData(data) {
622
728
  return {
@@ -827,6 +933,127 @@ var PrismaWorkflowPersistence = class {
827
933
  return key;
828
934
  }
829
935
  // ============================================================================
936
+ // Outbox Operations
937
+ // ============================================================================
938
+ async appendOutboxEvents(events) {
939
+ if (events.length === 0) return;
940
+ const byRun = /* @__PURE__ */ new Map();
941
+ for (const event of events) {
942
+ const list = byRun.get(event.workflowRunId) ?? [];
943
+ list.push(event);
944
+ byRun.set(event.workflowRunId, list);
945
+ }
946
+ const rows = [];
947
+ for (const [workflowRunId, runEvents] of byRun) {
948
+ if (this.databaseType === "postgresql" && typeof this.prisma.$executeRaw === "function") {
949
+ await this.prisma.$executeRaw`
950
+ SELECT pg_advisory_xact_lock(hashtext(${workflowRunId}))
951
+ `;
952
+ }
953
+ const maxResult = await this.prisma.outboxEvent.aggregate({
954
+ where: { workflowRunId },
955
+ _max: { sequence: true }
956
+ });
957
+ let seq = maxResult._max.sequence ?? 0;
958
+ for (const event of runEvents) {
959
+ seq++;
960
+ rows.push({
961
+ workflowRunId: event.workflowRunId,
962
+ sequence: seq,
963
+ eventType: event.eventType,
964
+ payload: event.payload,
965
+ causationId: event.causationId,
966
+ occurredAt: event.occurredAt
967
+ });
968
+ }
969
+ }
970
+ await this.prisma.outboxEvent.createMany({ data: rows });
971
+ }
972
+ async getUnpublishedOutboxEvents(limit) {
973
+ const effectiveLimit = limit ?? 100;
974
+ const records = await this.prisma.outboxEvent.findMany({
975
+ where: { publishedAt: null, dlqAt: null },
976
+ orderBy: [{ workflowRunId: "asc" }, { sequence: "asc" }],
977
+ take: effectiveLimit
978
+ });
979
+ return records.map((r) => this.mapOutboxEvent(r));
980
+ }
981
+ async markOutboxEventsPublished(ids) {
982
+ if (ids.length === 0) return;
983
+ await this.prisma.outboxEvent.updateMany({
984
+ where: { id: { in: ids } },
985
+ data: { publishedAt: /* @__PURE__ */ new Date() }
986
+ });
987
+ }
988
+ // ============================================================================
989
+ // Outbox DLQ Operations
990
+ // ============================================================================
991
+ async incrementOutboxRetryCount(id) {
992
+ const record = await this.prisma.outboxEvent.update({
993
+ where: { id },
994
+ data: { retryCount: { increment: 1 } },
995
+ select: { retryCount: true }
996
+ });
997
+ return record.retryCount;
998
+ }
999
+ async moveOutboxEventToDLQ(id) {
1000
+ await this.prisma.outboxEvent.update({
1001
+ where: { id },
1002
+ data: { dlqAt: /* @__PURE__ */ new Date() }
1003
+ });
1004
+ }
1005
+ async replayDLQEvents(maxEvents) {
1006
+ const dlqEvents = await this.prisma.outboxEvent.findMany({
1007
+ where: { dlqAt: { not: null } },
1008
+ take: maxEvents,
1009
+ select: { id: true }
1010
+ });
1011
+ if (dlqEvents.length === 0) return 0;
1012
+ const result = await this.prisma.outboxEvent.updateMany({
1013
+ where: { id: { in: dlqEvents.map((e) => e.id) } },
1014
+ data: { dlqAt: null, retryCount: 0 }
1015
+ });
1016
+ return result.count;
1017
+ }
1018
+ // ============================================================================
1019
+ // Idempotency Operations
1020
+ // ============================================================================
1021
+ async acquireIdempotencyKey(key, commandType) {
1022
+ try {
1023
+ await this.prisma.idempotencyKey.create({
1024
+ data: {
1025
+ key,
1026
+ commandType,
1027
+ result: IDEMPOTENCY_IN_PROGRESS_MARKER
1028
+ }
1029
+ });
1030
+ return { status: "acquired" };
1031
+ } catch (error) {
1032
+ if (error?.code !== "P2002") {
1033
+ throw error;
1034
+ }
1035
+ }
1036
+ const existing = await this.prisma.idempotencyKey.findUnique({
1037
+ where: { key_commandType: { key, commandType } },
1038
+ select: { result: true }
1039
+ });
1040
+ if (!existing || isInProgressResult(existing.result)) {
1041
+ return { status: "in_progress" };
1042
+ }
1043
+ return { status: "replay", result: existing.result };
1044
+ }
1045
+ async completeIdempotencyKey(key, commandType, result) {
1046
+ await this.prisma.idempotencyKey.update({
1047
+ where: { key_commandType: { key, commandType } },
1048
+ data: { result }
1049
+ });
1050
+ }
1051
+ async releaseIdempotencyKey(key, commandType) {
1052
+ await this.prisma.idempotencyKey.deleteMany({
1053
+ where: { key, commandType }
1054
+ });
1055
+ }
1056
+ // ============================================================================
830
1057
  // Type Mappers
831
1058
  // ============================================================================
832
1059
  mapWorkflowRun(run) {
@@ -846,7 +1073,8 @@ var PrismaWorkflowPersistence = class {
846
1073
  config: run.config,
847
1074
  totalCost: run.totalCost,
848
1075
  totalTokens: run.totalTokens,
849
- priority: run.priority
1076
+ priority: run.priority,
1077
+ version: run.version ?? 0
850
1078
  };
851
1079
  }
852
1080
  mapWorkflowStage(stage) {
@@ -873,7 +1101,22 @@ var PrismaWorkflowPersistence = class {
873
1101
  maxWaitUntil: stage.maxWaitUntil,
874
1102
  metrics: stage.metrics,
875
1103
  embeddingInfo: stage.embeddingInfo,
876
- errorMessage: stage.errorMessage
1104
+ errorMessage: stage.errorMessage,
1105
+ version: stage.version ?? 0
1106
+ };
1107
+ }
1108
+ mapOutboxEvent(record) {
1109
+ return {
1110
+ id: record.id,
1111
+ workflowRunId: record.workflowRunId,
1112
+ sequence: record.sequence,
1113
+ eventType: record.eventType,
1114
+ payload: record.payload,
1115
+ causationId: record.causationId,
1116
+ occurredAt: record.occurredAt,
1117
+ publishedAt: record.publishedAt,
1118
+ retryCount: record.retryCount,
1119
+ dlqAt: record.dlqAt
877
1120
  };
878
1121
  }
879
1122
  mapWorkflowArtifact(artifact) {
@@ -896,5 +1139,5 @@ function createPrismaWorkflowPersistence(prisma, options) {
896
1139
  }
897
1140
 
898
1141
  export { PrismaAICallLogger, PrismaJobQueue, PrismaWorkflowPersistence, createEnumHelper, createPrismaAICallLogger, createPrismaJobQueue, createPrismaWorkflowPersistence };
899
- //# sourceMappingURL=chunk-7IITBLFY.js.map
900
- //# sourceMappingURL=chunk-7IITBLFY.js.map
1142
+ //# sourceMappingURL=chunk-NYKMT46J.js.map
1143
+ //# sourceMappingURL=chunk-NYKMT46J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/persistence/prisma/ai-logger.ts","../src/persistence/prisma/enum-compat.ts","../src/persistence/prisma/job-queue.ts","../src/persistence/prisma/persistence.ts"],"names":["logger"],"mappings":"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","file":"chunk-NYKMT46J.js","sourcesContent":["/**\n * PrismaAICallLogger - Prisma implementation of AICallLogger\n *\n * Handles AI call logging to the database. Uses fire-and-forget pattern\n * for non-blocking logging during AI operations.\n */\n\nimport { createLogger } from \"../../utils/logger\";\nimport type {\n AICallLogger,\n AIHelperStats,\n CreateAICallInput,\n} from \"../interface\";\n\nconst logger = createLogger(\"AICallLogger\");\n\n// Type for prisma client - using any for flexibility\ntype PrismaClient = any;\n\nexport class PrismaAICallLogger implements AICallLogger {\n constructor(private readonly prisma: PrismaClient) {}\n\n /**\n * Log a single AI call (fire and forget)\n * Does not await - logs asynchronously to avoid blocking AI operations\n */\n logCall(call: CreateAICallInput): void {\n this.prisma.aICall\n .create({\n data: {\n topic: call.topic,\n callType: call.callType,\n modelKey: call.modelKey,\n modelId: call.modelId,\n prompt: call.prompt,\n response: call.response,\n inputTokens: call.inputTokens,\n outputTokens: call.outputTokens,\n cost: call.cost,\n metadata: call.metadata as unknown,\n },\n })\n .catch((error: unknown) =>\n logger.error(\"Failed to persist AI call:\", error),\n );\n }\n\n /**\n * Log batch results (for recording batch API results)\n */\n async logBatchResults(\n batchId: string,\n results: CreateAICallInput[],\n ): Promise<void> {\n await this.prisma.aICall.createMany({\n data: results.map((call) => ({\n topic: call.topic,\n callType: call.callType,\n modelKey: call.modelKey,\n modelId: call.modelId,\n prompt: call.prompt,\n response: call.response,\n inputTokens: call.inputTokens,\n outputTokens: call.outputTokens,\n cost: call.cost,\n metadata: {\n ...(call.metadata as object),\n batchId,\n } as unknown,\n })),\n });\n }\n\n /**\n * Get aggregated stats for a topic prefix\n */\n async getStats(topicPrefix: string): Promise<AIHelperStats> {\n const calls = await this.prisma.aICall.findMany({\n where: {\n topic: { startsWith: topicPrefix },\n },\n select: {\n modelKey: true,\n inputTokens: true,\n outputTokens: true,\n cost: true,\n },\n });\n\n const perModel: AIHelperStats[\"perModel\"] = {};\n\n for (const call of calls) {\n if (!perModel[call.modelKey]) {\n perModel[call.modelKey] = {\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n };\n }\n perModel[call.modelKey].calls++;\n perModel[call.modelKey].inputTokens += call.inputTokens;\n perModel[call.modelKey].outputTokens += call.outputTokens;\n perModel[call.modelKey].cost += call.cost;\n }\n\n return {\n totalCalls: calls.length,\n totalInputTokens: calls.reduce(\n (sum: number, c: { inputTokens: number }) => sum + c.inputTokens,\n 0,\n ),\n totalOutputTokens: calls.reduce(\n (sum: number, c: { outputTokens: number }) => sum + c.outputTokens,\n 0,\n ),\n totalCost: calls.reduce(\n (sum: number, c: { cost: number }) => sum + c.cost,\n 0,\n ),\n perModel,\n };\n }\n\n /**\n * Check if batch results are already recorded\n */\n async isRecorded(batchId: string): Promise<boolean> {\n const existing = await this.prisma.aICall.findFirst({\n where: {\n metadata: {\n path: [\"batchId\"],\n equals: batchId,\n },\n },\n select: { id: true },\n });\n return existing !== null;\n }\n}\n\n// Factory function to create PrismaAICallLogger with prisma client\nexport function createPrismaAICallLogger(prisma: PrismaClient): AICallLogger {\n return new PrismaAICallLogger(prisma);\n}\n","/**\n * Prisma Enum Compatibility Layer\n *\n * Provides compatibility between Prisma 6.x (string enums) and Prisma 7.x (typed enums).\n * Prisma 7.x introduced runtime enum validation, requiring actual enum values instead of strings.\n *\n * This utility resolves enum values from the Prisma client's $Enums property when available,\n * falling back to string values for older Prisma versions.\n *\n * @example\n * ```typescript\n * const helper = createEnumHelper(prisma);\n *\n * // Instead of: status: \"PENDING\"\n * // Use: status: helper.status(\"PENDING\")\n * ```\n */\n\ntype PrismaClient = any;\n\nexport interface PrismaEnumHelper {\n /** Resolve Status enum value (unified enum for workflows, stages, and jobs) */\n status(value: string): unknown;\n /** Resolve ArtifactType enum value */\n artifactType(value: string): unknown;\n /** Resolve LogLevel enum value */\n logLevel(value: string): unknown;\n}\n\n/**\n * Creates an enum helper that resolves enum values from the Prisma client.\n *\n * Supports both Prisma 6.x (returns string) and Prisma 7.x (returns typed enum).\n */\nexport function createEnumHelper(prisma: PrismaClient): PrismaEnumHelper {\n const resolveEnum = (enumName: string, value: string): unknown => {\n try {\n // Prisma 7.x exposes enums via $Enums\n const enumObj = prisma.$Enums?.[enumName];\n if (enumObj && value in enumObj) {\n return enumObj[value];\n }\n } catch {\n // Ignore - fall through to string\n }\n // Fallback for Prisma 6.x or if enum not found\n return value;\n };\n\n return {\n status: (value: string) => resolveEnum(\"Status\", value),\n artifactType: (value: string) => resolveEnum(\"ArtifactType\", value),\n logLevel: (value: string) => resolveEnum(\"LogLevel\", value),\n };\n}\n","/**\n * PrismaJobQueue - Prisma implementation of JobQueue\n *\n * Provides atomic job queue operations using PostgreSQL\n * with FOR UPDATE SKIP LOCKED for safe concurrent access.\n *\n * This is migrated from the original services/job-queue.server.ts\n */\n\nimport { createLogger } from \"../../utils/logger\";\nimport type { DequeueResult, EnqueueJobInput, JobQueue } from \"../interface\";\nimport { createEnumHelper, type PrismaEnumHelper } from \"./enum-compat\";\nimport type { DatabaseType } from \"./persistence\";\n\nconst logger = createLogger(\"JobQueue\");\n\n// Type for prisma client - using any for flexibility\ntype PrismaClient = any;\n\nexport interface PrismaJobQueueOptions {\n /**\n * Unique worker identifier. Defaults to auto-generated ID.\n */\n workerId?: string;\n /**\n * Database type. Defaults to \"postgresql\".\n * Set to \"sqlite\" when using SQLite (uses optimistic locking instead of FOR UPDATE SKIP LOCKED).\n */\n databaseType?: DatabaseType;\n}\n\nexport class PrismaJobQueue implements JobQueue {\n private workerId: string;\n private prisma: PrismaClient;\n private enums: PrismaEnumHelper;\n private databaseType: DatabaseType;\n\n constructor(prisma: PrismaClient, options: PrismaJobQueueOptions = {}) {\n this.prisma = prisma;\n this.workerId = options.workerId || `worker-${process.pid}-${Date.now()}`;\n this.enums = createEnumHelper(prisma);\n this.databaseType = options.databaseType ?? \"postgresql\";\n }\n\n /**\n * Add a new job to the queue\n */\n async enqueue(options: EnqueueJobInput): Promise<string> {\n const job = await this.prisma.jobQueue.create({\n data: {\n workflowRunId: options.workflowRunId,\n stageId: options.stageId,\n priority: options.priority ?? 5,\n payload: {\n ...options.payload,\n _workflowId: options.workflowId,\n } as unknown,\n status: this.enums.status(\"PENDING\"),\n nextPollAt: options.scheduledFor,\n },\n });\n\n logger.debug(\n `Enqueued job ${job.id} for stage ${options.stageId} (run: ${options.workflowRunId})`,\n );\n return job.id;\n }\n\n /**\n * Enqueue multiple stages in parallel (same execution group)\n */\n async enqueueParallel(jobs: EnqueueJobInput[]): Promise<string[]> {\n if (jobs.length === 0) return [];\n\n const results = await this.prisma.$transaction(\n jobs.map((job) =>\n this.prisma.jobQueue.create({\n data: {\n workflowRunId: job.workflowRunId,\n stageId: job.stageId,\n priority: job.priority ?? 5,\n payload: { ...job.payload, _workflowId: job.workflowId } as unknown,\n status: this.enums.status(\"PENDING\"),\n },\n }),\n ),\n );\n\n return results.map((r: { id: string }) => r.id);\n }\n\n /**\n * Atomically dequeue the next available job\n * Uses FOR UPDATE SKIP LOCKED (PostgreSQL) or optimistic locking (SQLite)\n */\n async dequeue(): Promise<DequeueResult | null> {\n if (this.databaseType === \"sqlite\") {\n return this.dequeueSqlite();\n }\n return this.dequeuePostgres();\n }\n\n /**\n * PostgreSQL implementation using FOR UPDATE SKIP LOCKED for safe concurrency\n */\n private async dequeuePostgres(): Promise<DequeueResult | null> {\n try {\n const result = await this.prisma.$queryRaw<\n Array<{\n id: string;\n workflowRunId: string;\n stageId: string;\n priority: number;\n attempt: number;\n maxAttempts: number;\n payload: unknown;\n }>\n >`\n UPDATE \"job_queue\"\n SET\n status = 'RUNNING',\n \"workerId\" = ${this.workerId},\n \"lockedAt\" = NOW(),\n \"startedAt\" = NOW(),\n attempt = attempt + 1\n WHERE id = (\n SELECT id FROM \"job_queue\"\n WHERE status = 'PENDING'\n AND (\"nextPollAt\" IS NULL OR \"nextPollAt\" <= NOW())\n ORDER BY priority DESC, \"createdAt\" ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED\n )\n RETURNING id, \"workflowRunId\", \"stageId\", priority, attempt, \"maxAttempts\", payload\n `;\n\n if (result.length === 0) {\n return null;\n }\n\n const job = result[0];\n logger.debug(\n `Dequeued job ${job.id} (stage: ${job.stageId}, attempt: ${job.attempt})`,\n );\n\n const payload = job.payload as Record<string, unknown>;\n const { _workflowId, ...rest } = payload;\n return {\n jobId: job.id,\n workflowRunId: job.workflowRunId,\n workflowId: (_workflowId as string) ?? \"\",\n stageId: job.stageId,\n priority: job.priority,\n attempt: job.attempt,\n maxAttempts: job.maxAttempts,\n payload: rest,\n };\n } catch (error) {\n logger.error(\"Error dequeuing job:\", error);\n return null;\n }\n }\n\n /**\n * SQLite implementation using optimistic locking.\n * SQLite doesn't support FOR UPDATE SKIP LOCKED, so we use a two-step approach:\n * 1. Find a PENDING job\n * 2. Atomically update it (only succeeds if still PENDING)\n * 3. If another worker claimed it, retry\n */\n private async dequeueSqlite(): Promise<DequeueResult | null> {\n try {\n const now = new Date();\n\n // Step 1: Find the next PENDING job\n const job = await this.prisma.jobQueue.findFirst({\n where: {\n status: this.enums.status(\"PENDING\"),\n OR: [{ nextPollAt: null }, { nextPollAt: { lte: now } }],\n },\n orderBy: [{ priority: \"desc\" }, { createdAt: \"asc\" }],\n });\n\n if (!job) {\n return null;\n }\n\n // Step 2: Atomically claim it (only succeeds if still PENDING)\n const result = await this.prisma.jobQueue.updateMany({\n where: {\n id: job.id,\n status: this.enums.status(\"PENDING\"), // Optimistic lock\n },\n data: {\n status: this.enums.status(\"RUNNING\"),\n workerId: this.workerId,\n lockedAt: now,\n startedAt: now,\n attempt: { increment: 1 },\n },\n });\n\n if (result.count === 0) {\n // Another worker claimed it, retry\n return this.dequeueSqlite();\n }\n\n // Fetch the updated job to get the new attempt count\n const claimedJob = await this.prisma.jobQueue.findUnique({\n where: { id: job.id },\n });\n\n if (!claimedJob) {\n return null;\n }\n\n logger.debug(\n `Dequeued job ${claimedJob.id} (stage: ${claimedJob.stageId}, attempt: ${claimedJob.attempt})`,\n );\n\n const claimedPayload = claimedJob.payload as Record<string, unknown>;\n const { _workflowId: claimedWfId, ...claimedRest } = claimedPayload;\n return {\n jobId: claimedJob.id,\n workflowRunId: claimedJob.workflowRunId,\n workflowId: (claimedWfId as string) ?? \"\",\n stageId: claimedJob.stageId,\n priority: claimedJob.priority,\n attempt: claimedJob.attempt,\n maxAttempts: claimedJob.maxAttempts,\n payload: claimedRest,\n };\n } catch (error) {\n logger.error(\"Error dequeuing job:\", error);\n return null;\n }\n }\n\n /**\n * Mark job as completed\n */\n async complete(jobId: string): Promise<void> {\n await this.prisma.jobQueue.update({\n where: { id: jobId },\n data: {\n status: this.enums.status(\"COMPLETED\"),\n completedAt: new Date(),\n },\n });\n logger.debug(`Job ${jobId} completed`);\n }\n\n /**\n * Mark job as suspended (for async-batch)\n */\n async suspend(jobId: string, nextPollAt: Date): Promise<void> {\n await this.prisma.jobQueue.update({\n where: { id: jobId },\n data: {\n status: this.enums.status(\"SUSPENDED\"),\n nextPollAt,\n workerId: null,\n lockedAt: null,\n },\n });\n logger.debug(`Job ${jobId} suspended until ${nextPollAt.toISOString()}`);\n }\n\n /**\n * Mark job as failed\n */\n async fail(\n jobId: string,\n error: string,\n shouldRetry: boolean = false,\n ): Promise<void> {\n const job = await this.prisma.jobQueue.findUnique({\n where: { id: jobId },\n select: { attempt: true, maxAttempts: true },\n });\n\n if (shouldRetry && job && job.attempt < job.maxAttempts) {\n // Re-queue for retry with exponential backoff\n const backoffMs = 2 ** job.attempt * 1000; // 2s, 4s, 8s...\n const nextPollAt = new Date(Date.now() + backoffMs);\n\n await this.prisma.jobQueue.update({\n where: { id: jobId },\n data: {\n status: this.enums.status(\"PENDING\"),\n lastError: error,\n workerId: null,\n lockedAt: null,\n nextPollAt: nextPollAt,\n },\n });\n logger.debug(`Job ${jobId} failed, will retry in ${backoffMs}ms`);\n } else {\n await this.prisma.jobQueue.update({\n where: { id: jobId },\n data: {\n status: this.enums.status(\"FAILED\"),\n completedAt: new Date(),\n lastError: error,\n },\n });\n logger.debug(`Job ${jobId} failed permanently: ${error}`);\n }\n }\n\n /**\n * Get suspended jobs that are ready to be checked\n */\n async getSuspendedJobsReadyToPoll(): Promise<\n Array<{ jobId: string; stageId: string; workflowRunId: string }>\n > {\n const jobs = await this.prisma.jobQueue.findMany({\n where: {\n status: this.enums.status(\"SUSPENDED\"),\n nextPollAt: { lte: new Date() },\n },\n select: {\n id: true,\n workflowRunId: true,\n stageId: true,\n },\n });\n\n return jobs.map(\n (j: { id: string; workflowRunId: string; stageId: string }) => ({\n jobId: j.id,\n workflowRunId: j.workflowRunId,\n stageId: j.stageId,\n }),\n );\n }\n\n /**\n * Release stale locks (for crashed workers)\n */\n async releaseStaleJobs(staleThresholdMs: number = 300000): Promise<number> {\n const thresholdDate = new Date(Date.now() - staleThresholdMs);\n\n const result = await this.prisma.jobQueue.updateMany({\n where: {\n status: this.enums.status(\"RUNNING\"),\n lockedAt: { lt: thresholdDate },\n },\n data: {\n status: this.enums.status(\"PENDING\"),\n workerId: null,\n lockedAt: null,\n },\n });\n\n if (result.count > 0) {\n logger.debug(\n `Released ${result.count} stale jobs (locked before ${thresholdDate.toISOString()})`,\n );\n }\n\n return result.count;\n }\n}\n\n/**\n * Factory function to create PrismaJobQueue with prisma client\n *\n * @param prisma - Prisma client instance\n * @param optionsOrWorkerId - Options object or workerId string (for backwards compatibility)\n */\nexport function createPrismaJobQueue(\n prisma: PrismaClient,\n optionsOrWorkerId?: PrismaJobQueueOptions | string,\n): JobQueue {\n // Handle backwards compatibility: if string is passed, treat as workerId\n const options: PrismaJobQueueOptions =\n typeof optionsOrWorkerId === \"string\"\n ? { workerId: optionsOrWorkerId }\n : (optionsOrWorkerId ?? {});\n\n return new PrismaJobQueue(prisma, options);\n}\n","/**\n * PrismaWorkflowPersistence - Prisma implementation of WorkflowPersistence\n *\n * This is the default persistence implementation used by the workflow engine.\n * It wraps Prisma client operations to match the WorkflowPersistence interface.\n */\n\nimport type {\n CreateLogInput,\n CreateOutboxEventInput,\n CreateRunInput,\n CreateStageInput,\n OutboxRecord,\n SaveArtifactInput,\n UpdateRunInput,\n UpdateStageInput,\n UpsertStageInput,\n WorkflowArtifactRecord,\n WorkflowPersistence,\n WorkflowRunRecord,\n WorkflowStageRecord,\n WorkflowStageStatus,\n WorkflowStatus,\n} from \"../interface\";\nimport { StaleVersionError } from \"../interface\";\nimport { createEnumHelper, type PrismaEnumHelper } from \"./enum-compat\";\n\n// Type for prisma client - using any to avoid dependency on specific prisma client\ntype PrismaClient = any;\n\nexport type DatabaseType = \"postgresql\" | \"sqlite\";\n\nexport interface PrismaWorkflowPersistenceOptions {\n /**\n * Database type. Defaults to \"postgresql\".\n * Set to \"sqlite\" when using SQLite (uses optimistic locking instead of FOR UPDATE SKIP LOCKED).\n */\n databaseType?: DatabaseType;\n}\n\nconst IDEMPOTENCY_IN_PROGRESS_MARKER = {\n __workflowEngineState: \"in_progress\",\n};\n\nfunction isInProgressResult(result: unknown): boolean {\n if (!result || typeof result !== \"object\") return false;\n return (\n (result as Record<string, unknown>).__workflowEngineState === \"in_progress\"\n );\n}\n\nexport class PrismaWorkflowPersistence implements WorkflowPersistence {\n private enums: PrismaEnumHelper;\n private databaseType: DatabaseType;\n\n constructor(\n private readonly prisma: PrismaClient,\n options: PrismaWorkflowPersistenceOptions = {},\n ) {\n this.enums = createEnumHelper(prisma);\n this.databaseType = options.databaseType ?? \"postgresql\";\n }\n\n async withTransaction<T>(\n fn: (tx: WorkflowPersistence) => Promise<T>,\n ): Promise<T> {\n if (typeof this.prisma.$transaction !== \"function\") {\n return fn(this);\n }\n return this.prisma.$transaction(async (tx: PrismaClient) => {\n const txPersistence = new PrismaWorkflowPersistence(tx, {\n databaseType: this.databaseType,\n });\n return fn(txPersistence);\n });\n }\n\n // ============================================================================\n // WorkflowRun Operations\n // ============================================================================\n\n async createRun(data: CreateRunInput): Promise<WorkflowRunRecord> {\n const run = await this.prisma.workflowRun.create({\n data: {\n id: data.id,\n workflowId: data.workflowId,\n workflowName: data.workflowName,\n workflowType: data.workflowType,\n input: data.input as unknown,\n config: (data.config ?? {}) as unknown,\n priority: data.priority ?? 5,\n // Spread metadata for domain-specific fields (certificateId, etc.)\n ...(data.metadata ?? {}),\n },\n });\n return this.mapWorkflowRun(run);\n }\n\n async updateRun(id: string, data: UpdateRunInput): Promise<void> {\n const updateData = this.buildRunUpdateData(data);\n\n if (data.expectedVersion === undefined) {\n await this.prisma.workflowRun.update({\n where: { id },\n data: updateData,\n });\n return;\n }\n\n const result = await this.prisma.workflowRun.updateMany({\n where: { id, version: data.expectedVersion },\n data: {\n ...updateData,\n version: { increment: 1 },\n },\n });\n\n if (result.count === 0) {\n const current = await this.prisma.workflowRun.findUnique({\n where: { id },\n select: { version: true },\n });\n throw new StaleVersionError(\n \"WorkflowRun\",\n id,\n data.expectedVersion,\n current?.version ?? -1,\n );\n }\n }\n\n async getRun(id: string): Promise<WorkflowRunRecord | null> {\n const run = await this.prisma.workflowRun.findUnique({ where: { id } });\n return run ? this.mapWorkflowRun(run) : null;\n }\n\n async getRunStatus(id: string): Promise<WorkflowStatus | null> {\n const run = await this.prisma.workflowRun.findUnique({\n where: { id },\n select: { status: true },\n });\n return run?.status ?? null;\n }\n\n async getRunsByStatus(status: WorkflowStatus): Promise<WorkflowRunRecord[]> {\n const runs = await this.prisma.workflowRun.findMany({\n where: { status: this.enums.status(status) },\n orderBy: { createdAt: \"asc\" },\n });\n return runs.map((run: any) => this.mapWorkflowRun(run));\n }\n\n async claimPendingRun(id: string): Promise<boolean> {\n // Atomic update: only succeeds if status is still PENDING\n // This prevents race conditions when multiple workers try to claim the same run\n const result = await this.prisma.workflowRun.updateMany({\n where: {\n id,\n status: this.enums.status(\"PENDING\"),\n },\n data: {\n status: this.enums.status(\"RUNNING\"),\n startedAt: new Date(),\n },\n });\n\n // updateMany returns { count: N } - if count is 0, another worker already claimed it\n return result.count > 0;\n }\n\n async claimNextPendingRun(): Promise<WorkflowRunRecord | null> {\n if (this.databaseType === \"sqlite\") {\n return this.claimNextPendingRunSqlite();\n }\n return this.claimNextPendingRunPostgres();\n }\n\n /**\n * PostgreSQL implementation using FOR UPDATE SKIP LOCKED for zero-contention claiming.\n * This atomically:\n * 1. Finds the highest priority PENDING run (FIFO within same priority)\n * 2. Locks it exclusively (other workers skip locked rows)\n * 3. Updates it to RUNNING\n * 4. Returns the claimed run\n */\n private async claimNextPendingRunPostgres(): Promise<WorkflowRunRecord | null> {\n // Note: Table name is \"workflow_runs\" (snake_case per Prisma @@map convention)\n // Column names are camelCase (e.g., \"createdAt\", \"startedAt\")\n const results = await this.prisma.$queryRaw<any[]>`\n WITH claimed AS (\n SELECT id\n FROM \"workflow_runs\"\n WHERE status = ${this.enums.status(\"PENDING\")}\n ORDER BY priority DESC, \"createdAt\" ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED\n )\n UPDATE \"workflow_runs\"\n SET status = ${this.enums.status(\"RUNNING\")},\n \"startedAt\" = NOW(),\n \"updatedAt\" = NOW()\n FROM claimed\n WHERE \"workflow_runs\".id = claimed.id\n RETURNING \"workflow_runs\".*\n `;\n\n if (results.length === 0) {\n return null;\n }\n\n return this.mapWorkflowRun(results[0]);\n }\n\n /**\n * SQLite implementation using optimistic locking.\n * SQLite doesn't support FOR UPDATE SKIP LOCKED, so we use a two-step approach:\n * 1. Find a PENDING run\n * 2. Atomically update it (only succeeds if still PENDING)\n * 3. If another worker claimed it, retry\n */\n private async claimNextPendingRunSqlite(): Promise<WorkflowRunRecord | null> {\n // Step 1: Find the next PENDING run\n const run = await this.prisma.workflowRun.findFirst({\n where: { status: this.enums.status(\"PENDING\") },\n orderBy: [{ priority: \"desc\" }, { createdAt: \"asc\" }],\n });\n\n if (!run) {\n return null;\n }\n\n // Step 2: Atomically claim it (only succeeds if still PENDING)\n const result = await this.prisma.workflowRun.updateMany({\n where: {\n id: run.id,\n status: this.enums.status(\"PENDING\"), // Optimistic lock\n },\n data: {\n status: this.enums.status(\"RUNNING\"),\n startedAt: new Date(),\n updatedAt: new Date(),\n },\n });\n\n if (result.count === 0) {\n // Another worker claimed it, retry\n return this.claimNextPendingRunSqlite();\n }\n\n // Fetch the updated record\n const claimedRun = await this.prisma.workflowRun.findUnique({\n where: { id: run.id },\n });\n\n return claimedRun ? this.mapWorkflowRun(claimedRun) : null;\n }\n\n // ============================================================================\n // WorkflowStage Operations\n // ============================================================================\n\n async createStage(data: CreateStageInput): Promise<WorkflowStageRecord> {\n const stage = await this.prisma.workflowStage.create({\n data: {\n workflowRunId: data.workflowRunId,\n stageId: data.stageId,\n stageName: data.stageName,\n stageNumber: data.stageNumber,\n executionGroup: data.executionGroup,\n status: data.status\n ? this.enums.status(data.status)\n : this.enums.status(\"PENDING\"),\n startedAt: data.startedAt,\n config: data.config as unknown,\n inputData: data.inputData as unknown,\n },\n });\n return this.mapWorkflowStage(stage);\n }\n\n async upsertStage(data: UpsertStageInput): Promise<WorkflowStageRecord> {\n const stage = await this.prisma.workflowStage.upsert({\n where: {\n workflowRunId_stageId: {\n workflowRunId: data.workflowRunId,\n stageId: data.stageId,\n },\n },\n create: {\n workflowRunId: data.create.workflowRunId,\n stageId: data.create.stageId,\n stageName: data.create.stageName,\n stageNumber: data.create.stageNumber,\n executionGroup: data.create.executionGroup,\n status: data.create.status\n ? this.enums.status(data.create.status)\n : this.enums.status(\"RUNNING\"),\n startedAt: data.create.startedAt ?? new Date(),\n config: data.create.config as unknown,\n inputData: data.create.inputData as unknown,\n },\n update: {\n status: data.update.status\n ? this.enums.status(data.update.status)\n : undefined,\n startedAt: data.update.startedAt,\n },\n });\n return this.mapWorkflowStage(stage);\n }\n\n async updateStage(id: string, data: UpdateStageInput): Promise<void> {\n const updateData = this.buildStageUpdateData(data);\n\n if (data.expectedVersion === undefined) {\n await this.prisma.workflowStage.update({\n where: { id },\n data: updateData,\n });\n return;\n }\n\n const result = await this.prisma.workflowStage.updateMany({\n where: { id, version: data.expectedVersion },\n data: {\n ...updateData,\n version: { increment: 1 },\n },\n });\n\n if (result.count === 0) {\n const current = await this.prisma.workflowStage.findUnique({\n where: { id },\n select: { version: true },\n });\n throw new StaleVersionError(\n \"WorkflowStage\",\n id,\n data.expectedVersion,\n current?.version ?? -1,\n );\n }\n }\n\n async updateStageByRunAndStageId(\n workflowRunId: string,\n stageId: string,\n data: UpdateStageInput,\n ): Promise<void> {\n const updateData = this.buildStageUpdateData(data);\n\n if (data.expectedVersion === undefined) {\n await this.prisma.workflowStage.update({\n where: {\n workflowRunId_stageId: { workflowRunId, stageId },\n },\n data: updateData,\n });\n return;\n }\n\n const result = await this.prisma.workflowStage.updateMany({\n where: {\n workflowRunId,\n stageId,\n version: data.expectedVersion,\n },\n data: {\n ...updateData,\n version: { increment: 1 },\n },\n });\n\n if (result.count === 0) {\n const current = await this.prisma.workflowStage.findFirst({\n where: { workflowRunId, stageId },\n select: { id: true, version: true },\n });\n throw new StaleVersionError(\n \"WorkflowStage\",\n current?.id ?? `${workflowRunId}/${stageId}`,\n data.expectedVersion,\n current?.version ?? -1,\n );\n }\n }\n\n private buildRunUpdateData(data: UpdateRunInput): Record<string, unknown> {\n return {\n status: data.status ? this.enums.status(data.status) : undefined,\n startedAt: data.startedAt,\n completedAt: data.completedAt,\n duration: data.duration,\n output: data.output as unknown,\n totalCost: data.totalCost,\n totalTokens: data.totalTokens,\n };\n }\n\n private buildStageUpdateData(\n data: UpdateStageInput,\n ): Record<string, unknown> {\n return {\n status: data.status ? this.enums.status(data.status) : undefined,\n startedAt: data.startedAt,\n completedAt: data.completedAt,\n duration: data.duration,\n outputData: data.outputData as unknown,\n config: data.config as unknown,\n suspendedState: data.suspendedState as unknown,\n resumeData: data.resumeData as unknown,\n nextPollAt: data.nextPollAt,\n pollInterval: data.pollInterval,\n maxWaitUntil: data.maxWaitUntil,\n metrics: data.metrics as unknown,\n embeddingInfo: data.embeddingInfo as unknown,\n errorMessage: data.errorMessage,\n };\n }\n\n async getStage(\n runId: string,\n stageId: string,\n ): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findUnique({\n where: {\n workflowRunId_stageId: { workflowRunId: runId, stageId },\n },\n });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async getStageById(id: string): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findUnique({ where: { id } });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async getStagesByRun(\n runId: string,\n options?: { status?: WorkflowStageStatus; orderBy?: \"asc\" | \"desc\" },\n ): Promise<WorkflowStageRecord[]> {\n const stages = await this.prisma.workflowStage.findMany({\n where: {\n workflowRunId: runId,\n ...(options?.status && { status: this.enums.status(options.status) }),\n },\n orderBy: { executionGroup: options?.orderBy ?? \"asc\" },\n });\n return stages.map((s: Record<string, unknown>) => this.mapWorkflowStage(s));\n }\n\n async getSuspendedStages(beforeDate: Date): Promise<WorkflowStageRecord[]> {\n const stages = await this.prisma.workflowStage.findMany({\n where: {\n status: this.enums.status(\"SUSPENDED\"),\n nextPollAt: { lte: beforeDate },\n },\n include: {\n workflowRun: { select: { workflowType: true } },\n },\n });\n return stages.map((s: Record<string, unknown>) => this.mapWorkflowStage(s));\n }\n\n async getFirstSuspendedStageReadyToResume(\n runId: string,\n ): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findFirst({\n where: {\n workflowRunId: runId,\n status: this.enums.status(\"SUSPENDED\"),\n nextPollAt: null, // Ready to resume (poll cleared by orchestrator)\n },\n orderBy: { executionGroup: \"asc\" },\n });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async getFirstFailedStage(\n runId: string,\n ): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findFirst({\n where: {\n workflowRunId: runId,\n status: this.enums.status(\"FAILED\"),\n },\n orderBy: { executionGroup: \"desc\" },\n });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async getLastCompletedStage(\n runId: string,\n ): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findFirst({\n where: {\n workflowRunId: runId,\n status: this.enums.status(\"COMPLETED\"),\n },\n orderBy: { executionGroup: \"desc\" },\n });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async getLastCompletedStageBefore(\n runId: string,\n executionGroup: number,\n ): Promise<WorkflowStageRecord | null> {\n const stage = await this.prisma.workflowStage.findFirst({\n where: {\n workflowRunId: runId,\n status: this.enums.status(\"COMPLETED\"),\n executionGroup: { lt: executionGroup },\n },\n orderBy: { executionGroup: \"desc\" },\n });\n return stage ? this.mapWorkflowStage(stage) : null;\n }\n\n async deleteStage(id: string): Promise<void> {\n await this.prisma.workflowStage.delete({ where: { id } });\n }\n\n // ============================================================================\n // WorkflowLog Operations\n // ============================================================================\n\n async createLog(data: CreateLogInput): Promise<void> {\n await this.prisma.workflowLog.create({\n data: {\n workflowRunId: data.workflowRunId,\n workflowStageId: data.workflowStageId,\n level: this.enums.logLevel(data.level),\n message: data.message,\n metadata: data.metadata as unknown,\n },\n });\n }\n\n // ============================================================================\n // WorkflowArtifact Operations\n // ============================================================================\n\n async saveArtifact(data: SaveArtifactInput): Promise<void> {\n await this.prisma.workflowArtifact.upsert({\n where: {\n workflowRunId_key: {\n workflowRunId: data.workflowRunId,\n key: data.key,\n },\n },\n create: {\n workflowRunId: data.workflowRunId,\n workflowStageId: data.workflowStageId,\n key: data.key,\n type: this.enums.artifactType(data.type),\n data: data.data as unknown,\n size: data.size,\n metadata: data.metadata as unknown,\n },\n update: {\n data: data.data as unknown,\n size: data.size,\n metadata: data.metadata as unknown,\n },\n });\n }\n\n async loadArtifact(runId: string, key: string): Promise<unknown> {\n const artifact = await this.prisma.workflowArtifact.findUnique({\n where: {\n workflowRunId_key: { workflowRunId: runId, key },\n },\n });\n return artifact?.data;\n }\n\n async hasArtifact(runId: string, key: string): Promise<boolean> {\n const artifact = await this.prisma.workflowArtifact.findUnique({\n where: {\n workflowRunId_key: { workflowRunId: runId, key },\n },\n select: { id: true },\n });\n return artifact !== null;\n }\n\n async deleteArtifact(runId: string, key: string): Promise<void> {\n await this.prisma.workflowArtifact.delete({\n where: {\n workflowRunId_key: { workflowRunId: runId, key },\n },\n });\n }\n\n async listArtifacts(runId: string): Promise<WorkflowArtifactRecord[]> {\n const artifacts = await this.prisma.workflowArtifact.findMany({\n where: { workflowRunId: runId },\n });\n return artifacts.map((a: Record<string, unknown>) =>\n this.mapWorkflowArtifact(a),\n );\n }\n\n async getStageIdForArtifact(\n runId: string,\n stageId: string,\n ): Promise<string | null> {\n const stage = await this.prisma.workflowStage.findUnique({\n where: {\n workflowRunId_stageId: { workflowRunId: runId, stageId },\n },\n select: { id: true },\n });\n return stage?.id ?? null;\n }\n\n // ============================================================================\n // Stage Output Convenience Methods\n // ============================================================================\n\n async saveStageOutput(\n runId: string,\n workflowType: string,\n stageId: string,\n output: unknown,\n ): Promise<string> {\n // Generate key with consistent pattern: workflow-v2/{type}/{runId}/{stageId}/output.json\n const key = `workflow-v2/${workflowType}/${runId}/${stageId}/output.json`;\n\n const json = JSON.stringify(output);\n const size = Buffer.byteLength(json, \"utf8\");\n\n // Get the workflowStage record ID for linking\n const workflowStageId = await this.getStageIdForArtifact(runId, stageId);\n\n await this.prisma.workflowArtifact.upsert({\n where: {\n workflowRunId_key: { workflowRunId: runId, key },\n },\n update: {\n data: output as unknown,\n size,\n workflowStageId,\n },\n create: {\n workflowRunId: runId,\n workflowStageId,\n key,\n type: this.enums.artifactType(\"STAGE_OUTPUT\"),\n data: output as unknown,\n size,\n },\n });\n\n return key;\n }\n\n // ============================================================================\n // Outbox Operations\n // ============================================================================\n\n async appendOutboxEvents(events: CreateOutboxEventInput[]): Promise<void> {\n if (events.length === 0) return;\n\n // Group by workflowRunId to assign sequences per-run\n const byRun = new Map<string, CreateOutboxEventInput[]>();\n for (const event of events) {\n const list = byRun.get(event.workflowRunId) ?? [];\n list.push(event);\n byRun.set(event.workflowRunId, list);\n }\n\n const rows: Array<{\n workflowRunId: string;\n sequence: number;\n eventType: string;\n payload: unknown;\n causationId: string;\n occurredAt: Date;\n }> = [];\n\n for (const [workflowRunId, runEvents] of byRun) {\n if (\n this.databaseType === \"postgresql\" &&\n typeof this.prisma.$executeRaw === \"function\"\n ) {\n // Serialize per-run sequence assignment inside the transaction.\n await this.prisma.$executeRaw`\n SELECT pg_advisory_xact_lock(hashtext(${workflowRunId}))\n `;\n }\n\n // Get the current max sequence for this run\n const maxResult = await this.prisma.outboxEvent.aggregate({\n where: { workflowRunId },\n _max: { sequence: true },\n });\n let seq = maxResult._max.sequence ?? 0;\n\n for (const event of runEvents) {\n seq++;\n rows.push({\n workflowRunId: event.workflowRunId,\n sequence: seq,\n eventType: event.eventType,\n payload: event.payload as any,\n causationId: event.causationId,\n occurredAt: event.occurredAt,\n });\n }\n }\n\n await this.prisma.outboxEvent.createMany({ data: rows });\n }\n\n async getUnpublishedOutboxEvents(limit?: number): Promise<OutboxRecord[]> {\n const effectiveLimit = limit ?? 100;\n const records = await this.prisma.outboxEvent.findMany({\n where: { publishedAt: null, dlqAt: null },\n orderBy: [{ workflowRunId: \"asc\" }, { sequence: \"asc\" }],\n take: effectiveLimit,\n });\n return records.map((r: any) => this.mapOutboxEvent(r));\n }\n\n async markOutboxEventsPublished(ids: string[]): Promise<void> {\n if (ids.length === 0) return;\n await this.prisma.outboxEvent.updateMany({\n where: { id: { in: ids } },\n data: { publishedAt: new Date() },\n });\n }\n\n // ============================================================================\n // Outbox DLQ Operations\n // ============================================================================\n\n async incrementOutboxRetryCount(id: string): Promise<number> {\n const record = await this.prisma.outboxEvent.update({\n where: { id },\n data: { retryCount: { increment: 1 } },\n select: { retryCount: true },\n });\n return record.retryCount;\n }\n\n async moveOutboxEventToDLQ(id: string): Promise<void> {\n await this.prisma.outboxEvent.update({\n where: { id },\n data: { dlqAt: new Date() },\n });\n }\n\n async replayDLQEvents(maxEvents: number): Promise<number> {\n const dlqEvents = await this.prisma.outboxEvent.findMany({\n where: { dlqAt: { not: null } },\n take: maxEvents,\n select: { id: true },\n });\n\n if (dlqEvents.length === 0) return 0;\n\n const result = await this.prisma.outboxEvent.updateMany({\n where: { id: { in: dlqEvents.map((e: any) => e.id) } },\n data: { dlqAt: null, retryCount: 0 },\n });\n return result.count;\n }\n\n // ============================================================================\n // Idempotency Operations\n // ============================================================================\n\n async acquireIdempotencyKey(\n key: string,\n commandType: string,\n ): Promise<\n | { status: \"acquired\" }\n | { status: \"replay\"; result: unknown }\n | { status: \"in_progress\" }\n > {\n try {\n await this.prisma.idempotencyKey.create({\n data: {\n key,\n commandType,\n result: IDEMPOTENCY_IN_PROGRESS_MARKER as any,\n },\n });\n return { status: \"acquired\" };\n } catch (error: any) {\n if (error?.code !== \"P2002\") {\n throw error;\n }\n }\n\n const existing = await this.prisma.idempotencyKey.findUnique({\n where: { key_commandType: { key, commandType } },\n select: { result: true },\n });\n\n if (!existing || isInProgressResult(existing.result)) {\n return { status: \"in_progress\" };\n }\n\n return { status: \"replay\", result: existing.result };\n }\n\n async completeIdempotencyKey(\n key: string,\n commandType: string,\n result: unknown,\n ): Promise<void> {\n await this.prisma.idempotencyKey.update({\n where: { key_commandType: { key, commandType } },\n data: { result: result as any },\n });\n }\n\n async releaseIdempotencyKey(key: string, commandType: string): Promise<void> {\n await this.prisma.idempotencyKey.deleteMany({\n where: { key, commandType },\n });\n }\n\n // ============================================================================\n // Type Mappers\n // ============================================================================\n\n private mapWorkflowRun(run: any): WorkflowRunRecord {\n return {\n id: run.id,\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n workflowId: run.workflowId,\n workflowName: run.workflowName,\n workflowType: run.workflowType,\n status: run.status,\n startedAt: run.startedAt,\n completedAt: run.completedAt,\n duration: run.duration,\n input: run.input,\n output: run.output,\n config: run.config,\n totalCost: run.totalCost,\n totalTokens: run.totalTokens,\n priority: run.priority,\n version: run.version ?? 0,\n };\n }\n\n private mapWorkflowStage(stage: any): WorkflowStageRecord {\n return {\n id: stage.id,\n createdAt: stage.createdAt,\n updatedAt: stage.updatedAt,\n workflowRunId: stage.workflowRunId,\n stageId: stage.stageId,\n stageName: stage.stageName,\n stageNumber: stage.stageNumber,\n executionGroup: stage.executionGroup,\n status: stage.status,\n startedAt: stage.startedAt,\n completedAt: stage.completedAt,\n duration: stage.duration,\n inputData: stage.inputData,\n outputData: stage.outputData,\n config: stage.config,\n suspendedState: stage.suspendedState,\n resumeData: stage.resumeData,\n nextPollAt: stage.nextPollAt,\n pollInterval: stage.pollInterval,\n maxWaitUntil: stage.maxWaitUntil,\n metrics: stage.metrics,\n embeddingInfo: stage.embeddingInfo,\n errorMessage: stage.errorMessage,\n version: stage.version ?? 0,\n };\n }\n\n private mapOutboxEvent(record: any): OutboxRecord {\n return {\n id: record.id,\n workflowRunId: record.workflowRunId,\n sequence: record.sequence,\n eventType: record.eventType,\n payload: record.payload,\n causationId: record.causationId,\n occurredAt: record.occurredAt,\n publishedAt: record.publishedAt,\n retryCount: record.retryCount,\n dlqAt: record.dlqAt,\n };\n }\n\n private mapWorkflowArtifact(artifact: any): WorkflowArtifactRecord {\n return {\n id: artifact.id,\n createdAt: artifact.createdAt,\n updatedAt: artifact.updatedAt,\n workflowRunId: artifact.workflowRunId,\n workflowStageId: artifact.workflowStageId,\n key: artifact.key,\n type: artifact.type,\n data: artifact.data,\n size: artifact.size,\n metadata: artifact.metadata,\n };\n }\n}\n\n/**\n * Factory function to create PrismaWorkflowPersistence\n */\nexport function createPrismaWorkflowPersistence(\n prisma: PrismaClient,\n options?: PrismaWorkflowPersistenceOptions,\n): PrismaWorkflowPersistence {\n return new PrismaWorkflowPersistence(prisma, options);\n}\n"]}
@@ -0,0 +1,17 @@
1
+ // src/persistence/interface.ts
2
+ var StaleVersionError = class extends Error {
3
+ constructor(entity, id, expected, actual) {
4
+ super(
5
+ `Stale version on ${entity} ${id}: expected ${expected}, got ${actual}`
6
+ );
7
+ this.entity = entity;
8
+ this.id = id;
9
+ this.expected = expected;
10
+ this.actual = actual;
11
+ this.name = "StaleVersionError";
12
+ }
13
+ };
14
+
15
+ export { StaleVersionError };
16
+ //# sourceMappingURL=chunk-SPXBCZLB.js.map
17
+ //# sourceMappingURL=chunk-SPXBCZLB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/persistence/interface.ts"],"names":[],"mappings":";AAkDO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAC3C,WAAA,CACkB,MAAA,EACA,EAAA,EACA,QAAA,EACA,MAAA,EAChB;AACA,IAAA,KAAA;AAAA,MACE,oBAAoB,MAAM,CAAA,CAAA,EAAI,EAAE,CAAA,WAAA,EAAc,QAAQ,SAAS,MAAM,CAAA;AAAA,KACvE;AAPgB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAKhB,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF","file":"chunk-SPXBCZLB.js","sourcesContent":["/**\n * Persistence Interfaces for Workflow Engine\n *\n * These interfaces abstract database operations to enable:\n * - Testing with mock implementations\n * - Future extraction into @bratsos/workflow-engine package\n * - Alternative database backends\n *\n * Implementations:\n * - PrismaWorkflowPersistence (default, in ./prisma/)\n * - InMemoryPersistence (for testing)\n */\n\n// ============================================================================\n// Unified Status Type\n// ============================================================================\n\n/**\n * Unified status type for workflows, stages, and jobs.\n *\n * - PENDING: Not started yet\n * - RUNNING: Currently executing\n * - SUSPENDED: Paused, waiting for external event (e.g., batch job completion)\n * - COMPLETED: Finished successfully\n * - FAILED: Finished with error\n * - CANCELLED: Manually stopped by user\n * - SKIPPED: Stage-specific - bypassed due to condition\n */\nexport type Status =\n | \"PENDING\"\n | \"RUNNING\"\n | \"SUSPENDED\"\n | \"COMPLETED\"\n | \"FAILED\"\n | \"CANCELLED\"\n | \"SKIPPED\";\n\n/** @deprecated Use Status instead */\nexport type WorkflowStatus = Status;\n\n/** @deprecated Use Status instead */\nexport type WorkflowStageStatus = Status;\n\n/** @deprecated Use Status instead. Note: PROCESSING is now RUNNING. */\nexport type JobStatus = Status;\n\nexport type LogLevel = \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\n\nexport type ArtifactType = \"STAGE_OUTPUT\" | \"ARTIFACT\" | \"METADATA\";\n\nexport class StaleVersionError extends Error {\n constructor(\n public readonly entity: string,\n public readonly id: string,\n public readonly expected: number,\n public readonly actual: number,\n ) {\n super(\n `Stale version on ${entity} ${id}: expected ${expected}, got ${actual}`,\n );\n this.name = \"StaleVersionError\";\n }\n}\n\n// ============================================================================\n// Record Types (minimal fields needed by the workflow engine)\n// ============================================================================\n\nexport interface WorkflowRunRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n version: number;\n workflowId: string;\n workflowName: string;\n workflowType: string;\n status: WorkflowStatus;\n startedAt: Date | null;\n completedAt: Date | null;\n duration: number | null;\n input: unknown;\n output: unknown | null;\n config: unknown;\n totalCost: number;\n totalTokens: number;\n priority: number;\n}\n\nexport interface WorkflowStageRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n version: number;\n workflowRunId: string;\n stageId: string;\n stageName: string;\n stageNumber: number;\n executionGroup: number;\n status: WorkflowStageStatus;\n startedAt: Date | null;\n completedAt: Date | null;\n duration: number | null;\n inputData: unknown | null;\n outputData: unknown | null;\n config: unknown | null;\n suspendedState: unknown | null;\n resumeData: unknown | null;\n nextPollAt: Date | null;\n pollInterval: number | null;\n maxWaitUntil: Date | null;\n metrics: unknown | null;\n embeddingInfo: unknown | null;\n errorMessage: string | null;\n}\n\nexport interface WorkflowLogRecord {\n id: string;\n createdAt: Date;\n workflowStageId: string | null;\n workflowRunId: string | null;\n level: LogLevel;\n message: string;\n metadata: unknown | null;\n}\n\nexport interface WorkflowArtifactRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n workflowRunId: string;\n workflowStageId: string | null;\n key: string;\n type: ArtifactType;\n data: unknown;\n size: number;\n metadata: unknown | null;\n}\n\n// ============================================================================\n// Outbox and Idempotency Record Types (for kernel transactional outbox)\n// ============================================================================\n\nexport interface OutboxRecord {\n id: string;\n workflowRunId: string;\n sequence: number;\n eventType: string;\n payload: unknown;\n causationId: string;\n occurredAt: Date;\n publishedAt: Date | null;\n retryCount: number;\n dlqAt: Date | null;\n}\n\nexport interface CreateOutboxEventInput {\n workflowRunId: string;\n eventType: string;\n payload: unknown;\n causationId: string;\n occurredAt: Date;\n}\n\nexport interface IdempotencyRecord {\n key: string;\n commandType: string;\n result: unknown;\n createdAt: Date;\n}\n\n// ============================================================================\n// AI Call Record Types\n// ============================================================================\n\nexport interface AICallRecord {\n id: string;\n createdAt: Date;\n topic: string;\n callType: string;\n modelKey: string;\n modelId: string;\n prompt: string;\n response: string;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n metadata: unknown | null;\n}\n\nexport interface JobRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n status: JobStatus;\n priority: number;\n workerId: string | null;\n lockedAt: Date | null;\n startedAt: Date | null;\n completedAt: Date | null;\n attempt: number;\n maxAttempts: number;\n lastError: string | null;\n nextPollAt: Date | null;\n payload: Record<string, unknown>;\n}\n\n// ============================================================================\n// Input Types (for creating/updating records)\n// ============================================================================\n\nexport interface CreateRunInput {\n id?: string;\n workflowId: string;\n workflowName: string;\n workflowType: string;\n input: unknown;\n config?: unknown;\n priority?: number;\n /** Optional metadata for domain-specific fields */\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateRunInput {\n status?: WorkflowStatus;\n startedAt?: Date;\n completedAt?: Date | null;\n duration?: number;\n output?: unknown;\n totalCost?: number;\n totalTokens?: number;\n expectedVersion?: number;\n}\n\nexport interface CreateStageInput {\n workflowRunId: string;\n stageId: string;\n stageName: string;\n stageNumber: number;\n executionGroup: number;\n status?: WorkflowStageStatus;\n startedAt?: Date;\n config?: unknown;\n inputData?: unknown;\n}\n\nexport interface UpdateStageInput {\n status?: WorkflowStageStatus;\n startedAt?: Date;\n completedAt?: Date;\n duration?: number;\n outputData?: unknown;\n config?: unknown;\n suspendedState?: unknown;\n resumeData?: unknown;\n nextPollAt?: Date | null;\n pollInterval?: number;\n maxWaitUntil?: Date;\n metrics?: unknown;\n embeddingInfo?: unknown;\n artifacts?: unknown;\n errorMessage?: string;\n expectedVersion?: number;\n}\n\nexport interface UpsertStageInput {\n workflowRunId: string;\n stageId: string;\n create: CreateStageInput;\n update: UpdateStageInput;\n}\n\nexport interface CreateLogInput {\n workflowRunId?: string;\n workflowStageId?: string;\n level: LogLevel;\n message: string;\n metadata?: unknown;\n}\n\nexport interface SaveArtifactInput {\n workflowRunId: string;\n workflowStageId?: string;\n key: string;\n type: ArtifactType;\n data: unknown;\n size: number;\n metadata?: unknown;\n}\n\nexport interface CreateAICallInput {\n topic: string;\n callType: string;\n modelKey: string;\n modelId: string;\n prompt: string;\n response: string;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n metadata?: unknown;\n}\n\nexport interface EnqueueJobInput {\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n priority?: number;\n payload?: Record<string, unknown>;\n scheduledFor?: Date;\n}\n\nexport interface DequeueResult {\n jobId: string;\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n priority: number;\n attempt: number;\n maxAttempts: number;\n payload: Record<string, unknown>;\n}\n\n// ============================================================================\n// WorkflowPersistence Interface\n// ============================================================================\n\nexport interface WorkflowPersistence {\n /** Execute operations within a transaction boundary. */\n withTransaction<T>(fn: (tx: WorkflowPersistence) => Promise<T>): Promise<T>;\n\n // WorkflowRun operations\n createRun(data: CreateRunInput): Promise<WorkflowRunRecord>;\n updateRun(id: string, data: UpdateRunInput): Promise<void>;\n getRun(id: string): Promise<WorkflowRunRecord | null>;\n getRunStatus(id: string): Promise<WorkflowStatus | null>;\n getRunsByStatus(status: WorkflowStatus): Promise<WorkflowRunRecord[]>;\n\n /**\n * Atomically claim a pending workflow run for processing.\n * Uses atomic update with WHERE status = 'PENDING' to prevent race conditions.\n *\n * @param id - The workflow run ID to claim\n * @returns true if successfully claimed, false if already claimed by another worker\n */\n claimPendingRun(id: string): Promise<boolean>;\n\n /**\n * Atomically find and claim the next pending workflow run.\n * Uses FOR UPDATE SKIP LOCKED pattern (in Postgres) to prevent race conditions\n * when multiple workers try to claim workflows simultaneously.\n *\n * Priority ordering: higher priority first, then oldest (FIFO within same priority).\n *\n * @returns The claimed workflow run (now with status RUNNING), or null if no pending runs\n */\n claimNextPendingRun(): Promise<WorkflowRunRecord | null>;\n\n // WorkflowStage operations\n createStage(data: CreateStageInput): Promise<WorkflowStageRecord>;\n upsertStage(data: UpsertStageInput): Promise<WorkflowStageRecord>;\n updateStage(id: string, data: UpdateStageInput): Promise<void>;\n updateStageByRunAndStageId(\n workflowRunId: string,\n stageId: string,\n data: UpdateStageInput,\n ): Promise<void>;\n getStage(runId: string, stageId: string): Promise<WorkflowStageRecord | null>;\n getStageById(id: string): Promise<WorkflowStageRecord | null>;\n getStagesByRun(\n runId: string,\n options?: { status?: WorkflowStageStatus; orderBy?: \"asc\" | \"desc\" },\n ): Promise<WorkflowStageRecord[]>;\n getSuspendedStages(beforeDate: Date): Promise<WorkflowStageRecord[]>;\n getFirstSuspendedStageReadyToResume(\n runId: string,\n ): Promise<WorkflowStageRecord | null>;\n getFirstFailedStage(runId: string): Promise<WorkflowStageRecord | null>;\n getLastCompletedStage(runId: string): Promise<WorkflowStageRecord | null>;\n getLastCompletedStageBefore(\n runId: string,\n executionGroup: number,\n ): Promise<WorkflowStageRecord | null>;\n deleteStage(id: string): Promise<void>;\n\n // WorkflowLog operations\n createLog(data: CreateLogInput): Promise<void>;\n\n // WorkflowArtifact operations (for StageStorage)\n saveArtifact(data: SaveArtifactInput): Promise<void>;\n loadArtifact(runId: string, key: string): Promise<unknown>;\n hasArtifact(runId: string, key: string): Promise<boolean>;\n deleteArtifact(runId: string, key: string): Promise<void>;\n listArtifacts(runId: string): Promise<WorkflowArtifactRecord[]>;\n getStageIdForArtifact(runId: string, stageId: string): Promise<string | null>;\n\n // Stage output convenience methods (replaces separate StageStorage)\n saveStageOutput(\n runId: string,\n workflowType: string,\n stageId: string,\n output: unknown,\n ): Promise<string>;\n\n // Outbox DLQ operations\n /** Increment retry count for a failed outbox event. Returns new count. */\n incrementOutboxRetryCount(id: string): Promise<number>;\n\n /** Move an outbox event to DLQ (sets dlqAt). */\n moveOutboxEventToDLQ(id: string): Promise<void>;\n\n /** Reset DLQ events so they can be reprocessed by outbox.flush. Returns count reset. */\n replayDLQEvents(maxEvents: number): Promise<number>;\n\n // Outbox operations\n /** Write events to the outbox. Sequences are auto-assigned per workflowRunId. */\n appendOutboxEvents(events: CreateOutboxEventInput[]): Promise<void>;\n\n /** Read unpublished events ordered by (workflowRunId, sequence). */\n getUnpublishedOutboxEvents(limit?: number): Promise<OutboxRecord[]>;\n\n /** Mark events as published. */\n markOutboxEventsPublished(ids: string[]): Promise<void>;\n\n // Idempotency operations\n /** Atomically acquire an idempotency key for command execution. */\n acquireIdempotencyKey(\n key: string,\n commandType: string,\n ): Promise<\n | { status: \"acquired\" }\n | { status: \"replay\"; result: unknown }\n | { status: \"in_progress\" }\n >;\n\n /** Mark an idempotency key as completed and cache the command result. */\n completeIdempotencyKey(\n key: string,\n commandType: string,\n result: unknown,\n ): Promise<void>;\n\n /** Release an in-progress idempotency key after command failure. */\n releaseIdempotencyKey(key: string, commandType: string): Promise<void>;\n}\n\n// ============================================================================\n// AICallLogger Interface\n// ============================================================================\n\nexport interface AIHelperStats {\n totalCalls: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCost: number;\n perModel: Record<\n string,\n { calls: number; inputTokens: number; outputTokens: number; cost: number }\n >;\n}\n\nexport interface AICallLogger {\n /**\n * Log a single AI call (fire and forget)\n */\n logCall(call: CreateAICallInput): void;\n\n /**\n * Log batch results (for recording batch API results)\n */\n logBatchResults(batchId: string, results: CreateAICallInput[]): Promise<void>;\n\n /**\n * Get aggregated stats for a topic prefix\n */\n getStats(topicPrefix: string): Promise<AIHelperStats>;\n\n /**\n * Check if batch results are already recorded\n */\n isRecorded(batchId: string): Promise<boolean>;\n}\n\n// ============================================================================\n// JobQueue Interface\n// ============================================================================\n\nexport interface JobQueue {\n /**\n * Add a new job to the queue\n */\n enqueue(options: EnqueueJobInput): Promise<string>;\n\n /**\n * Enqueue multiple stages in parallel (same execution group)\n */\n enqueueParallel(jobs: EnqueueJobInput[]): Promise<string[]>;\n\n /**\n * Atomically dequeue the next available job\n */\n dequeue(): Promise<DequeueResult | null>;\n\n /**\n * Mark job as completed\n */\n complete(jobId: string): Promise<void>;\n\n /**\n * Mark job as suspended (for async-batch)\n */\n suspend(jobId: string, nextPollAt: Date): Promise<void>;\n\n /**\n * Mark job as failed\n */\n fail(jobId: string, error: string, shouldRetry?: boolean): Promise<void>;\n\n /**\n * Get suspended jobs that are ready to be checked\n */\n getSuspendedJobsReadyToPoll(): Promise<\n Array<{ jobId: string; stageId: string; workflowRunId: string }>\n >;\n\n /**\n * Release stale locks (for crashed workers)\n */\n releaseStaleJobs(staleThresholdMs?: number): Promise<number>;\n}\n\n// ============================================================================\n// Default Implementations (lazy loaded to avoid circular deps)\n// ============================================================================\n\n// Re-export from prisma implementations for convenience\n// These will be the default implementations used when no custom persistence is provided\n"]}