@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
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Core type definitions for Workflow System v2
5
+ *
6
+ * See WORKFLOW_SYSTEM_PROPOSAL.md for full architectural details
7
+ */
8
+
9
+ interface ProgressUpdate {
10
+ stageId: string;
11
+ stageName: string;
12
+ progress: number;
13
+ message: string;
14
+ details?: Record<string, unknown>;
15
+ }
16
+ interface StageMetrics {
17
+ startTime: number;
18
+ endTime: number;
19
+ duration: number;
20
+ itemsProcessed?: number;
21
+ itemsProduced?: number;
22
+ aiCalls?: number;
23
+ totalTokens?: number;
24
+ totalCost?: number;
25
+ }
26
+ interface EmbeddingResult {
27
+ id: string;
28
+ content: string;
29
+ embedding: number[];
30
+ similarity?: number;
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+ interface EmbeddingInfo {
34
+ model: string;
35
+ dimensions: number;
36
+ results: EmbeddingResult[];
37
+ totalProcessed?: number;
38
+ averageSimilarity?: number;
39
+ }
40
+ interface StageResult<TOutput> {
41
+ output: TOutput;
42
+ metrics: StageMetrics;
43
+ artifacts?: Record<string, unknown>;
44
+ embeddings?: EmbeddingInfo;
45
+ }
46
+ declare const SuspendedStateSchema: z.ZodObject<{
47
+ batchId: z.ZodString;
48
+ statusUrl: z.ZodOptional<z.ZodString>;
49
+ apiKey: z.ZodOptional<z.ZodString>;
50
+ submittedAt: z.ZodString;
51
+ pollInterval: z.ZodNumber;
52
+ maxWaitTime: z.ZodNumber;
53
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
54
+ }, z.core.$strip>;
55
+ interface SuspendedResult {
56
+ suspended: true;
57
+ state: z.infer<typeof SuspendedStateSchema>;
58
+ pollConfig: {
59
+ pollInterval: number;
60
+ maxWaitTime: number;
61
+ nextPollAt: Date;
62
+ };
63
+ metrics: StageMetrics;
64
+ }
65
+ interface CompletionCheckResult<TOutput> {
66
+ ready: boolean;
67
+ output?: TOutput;
68
+ error?: string;
69
+ nextCheckIn?: number;
70
+ metrics?: StageMetrics;
71
+ embeddings?: EmbeddingInfo;
72
+ }
73
+ type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR";
74
+ type StageMode = "sync" | "async-batch";
75
+
76
+ /**
77
+ * Stage interface and context definitions
78
+ *
79
+ * Stages are the building blocks of workflows. Each stage:
80
+ * - Has strongly-typed input, output, and config schemas (Zod)
81
+ * - Can be sync or async-batch
82
+ * - Has access to AI helper, storage, and logging via context
83
+ * - Can suspend workflow for long-running batch operations
84
+ */
85
+
86
+ interface StageContext<TInput, TConfig, TWorkflowContext = Record<string, unknown>> {
87
+ workflowRunId: string;
88
+ stageId: string;
89
+ stageNumber: number;
90
+ stageName: string;
91
+ /** Database record ID for this stage execution (for logging to persistence) */
92
+ stageRecordId?: string;
93
+ input: TInput;
94
+ config: TConfig;
95
+ resumeState?: z.infer<typeof SuspendedStateSchema>;
96
+ onProgress: (update: ProgressUpdate) => void;
97
+ onLog: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void;
98
+ log: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void;
99
+ storage: StageStorage;
100
+ workflowContext: Partial<TWorkflowContext>;
101
+ }
102
+ interface StageStorage {
103
+ save<T>(key: string, data: T): Promise<void>;
104
+ load<T>(key: string): Promise<T>;
105
+ exists(key: string): Promise<boolean>;
106
+ delete(key: string): Promise<void>;
107
+ getStageKey(stageId: string, suffix?: string): string;
108
+ }
109
+ /**
110
+ * Context passed to checkCompletion for async-batch stages.
111
+ * Includes identification info so stages don't need to store it in metadata.
112
+ */
113
+ interface CheckCompletionContext<TConfig> {
114
+ workflowRunId: string;
115
+ stageId: string;
116
+ /** Database record ID for this stage execution (for logging to persistence) */
117
+ stageRecordId?: string;
118
+ config: TConfig;
119
+ onLog: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void;
120
+ log: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void;
121
+ storage: StageStorage;
122
+ }
123
+ interface Stage<TInput extends z.ZodTypeAny, TOutput extends z.ZodTypeAny, TConfig extends z.ZodTypeAny, TWorkflowContext = Record<string, unknown>> {
124
+ id: string;
125
+ name: string;
126
+ description?: string;
127
+ /**
128
+ * Optional: List of stage IDs that this stage depends on.
129
+ * The workflow builder will validate that all dependencies are present
130
+ * in the workflow before this stage is executed.
131
+ *
132
+ * Example: dependencies: ["data-extraction", "guidelines"]
133
+ */
134
+ dependencies?: string[];
135
+ inputSchema: TInput;
136
+ outputSchema: TOutput;
137
+ configSchema: TConfig;
138
+ execute: (context: StageContext<z.infer<TInput>, z.infer<TConfig>, TWorkflowContext>) => Promise<StageResult<z.infer<TOutput>> | SuspendedResult>;
139
+ checkCompletion?: (suspendedState: z.infer<typeof SuspendedStateSchema>, context: CheckCompletionContext<z.infer<TConfig>>) => Promise<CompletionCheckResult<z.infer<TOutput>>>;
140
+ mode?: StageMode;
141
+ estimateCost?: (input: z.infer<TInput>, config: z.infer<TConfig>) => number;
142
+ }
143
+
144
+ export { type CheckCompletionContext as C, type LogLevel as L, type Stage as S, type StageResult as a, type StageContext as b, SuspendedStateSchema as c, type CompletionCheckResult as d };
@@ -1,4 +1,4 @@
1
- import { A as AICallLogger, C as CreateAICallInput, a as AIHelperStats, b as AICallRecord, J as JobQueue, E as EnqueueJobInput, D as DequeueResult, c as JobRecord, d as JobStatus, W as WorkflowPersistence, e as CreateRunInput, f as WorkflowRunRecord, U as UpdateRunInput, g as WorkflowStatus, h as CreateStageInput, i as WorkflowStageRecord, j as UpsertStageInput, k as UpdateStageInput, l as WorkflowStageStatus, m as CreateLogInput, S as SaveArtifactInput, n as WorkflowArtifactRecord, o as WorkflowLogRecord } from '../interface-Cv22wvLG.js';
1
+ import { A as AICallLogger, g as CreateAICallInput, h as AIHelperStats, i as AICallRecord, J as JobQueue, E as EnqueueJobInput, D as DequeueResult, j as JobRecord, k as JobStatus, l as WorkflowPersistence, a as CreateRunInput, W as WorkflowRunRecord, U as UpdateRunInput, m as WorkflowStatus, b as CreateStageInput, c as WorkflowStageRecord, d as UpsertStageInput, e as UpdateStageInput, n as WorkflowStageStatus, f as CreateLogInput, C as CreateOutboxEventInput, O as OutboxRecord, o as SaveArtifactInput, p as WorkflowArtifactRecord, q as WorkflowLogRecord } from '../interface-MMqhfQQK.js';
2
2
 
3
3
  /**
4
4
  * In-Memory AI Call Logger
@@ -184,8 +184,14 @@ declare class InMemoryWorkflowPersistence implements WorkflowPersistence {
184
184
  private stages;
185
185
  private logs;
186
186
  private artifacts;
187
+ private outbox;
188
+ private idempotencyKeys;
189
+ private idempotencyInProgress;
190
+ private outboxSequences;
187
191
  private stageKey;
188
192
  private artifactKey;
193
+ private idempotencyCompositeKey;
194
+ withTransaction<T>(fn: (tx: WorkflowPersistence) => Promise<T>): Promise<T>;
189
195
  createRun(data: CreateRunInput): Promise<WorkflowRunRecord>;
190
196
  updateRun(id: string, data: UpdateRunInput): Promise<void>;
191
197
  getRun(id: string): Promise<WorkflowRunRecord | null>;
@@ -210,6 +216,22 @@ declare class InMemoryWorkflowPersistence implements WorkflowPersistence {
210
216
  getLastCompletedStageBefore(runId: string, executionGroup: number): Promise<WorkflowStageRecord | null>;
211
217
  deleteStage(id: string): Promise<void>;
212
218
  createLog(data: CreateLogInput): Promise<void>;
219
+ appendOutboxEvents(events: CreateOutboxEventInput[]): Promise<void>;
220
+ getUnpublishedOutboxEvents(limit?: number): Promise<OutboxRecord[]>;
221
+ markOutboxEventsPublished(ids: string[]): Promise<void>;
222
+ incrementOutboxRetryCount(id: string): Promise<number>;
223
+ moveOutboxEventToDLQ(id: string): Promise<void>;
224
+ replayDLQEvents(maxEvents: number): Promise<number>;
225
+ acquireIdempotencyKey(key: string, commandType: string): Promise<{
226
+ status: "acquired";
227
+ } | {
228
+ status: "replay";
229
+ result: unknown;
230
+ } | {
231
+ status: "in_progress";
232
+ }>;
233
+ completeIdempotencyKey(key: string, commandType: string, result: unknown): Promise<void>;
234
+ releaseIdempotencyKey(key: string, commandType: string): Promise<void>;
213
235
  saveArtifact(data: SaveArtifactInput): Promise<void>;
214
236
  loadArtifact(runId: string, key: string): Promise<unknown>;
215
237
  hasArtifact(runId: string, key: string): Promise<boolean>;
@@ -1,6 +1,6 @@
1
+ import { StaleVersionError } from '../chunk-SPXBCZLB.js';
1
2
  import { randomUUID } from 'crypto';
2
3
 
3
- // src/testing/in-memory-ai-logger.ts
4
4
  var InMemoryAICallLogger = class {
5
5
  calls = /* @__PURE__ */ new Map();
6
6
  recordedBatches = /* @__PURE__ */ new Set();
@@ -190,6 +190,7 @@ var InMemoryJobQueue = class {
190
190
  createdAt: now,
191
191
  updatedAt: now,
192
192
  workflowRunId: options.workflowRunId,
193
+ workflowId: options.workflowId,
193
194
  stageId: options.stageId,
194
195
  status: "PENDING",
195
196
  priority: options.priority ?? 5,
@@ -240,9 +241,11 @@ var InMemoryJobQueue = class {
240
241
  return {
241
242
  jobId: job.id,
242
243
  workflowRunId: job.workflowRunId,
244
+ workflowId: job.workflowId,
243
245
  stageId: job.stageId,
244
246
  priority: job.priority,
245
247
  attempt: job.attempt,
248
+ maxAttempts: job.maxAttempts,
246
249
  payload: job.payload
247
250
  };
248
251
  }
@@ -426,6 +429,10 @@ var InMemoryWorkflowPersistence = class {
426
429
  stages = /* @__PURE__ */ new Map();
427
430
  logs = /* @__PURE__ */ new Map();
428
431
  artifacts = /* @__PURE__ */ new Map();
432
+ outbox = [];
433
+ idempotencyKeys = /* @__PURE__ */ new Map();
434
+ idempotencyInProgress = /* @__PURE__ */ new Set();
435
+ outboxSequences = /* @__PURE__ */ new Map();
429
436
  // Helper to generate composite keys for stages
430
437
  stageKey(runId, stageId) {
431
438
  return `${runId}:${stageId}`;
@@ -434,6 +441,12 @@ var InMemoryWorkflowPersistence = class {
434
441
  artifactKey(runId, key) {
435
442
  return `${runId}:${key}`;
436
443
  }
444
+ idempotencyCompositeKey(commandType, key) {
445
+ return `${commandType}:${key}`;
446
+ }
447
+ async withTransaction(fn) {
448
+ return fn(this);
449
+ }
437
450
  // ============================================================================
438
451
  // WorkflowRun Operations
439
452
  // ============================================================================
@@ -443,6 +456,7 @@ var InMemoryWorkflowPersistence = class {
443
456
  id: data.id ?? randomUUID(),
444
457
  createdAt: now,
445
458
  updatedAt: now,
459
+ version: 1,
446
460
  workflowId: data.workflowId,
447
461
  workflowName: data.workflowName,
448
462
  workflowType: data.workflowType,
@@ -465,10 +479,20 @@ var InMemoryWorkflowPersistence = class {
465
479
  if (!run) {
466
480
  throw new Error(`WorkflowRun not found: ${id}`);
467
481
  }
482
+ if (data.expectedVersion !== void 0 && run.version !== data.expectedVersion) {
483
+ throw new StaleVersionError(
484
+ "WorkflowRun",
485
+ id,
486
+ data.expectedVersion,
487
+ run.version
488
+ );
489
+ }
490
+ const { expectedVersion: _, ...rest } = data;
468
491
  const updated = {
469
492
  ...run,
470
- ...data,
471
- updatedAt: /* @__PURE__ */ new Date()
493
+ ...rest,
494
+ updatedAt: /* @__PURE__ */ new Date(),
495
+ version: run.version + 1
472
496
  };
473
497
  this.runs.set(id, updated);
474
498
  }
@@ -492,7 +516,8 @@ var InMemoryWorkflowPersistence = class {
492
516
  ...run,
493
517
  status: "RUNNING",
494
518
  startedAt: /* @__PURE__ */ new Date(),
495
- updatedAt: /* @__PURE__ */ new Date()
519
+ updatedAt: /* @__PURE__ */ new Date(),
520
+ version: run.version + 1
496
521
  };
497
522
  this.runs.set(id, updated);
498
523
  return true;
@@ -516,7 +541,8 @@ var InMemoryWorkflowPersistence = class {
516
541
  ...currentRun,
517
542
  status: "RUNNING",
518
543
  startedAt: /* @__PURE__ */ new Date(),
519
- updatedAt: /* @__PURE__ */ new Date()
544
+ updatedAt: /* @__PURE__ */ new Date(),
545
+ version: currentRun.version + 1
520
546
  };
521
547
  this.runs.set(claimed.id, claimed);
522
548
  return { ...claimed };
@@ -531,6 +557,7 @@ var InMemoryWorkflowPersistence = class {
531
557
  id,
532
558
  createdAt: now,
533
559
  updatedAt: now,
560
+ version: 1,
534
561
  workflowRunId: data.workflowRunId,
535
562
  stageId: data.stageId,
536
563
  stageName: data.stageName,
@@ -563,7 +590,8 @@ var InMemoryWorkflowPersistence = class {
563
590
  const updated = {
564
591
  ...existing,
565
592
  ...data.update,
566
- updatedAt: /* @__PURE__ */ new Date()
593
+ updatedAt: /* @__PURE__ */ new Date(),
594
+ version: existing.version + 1
567
595
  };
568
596
  this.stages.set(existing.id, updated);
569
597
  this.stages.set(key, updated);
@@ -577,10 +605,20 @@ var InMemoryWorkflowPersistence = class {
577
605
  if (!stage) {
578
606
  throw new Error(`WorkflowStage not found: ${id}`);
579
607
  }
608
+ if (data.expectedVersion !== void 0 && stage.version !== data.expectedVersion) {
609
+ throw new StaleVersionError(
610
+ "WorkflowStage",
611
+ id,
612
+ data.expectedVersion,
613
+ stage.version
614
+ );
615
+ }
616
+ const { expectedVersion: _, ...rest } = data;
580
617
  const updated = {
581
618
  ...stage,
582
- ...data,
583
- updatedAt: /* @__PURE__ */ new Date()
619
+ ...rest,
620
+ updatedAt: /* @__PURE__ */ new Date(),
621
+ version: stage.version + 1
584
622
  };
585
623
  this.stages.set(id, updated);
586
624
  this.stages.set(this.stageKey(stage.workflowRunId, stage.stageId), updated);
@@ -591,10 +629,20 @@ var InMemoryWorkflowPersistence = class {
591
629
  if (!stage) {
592
630
  throw new Error(`WorkflowStage not found: ${workflowRunId}/${stageId}`);
593
631
  }
632
+ if (data.expectedVersion !== void 0 && stage.version !== data.expectedVersion) {
633
+ throw new StaleVersionError(
634
+ "WorkflowStage",
635
+ `${workflowRunId}/${stageId}`,
636
+ data.expectedVersion,
637
+ stage.version
638
+ );
639
+ }
640
+ const { expectedVersion: _, ...rest } = data;
594
641
  const updated = {
595
642
  ...stage,
596
- ...data,
597
- updatedAt: /* @__PURE__ */ new Date()
643
+ ...rest,
644
+ updatedAt: /* @__PURE__ */ new Date(),
645
+ version: stage.version + 1
598
646
  };
599
647
  this.stages.set(stage.id, updated);
600
648
  this.stages.set(key, updated);
@@ -626,9 +674,12 @@ var InMemoryWorkflowPersistence = class {
626
674
  return stages.map((s) => ({ ...s }));
627
675
  }
628
676
  async getSuspendedStages(beforeDate) {
629
- return Array.from(this.stages.values()).filter(
630
- (s) => s.status === "SUSPENDED" && s.nextPollAt && s.nextPollAt <= beforeDate && this.stages.get(s.id) === s
631
- ).map((s) => ({ ...s }));
677
+ const seenIds = /* @__PURE__ */ new Set();
678
+ return Array.from(this.stages.values()).filter((s) => {
679
+ if (seenIds.has(s.id)) return false;
680
+ seenIds.add(s.id);
681
+ return s.status === "SUSPENDED" && s.nextPollAt !== null && s.nextPollAt <= beforeDate;
682
+ }).map((s) => ({ ...s }));
632
683
  }
633
684
  async getFirstSuspendedStageReadyToResume(runId) {
634
685
  const stages = await this.getStagesByRun(runId, { status: "SUSPENDED" });
@@ -678,6 +729,94 @@ var InMemoryWorkflowPersistence = class {
678
729
  this.logs.set(record.id, record);
679
730
  }
680
731
  // ============================================================================
732
+ // Outbox Operations
733
+ // ============================================================================
734
+ async appendOutboxEvents(events) {
735
+ for (const event of events) {
736
+ const currentSeq = this.outboxSequences.get(event.workflowRunId) ?? 0;
737
+ const nextSeq = currentSeq + 1;
738
+ this.outboxSequences.set(event.workflowRunId, nextSeq);
739
+ const record = {
740
+ id: randomUUID(),
741
+ workflowRunId: event.workflowRunId,
742
+ sequence: nextSeq,
743
+ eventType: event.eventType,
744
+ payload: event.payload,
745
+ causationId: event.causationId,
746
+ occurredAt: event.occurredAt,
747
+ publishedAt: null,
748
+ retryCount: 0,
749
+ dlqAt: null
750
+ };
751
+ this.outbox.push(record);
752
+ }
753
+ }
754
+ async getUnpublishedOutboxEvents(limit) {
755
+ const effectiveLimit = limit ?? 100;
756
+ return this.outbox.filter((r) => r.publishedAt === null && r.dlqAt === null).sort((a, b) => {
757
+ const runCmp = a.workflowRunId.localeCompare(b.workflowRunId);
758
+ if (runCmp !== 0) return runCmp;
759
+ return a.sequence - b.sequence;
760
+ }).slice(0, effectiveLimit).map((r) => ({ ...r }));
761
+ }
762
+ async markOutboxEventsPublished(ids) {
763
+ const idSet = new Set(ids);
764
+ for (const record of this.outbox) {
765
+ if (idSet.has(record.id)) {
766
+ record.publishedAt = /* @__PURE__ */ new Date();
767
+ }
768
+ }
769
+ }
770
+ async incrementOutboxRetryCount(id) {
771
+ const record = this.outbox.find((r) => r.id === id);
772
+ if (!record) throw new Error(`Outbox event not found: ${id}`);
773
+ record.retryCount++;
774
+ return record.retryCount;
775
+ }
776
+ async moveOutboxEventToDLQ(id) {
777
+ const record = this.outbox.find((r) => r.id === id);
778
+ if (!record) throw new Error(`Outbox event not found: ${id}`);
779
+ record.dlqAt = /* @__PURE__ */ new Date();
780
+ }
781
+ async replayDLQEvents(maxEvents) {
782
+ const dlqEvents = this.outbox.filter((r) => r.dlqAt !== null).slice(0, maxEvents);
783
+ for (const record of dlqEvents) {
784
+ record.dlqAt = null;
785
+ record.retryCount = 0;
786
+ }
787
+ return dlqEvents.length;
788
+ }
789
+ // ============================================================================
790
+ // Idempotency Operations
791
+ // ============================================================================
792
+ async acquireIdempotencyKey(key, commandType) {
793
+ const compositeKey = this.idempotencyCompositeKey(commandType, key);
794
+ const record = this.idempotencyKeys.get(compositeKey);
795
+ if (record) {
796
+ return { status: "replay", result: record.result };
797
+ }
798
+ if (this.idempotencyInProgress.has(compositeKey)) {
799
+ return { status: "in_progress" };
800
+ }
801
+ this.idempotencyInProgress.add(compositeKey);
802
+ return { status: "acquired" };
803
+ }
804
+ async completeIdempotencyKey(key, commandType, result) {
805
+ const compositeKey = this.idempotencyCompositeKey(commandType, key);
806
+ this.idempotencyInProgress.delete(compositeKey);
807
+ this.idempotencyKeys.set(compositeKey, {
808
+ key,
809
+ commandType,
810
+ result,
811
+ createdAt: /* @__PURE__ */ new Date()
812
+ });
813
+ }
814
+ async releaseIdempotencyKey(key, commandType) {
815
+ this.idempotencyInProgress.delete(
816
+ this.idempotencyCompositeKey(commandType, key)
817
+ );
818
+ }
819
+ // ============================================================================
681
820
  // WorkflowArtifact Operations
682
821
  // ============================================================================
683
822
  async saveArtifact(data) {
@@ -745,6 +884,10 @@ var InMemoryWorkflowPersistence = class {
745
884
  this.stages.clear();
746
885
  this.logs.clear();
747
886
  this.artifacts.clear();
887
+ this.outbox = [];
888
+ this.idempotencyKeys.clear();
889
+ this.idempotencyInProgress.clear();
890
+ this.outboxSequences.clear();
748
891
  }
749
892
  /**
750
893
  * Get all runs for inspection