@bratsos/workflow-engine 0.0.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +270 -513
  2. package/dist/chunk-D7RVRRM2.js +3 -0
  3. package/dist/chunk-D7RVRRM2.js.map +1 -0
  4. package/dist/chunk-HL3OJG7W.js +1033 -0
  5. package/dist/chunk-HL3OJG7W.js.map +1 -0
  6. package/dist/chunk-MUWP5SF2.js +33 -0
  7. package/dist/chunk-MUWP5SF2.js.map +1 -0
  8. package/dist/chunk-NYKMT46J.js +1143 -0
  9. package/dist/chunk-NYKMT46J.js.map +1 -0
  10. package/dist/chunk-P4KMGCT3.js +2292 -0
  11. package/dist/chunk-P4KMGCT3.js.map +1 -0
  12. package/dist/chunk-SPXBCZLB.js +17 -0
  13. package/dist/chunk-SPXBCZLB.js.map +1 -0
  14. package/dist/cli/sync-models.d.ts +1 -0
  15. package/dist/cli/sync-models.js +210 -0
  16. package/dist/cli/sync-models.js.map +1 -0
  17. package/dist/client-D4PoxADF.d.ts +798 -0
  18. package/dist/client.d.ts +5 -0
  19. package/dist/client.js +4 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/index-DAzCfO1R.d.ts +217 -0
  22. package/dist/index.d.ts +569 -0
  23. package/dist/index.js +399 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/interface-MMqhfQQK.d.ts +411 -0
  26. package/dist/kernel/index.d.ts +26 -0
  27. package/dist/kernel/index.js +3 -0
  28. package/dist/kernel/index.js.map +1 -0
  29. package/dist/kernel/testing/index.d.ts +44 -0
  30. package/dist/kernel/testing/index.js +85 -0
  31. package/dist/kernel/testing/index.js.map +1 -0
  32. package/dist/persistence/index.d.ts +2 -0
  33. package/dist/persistence/index.js +6 -0
  34. package/dist/persistence/index.js.map +1 -0
  35. package/dist/persistence/prisma/index.d.ts +37 -0
  36. package/dist/persistence/prisma/index.js +5 -0
  37. package/dist/persistence/prisma/index.js.map +1 -0
  38. package/dist/plugins-BCnDUwIc.d.ts +415 -0
  39. package/dist/ports-tU3rzPXJ.d.ts +245 -0
  40. package/dist/stage-BPw7m9Wx.d.ts +144 -0
  41. package/dist/testing/index.d.ts +264 -0
  42. package/dist/testing/index.js +920 -0
  43. package/dist/testing/index.js.map +1 -0
  44. package/package.json +11 -1
  45. package/skills/workflow-engine/SKILL.md +234 -348
  46. package/skills/workflow-engine/references/03-runtime-setup.md +111 -426
  47. package/skills/workflow-engine/references/05-persistence-setup.md +32 -0
  48. package/skills/workflow-engine/references/07-testing-patterns.md +141 -474
  49. package/skills/workflow-engine/references/08-common-patterns.md +118 -431
@@ -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 };
@@ -0,0 +1,264 @@
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
+
3
+ /**
4
+ * In-Memory AI Call Logger
5
+ *
6
+ * A complete in-memory implementation of AICallLogger for testing.
7
+ * Tracks all AI calls with full cost/token tracking and topic-based aggregation.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { InMemoryAICallLogger } from '@bratsos/workflow-engine/testing';
12
+ *
13
+ * const aiLogger = new InMemoryAICallLogger();
14
+ * // Use in tests...
15
+ * aiLogger.clear(); // Reset between tests
16
+ * ```
17
+ */
18
+
19
+ declare class InMemoryAICallLogger implements AICallLogger {
20
+ private calls;
21
+ private recordedBatches;
22
+ /**
23
+ * Log a single AI call (fire and forget)
24
+ */
25
+ logCall(call: CreateAICallInput): void;
26
+ /**
27
+ * Log batch results (for recording batch API results)
28
+ */
29
+ logBatchResults(batchId: string, results: CreateAICallInput[]): Promise<void>;
30
+ /**
31
+ * Get aggregated stats for a topic prefix
32
+ */
33
+ getStats(topicPrefix: string): Promise<AIHelperStats>;
34
+ /**
35
+ * Check if batch results are already recorded
36
+ */
37
+ isRecorded(batchId: string): Promise<boolean>;
38
+ /**
39
+ * Clear all data - useful between tests
40
+ */
41
+ clear(): void;
42
+ /**
43
+ * Get all calls for inspection
44
+ */
45
+ getAllCalls(): AICallRecord[];
46
+ /**
47
+ * Get calls by topic for inspection
48
+ */
49
+ getCallsByTopic(topic: string): AICallRecord[];
50
+ /**
51
+ * Get calls by topic prefix for inspection
52
+ */
53
+ getCallsByTopicPrefix(prefix: string): AICallRecord[];
54
+ /**
55
+ * Get calls by model for inspection
56
+ */
57
+ getCallsByModel(modelKey: string): AICallRecord[];
58
+ /**
59
+ * Get calls by call type for inspection
60
+ */
61
+ getCallsByType(callType: string): AICallRecord[];
62
+ /**
63
+ * Get total cost across all calls
64
+ */
65
+ getTotalCost(): number;
66
+ /**
67
+ * Get total tokens across all calls
68
+ */
69
+ getTotalTokens(): {
70
+ input: number;
71
+ output: number;
72
+ };
73
+ /**
74
+ * Get call count
75
+ */
76
+ getCallCount(): number;
77
+ /**
78
+ * Get all recorded batch IDs
79
+ */
80
+ getRecordedBatchIds(): string[];
81
+ /**
82
+ * Get the last call made (useful for assertions)
83
+ */
84
+ getLastCall(): AICallRecord | null;
85
+ /**
86
+ * Assert a call was made with specific properties
87
+ */
88
+ hasCallMatching(predicate: (call: AICallRecord) => boolean): boolean;
89
+ }
90
+
91
+ /**
92
+ * In-Memory Job Queue
93
+ *
94
+ * A complete in-memory implementation of JobQueue for testing.
95
+ * Supports priority ordering, locking, and stale job recovery.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * import { InMemoryJobQueue } from '@bratsos/workflow-engine/testing';
100
+ *
101
+ * const jobQueue = new InMemoryJobQueue();
102
+ * // Use in tests...
103
+ * jobQueue.clear(); // Reset between tests
104
+ * ```
105
+ */
106
+
107
+ declare class InMemoryJobQueue implements JobQueue {
108
+ private jobs;
109
+ private workerId;
110
+ private defaultMaxAttempts;
111
+ constructor(workerId?: string);
112
+ enqueue(options: EnqueueJobInput): Promise<string>;
113
+ enqueueParallel(jobs: EnqueueJobInput[]): Promise<string[]>;
114
+ dequeue(): Promise<DequeueResult | null>;
115
+ complete(jobId: string): Promise<void>;
116
+ suspend(jobId: string, nextPollAt: Date): Promise<void>;
117
+ fail(jobId: string, error: string, shouldRetry?: boolean): Promise<void>;
118
+ getSuspendedJobsReadyToPoll(): Promise<Array<{
119
+ jobId: string;
120
+ stageId: string;
121
+ workflowRunId: string;
122
+ }>>;
123
+ releaseStaleJobs(staleThresholdMs?: number): Promise<number>;
124
+ /**
125
+ * Clear all jobs - useful between tests
126
+ */
127
+ clear(): void;
128
+ /**
129
+ * Get all jobs for inspection
130
+ */
131
+ getAllJobs(): JobRecord[];
132
+ /**
133
+ * Get jobs by status for inspection
134
+ */
135
+ getJobsByStatus(status: JobStatus): JobRecord[];
136
+ /**
137
+ * Get a specific job by ID
138
+ */
139
+ getJob(jobId: string): JobRecord | null;
140
+ /**
141
+ * Get the worker ID for this queue instance
142
+ */
143
+ getWorkerId(): string;
144
+ /**
145
+ * Set max attempts for new jobs
146
+ */
147
+ setDefaultMaxAttempts(maxAttempts: number): void;
148
+ /**
149
+ * Simulate a worker crash by releasing a job's lock without completing it
150
+ */
151
+ simulateCrash(jobId: string): void;
152
+ /**
153
+ * Move a suspended job back to pending (for manual resume testing)
154
+ */
155
+ resumeJob(jobId: string): void;
156
+ /**
157
+ * Set lockedAt for testing stale job scenarios
158
+ */
159
+ setJobLockedAt(jobId: string, lockedAt: Date): void;
160
+ /**
161
+ * Set nextPollAt for testing suspended job polling
162
+ */
163
+ setJobNextPollAt(jobId: string, nextPollAt: Date | null): void;
164
+ }
165
+
166
+ /**
167
+ * In-Memory Workflow Persistence
168
+ *
169
+ * A complete in-memory implementation of WorkflowPersistence for testing.
170
+ * All data is stored in Maps and lost when the instance is garbage collected.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * import { InMemoryWorkflowPersistence } from '@bratsos/workflow-engine/testing';
175
+ *
176
+ * const persistence = new InMemoryWorkflowPersistence();
177
+ * // Use in tests...
178
+ * persistence.clear(); // Reset between tests
179
+ * ```
180
+ */
181
+
182
+ declare class InMemoryWorkflowPersistence implements WorkflowPersistence {
183
+ private runs;
184
+ private stages;
185
+ private logs;
186
+ private artifacts;
187
+ private outbox;
188
+ private idempotencyKeys;
189
+ private idempotencyInProgress;
190
+ private outboxSequences;
191
+ private stageKey;
192
+ private artifactKey;
193
+ private idempotencyCompositeKey;
194
+ withTransaction<T>(fn: (tx: WorkflowPersistence) => Promise<T>): Promise<T>;
195
+ createRun(data: CreateRunInput): Promise<WorkflowRunRecord>;
196
+ updateRun(id: string, data: UpdateRunInput): Promise<void>;
197
+ getRun(id: string): Promise<WorkflowRunRecord | null>;
198
+ getRunStatus(id: string): Promise<WorkflowStatus | null>;
199
+ getRunsByStatus(status: WorkflowStatus): Promise<WorkflowRunRecord[]>;
200
+ claimPendingRun(id: string): Promise<boolean>;
201
+ claimNextPendingRun(): Promise<WorkflowRunRecord | null>;
202
+ createStage(data: CreateStageInput): Promise<WorkflowStageRecord>;
203
+ upsertStage(data: UpsertStageInput): Promise<WorkflowStageRecord>;
204
+ updateStage(id: string, data: UpdateStageInput): Promise<void>;
205
+ updateStageByRunAndStageId(workflowRunId: string, stageId: string, data: UpdateStageInput): Promise<void>;
206
+ getStage(runId: string, stageId: string): Promise<WorkflowStageRecord | null>;
207
+ getStageById(id: string): Promise<WorkflowStageRecord | null>;
208
+ getStagesByRun(runId: string, options?: {
209
+ status?: WorkflowStageStatus;
210
+ orderBy?: "asc" | "desc";
211
+ }): Promise<WorkflowStageRecord[]>;
212
+ getSuspendedStages(beforeDate: Date): Promise<WorkflowStageRecord[]>;
213
+ getFirstSuspendedStageReadyToResume(runId: string): Promise<WorkflowStageRecord | null>;
214
+ getFirstFailedStage(runId: string): Promise<WorkflowStageRecord | null>;
215
+ getLastCompletedStage(runId: string): Promise<WorkflowStageRecord | null>;
216
+ getLastCompletedStageBefore(runId: string, executionGroup: number): Promise<WorkflowStageRecord | null>;
217
+ deleteStage(id: string): Promise<void>;
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>;
235
+ saveArtifact(data: SaveArtifactInput): Promise<void>;
236
+ loadArtifact(runId: string, key: string): Promise<unknown>;
237
+ hasArtifact(runId: string, key: string): Promise<boolean>;
238
+ deleteArtifact(runId: string, key: string): Promise<void>;
239
+ listArtifacts(runId: string): Promise<WorkflowArtifactRecord[]>;
240
+ getStageIdForArtifact(runId: string, stageId: string): Promise<string | null>;
241
+ saveStageOutput(runId: string, workflowType: string, stageId: string, output: unknown): Promise<string>;
242
+ /**
243
+ * Clear all data - useful between tests
244
+ */
245
+ clear(): void;
246
+ /**
247
+ * Get all runs for inspection
248
+ */
249
+ getAllRuns(): WorkflowRunRecord[];
250
+ /**
251
+ * Get all stages for inspection
252
+ */
253
+ getAllStages(): WorkflowStageRecord[];
254
+ /**
255
+ * Get all logs for inspection
256
+ */
257
+ getAllLogs(): WorkflowLogRecord[];
258
+ /**
259
+ * Get all artifacts for inspection
260
+ */
261
+ getAllArtifacts(): WorkflowArtifactRecord[];
262
+ }
263
+
264
+ export { InMemoryAICallLogger, InMemoryJobQueue, InMemoryWorkflowPersistence };