@gobing-ai/ts-dual-workflow-engine 0.3.16 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +22 -6
  2. package/dist/action-step.d.ts +61 -0
  3. package/dist/action-step.d.ts.map +1 -0
  4. package/dist/action-step.js +81 -0
  5. package/dist/errors.d.ts +5 -0
  6. package/dist/errors.d.ts.map +1 -1
  7. package/dist/errors.js +9 -0
  8. package/dist/events.d.ts +41 -0
  9. package/dist/events.d.ts.map +1 -1
  10. package/dist/host.d.ts +10 -1
  11. package/dist/host.d.ts.map +1 -1
  12. package/dist/host.js +41 -2
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/persistence.d.ts +35 -1
  17. package/dist/persistence.d.ts.map +1 -1
  18. package/dist/persistence.js +112 -5
  19. package/dist/run-lifecycle.d.ts +21 -2
  20. package/dist/run-lifecycle.d.ts.map +1 -1
  21. package/dist/run-lifecycle.js +80 -16
  22. package/dist/schema-sql.d.ts.map +1 -1
  23. package/dist/schema-sql.js +5 -0
  24. package/dist/schema.d.ts +4 -0
  25. package/dist/schema.d.ts.map +1 -1
  26. package/dist/schema.js +4 -0
  27. package/dist/service.d.ts +36 -2
  28. package/dist/service.d.ts.map +1 -1
  29. package/dist/service.js +184 -0
  30. package/dist/state-machine.d.ts +2 -7
  31. package/dist/state-machine.d.ts.map +1 -1
  32. package/dist/state-machine.js +72 -71
  33. package/dist/transition-flow.d.ts +2 -0
  34. package/dist/transition-flow.d.ts.map +1 -1
  35. package/dist/transition-flow.js +46 -43
  36. package/dist/types.d.ts +59 -3
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +4 -4
  39. package/schemas/state-machine-workflow.schema.json +5 -1
  40. package/schemas/transition-flow-workflow.schema.json +5 -1
  41. package/src/action-step.ts +130 -0
  42. package/src/errors.ts +11 -0
  43. package/src/events.ts +54 -4
  44. package/src/host.ts +53 -3
  45. package/src/index.ts +7 -1
  46. package/src/persistence.ts +137 -6
  47. package/src/run-lifecycle.ts +113 -15
  48. package/src/schema-sql.ts +5 -0
  49. package/src/schema.ts +4 -0
  50. package/src/service.ts +255 -1
  51. package/src/state-machine.ts +86 -114
  52. package/src/transition-flow.ts +59 -46
  53. package/src/types.ts +61 -2
@@ -11,17 +11,28 @@ export async function applyWorkflowEngineSchema(db) {
11
11
  /** SQLite/D1-compatible workflow persistence adapter backed by ts-db. */
12
12
  export class DbWorkflowPersistenceAdapter {
13
13
  db;
14
+ /** Memoized schema-ensure; the DDL runs at most once per adapter instance. */
15
+ schemaReady;
14
16
  constructor(db) {
15
17
  this.db = db;
16
18
  }
19
+ /**
20
+ * Apply the workflow-engine schema once per adapter, latching the in-flight
21
+ * promise so concurrent first calls share a single DDL pass. Every public
22
+ * read/write awaits this instead of re-running the idempotent DDL per call.
23
+ */
24
+ ensureSchema() {
25
+ this.schemaReady ??= applyWorkflowEngineSchema(this.db);
26
+ return this.schemaReady;
27
+ }
17
28
  /** Create a run row, rejecting duplicate run ids. */
18
29
  async createRun(record) {
19
30
  const existing = await this.loadRun(record.id);
20
31
  if (existing !== undefined)
21
32
  throw new RunCollisionError(record.id);
22
- await applyWorkflowEngineSchema(this.db);
23
- await this.db.run(`INSERT INTO runs (id, workflow_name, mode, status, started_at, completed_at, metadata_json, created_at, updated_at)
24
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, record.id, record.workflow_name, record.mode, record.status, record.started_at, record.completed_at, record.metadata_json, Date.now(), Date.now());
33
+ await this.ensureSchema();
34
+ await this.db.run(`INSERT INTO runs (id, workflow_name, mode, status, external_key, started_at, completed_at, metadata_json, created_at, updated_at)
35
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, record.id, record.workflow_name, record.mode, record.status, record.external_key ?? null, record.started_at, record.completed_at, record.metadata_json, Date.now(), Date.now());
25
36
  }
26
37
  /** Finalize a run with terminal status and timestamp. */
27
38
  async finalizeRun(runId, status, completedAt) {
@@ -62,15 +73,71 @@ export class DbWorkflowPersistenceAdapter {
62
73
  }
63
74
  /** Load a single run by id. */
64
75
  async loadRun(runId) {
65
- await applyWorkflowEngineSchema(this.db);
76
+ await this.ensureSchema();
66
77
  const row = await this.db.queryFirst('SELECT * FROM runs WHERE id = ?', runId);
67
78
  return row ?? undefined;
68
79
  }
69
80
  /** List persisted workflow runs. */
70
81
  async listRuns() {
71
- await applyWorkflowEngineSchema(this.db);
82
+ await this.ensureSchema();
72
83
  return await this.db.queryAll('SELECT * FROM runs ORDER BY started_at DESC');
73
84
  }
85
+ /** Look up a run by its external key within a workflow definition. */
86
+ async findRunByKey(workflowName, externalKey) {
87
+ await this.ensureSchema();
88
+ const row = await this.db.queryFirst('SELECT * FROM runs WHERE workflow_name = ? AND external_key = ?', workflowName, externalKey);
89
+ return row ?? undefined;
90
+ }
91
+ /** Create a run or attach to an existing one by external key. */
92
+ async createOrAttachRun(record) {
93
+ await this.ensureSchema();
94
+ if (record.external_key) {
95
+ const existing = await this.findRunByKey(record.workflow_name, record.external_key);
96
+ if (existing)
97
+ return existing;
98
+ }
99
+ try {
100
+ await this.createRun(record);
101
+ }
102
+ catch (error) {
103
+ if (record.external_key) {
104
+ const existing = await this.findRunByKey(record.workflow_name, record.external_key);
105
+ if (existing)
106
+ return existing;
107
+ }
108
+ throw error;
109
+ }
110
+ return { ...record };
111
+ }
112
+ /** Force-set the current state of a run (reseed). */
113
+ async reseedRun(runId, newState) {
114
+ const now = Date.now();
115
+ await this.ensureSchema();
116
+ const previous = await this.db.queryFirst('SELECT state FROM workflow_states WHERE run_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1', runId);
117
+ await this.saveWorkflowState(runId, newState, { reseeded: true, reseededAt: new Date(now).toISOString() });
118
+ await this.saveTransition(runId, previous?.state ?? '', newState, '__reseed__');
119
+ return { fromState: previous?.state ?? null, toState: newState };
120
+ }
121
+ /** Load the current state name for a run. */
122
+ async loadCurrentState(runId) {
123
+ await this.ensureSchema();
124
+ const row = await this.db.queryFirst('SELECT state FROM workflow_states WHERE run_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1', runId);
125
+ return row?.state;
126
+ }
127
+ /** List runs with status 'paused'. Ordered most-recent-first. */
128
+ async listPausedRuns(options) {
129
+ await this.ensureSchema();
130
+ const where = ["status = 'paused'"];
131
+ const params = [];
132
+ if (options?.workflowName !== undefined) {
133
+ where.push('workflow_name = ?');
134
+ params.push(options.workflowName);
135
+ }
136
+ const limit = options?.limit ?? 100;
137
+ const sql = `SELECT * FROM runs WHERE ${where.join(' AND ')} ORDER BY updated_at DESC, rowid DESC LIMIT ?`;
138
+ params.push(limit);
139
+ return await this.db.queryAll(sql, ...params);
140
+ }
74
141
  }
75
142
  /** In-memory persistence adapter for tests and embedding. */
76
143
  export class MemoryWorkflowPersistenceAdapter {
@@ -136,4 +203,44 @@ export class MemoryWorkflowPersistenceAdapter {
136
203
  async listRuns() {
137
204
  return [...this.runs.values()];
138
205
  }
206
+ /** Look up a run by its external key within a workflow definition. */
207
+ async findRunByKey(workflowName, externalKey) {
208
+ for (const run of this.runs.values()) {
209
+ if (run.workflow_name === workflowName && run.external_key === externalKey)
210
+ return run;
211
+ }
212
+ return undefined;
213
+ }
214
+ /** Create a run or attach to an existing one by external key. */
215
+ async createOrAttachRun(record) {
216
+ if (record.external_key) {
217
+ const existing = await this.findRunByKey(record.workflow_name, record.external_key);
218
+ if (existing)
219
+ return existing;
220
+ }
221
+ await this.createRun(record);
222
+ return { ...record };
223
+ }
224
+ /** Force-set the current state of a run (reseed). */
225
+ async reseedRun(runId, newState) {
226
+ const previous = this.states.findLast((state) => state.runId === runId);
227
+ await this.saveWorkflowState(runId, newState, { reseeded: true, reseededAt: new Date().toISOString() });
228
+ await this.saveTransition(runId, previous?.state ?? '', newState, '__reseed__');
229
+ return { fromState: previous?.state ?? null, toState: newState };
230
+ }
231
+ /** Load the current state name for a run. */
232
+ async loadCurrentState(runId) {
233
+ const last = this.states.findLast((s) => s.runId === runId);
234
+ return last?.state;
235
+ }
236
+ /** List runs with status 'paused'. Ordered most-recent-first. */
237
+ async listPausedRuns(options) {
238
+ let runs = [...this.runs.values()].filter((r) => r.status === 'paused');
239
+ if (options?.workflowName !== undefined) {
240
+ runs = runs.filter((r) => r.workflow_name === options.workflowName);
241
+ }
242
+ // Memory adapter has no updated_at tracking; use insertion order (reverse = most-recent-first).
243
+ runs.reverse();
244
+ return runs.slice(0, options?.limit ?? 100);
245
+ }
139
246
  }
@@ -27,17 +27,32 @@ export declare class RunLifecycle {
27
27
  private readonly workflowName;
28
28
  private readonly mode;
29
29
  readonly runId: string;
30
+ readonly externalKey?: string;
30
31
  private readonly persistence;
31
32
  private readonly events;
32
33
  private readonly logger;
33
- private readonly startedAt;
34
34
  private constructor();
35
+ /**
36
+ * Build a lifecycle for an external, single-hop transition request — NOT a run.
37
+ * Unlike {@link run} / {@link resume} this opens no `workflow.run` span and
38
+ * creates no run record: an external transition is one guarded hop on an
39
+ * already-existing run, so it must not masquerade as a run in traces. It exists
40
+ * only to let {@link WorkflowService.requestTransition} reuse {@link recordTransition}
41
+ * and {@link guardEvaluated} so the transition persist+emit mechanics live in one
42
+ * place instead of being hand-rolled at the service layer.
43
+ */
44
+ static forExternalTransition(workflowName: string, runId: string, deps: RunLifecycleDeps, externalKey: string | undefined): RunLifecycle;
35
45
  /**
36
46
  * Create the run record and execute `loop` inside the run's OTel span. The
37
47
  * driver's control loop is the body; it receives this lifecycle to drive
38
48
  * per-step persistence and terminal results.
39
49
  */
40
50
  static run(workflowName: string, mode: WorkflowMode, deps: RunLifecycleDeps, options: WorkflowRunOptions, loop: (lifecycle: RunLifecycle) => Promise<WorkflowRunResult>): Promise<WorkflowRunResult>;
51
+ /**
52
+ * Resume an existing run without creating a new record. Used by driver resume paths.
53
+ * The caller is responsible for ensuring the run exists and emitting the resumed event.
54
+ */
55
+ static resume(workflowName: string, mode: WorkflowMode, deps: RunLifecycleDeps, runId: string, externalKey: string | undefined, loop: (lifecycle: RunLifecycle) => Promise<WorkflowRunResult>): Promise<WorkflowRunResult>;
41
56
  /** Persist the current state/node snapshot and mark its phase running. */
42
57
  enter(stateOrNodeId: string, transitionsTaken: number): Promise<void>;
43
58
  /** Persist a transition and emit its observability event. */
@@ -46,6 +61,10 @@ export declare class RunLifecycle {
46
61
  done(finalState: string, transitionsTaken: number): Promise<WorkflowRunResult>;
47
62
  /** Finalize the run as failed and return its result. */
48
63
  fail(finalState: string, transitionsTaken: number, reason?: string): Promise<WorkflowRunResult>;
64
+ /** Finalize the run as paused and return its result. */
65
+ pause(stateOrNodeId: string, transitionsTaken: number): Promise<WorkflowRunResult>;
66
+ /** Emit the resumed event (called by WorkflowService after re-creating a lifecycle for resume). */
67
+ emitResumed(node: string): void;
49
68
  /** Emit action-level observability before a host action is invoked. */
50
69
  actionStart(stateOrNodeId: string, kind: string): void;
51
70
  /** Emit action-level observability after a host action settles. */
@@ -59,6 +78,6 @@ export declare class RunLifecycle {
59
78
  /** Emit when an interactive HITL prompt resolves. */
60
79
  hitlResponse(node: string, ok: boolean): void;
61
80
  private result;
62
- private runRecord;
81
+ private static runRecord;
63
82
  }
64
83
  //# sourceMappingURL=run-lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../src/run-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../src/run-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -38,44 +38,76 @@ export class RunLifecycle {
38
38
  workflowName;
39
39
  mode;
40
40
  runId;
41
+ externalKey;
41
42
  persistence;
42
43
  events;
43
44
  logger;
44
- startedAt;
45
- constructor(runId, workflowName, mode, deps) {
45
+ constructor(runId, workflowName, mode, deps, externalKey) {
46
46
  this.workflowName = workflowName;
47
47
  this.mode = mode;
48
48
  this.runId = runId;
49
+ this.externalKey = externalKey;
49
50
  this.persistence = deps.persistence;
50
51
  this.events = deps.events;
51
- this.startedAt = new Date().toISOString();
52
52
  this.logger = (deps.logger ?? getLogger('workflow')).child({ runId, workflow: workflowName, mode });
53
53
  }
54
+ /**
55
+ * Build a lifecycle for an external, single-hop transition request — NOT a run.
56
+ * Unlike {@link run} / {@link resume} this opens no `workflow.run` span and
57
+ * creates no run record: an external transition is one guarded hop on an
58
+ * already-existing run, so it must not masquerade as a run in traces. It exists
59
+ * only to let {@link WorkflowService.requestTransition} reuse {@link recordTransition}
60
+ * and {@link guardEvaluated} so the transition persist+emit mechanics live in one
61
+ * place instead of being hand-rolled at the service layer.
62
+ */
63
+ static forExternalTransition(workflowName, runId, deps, externalKey) {
64
+ return new RunLifecycle(runId, workflowName, 'state-machine', deps, externalKey);
65
+ }
54
66
  /**
55
67
  * Create the run record and execute `loop` inside the run's OTel span. The
56
68
  * driver's control loop is the body; it receives this lifecycle to drive
57
69
  * per-step persistence and terminal results.
58
70
  */
59
71
  static async run(workflowName, mode, deps, options, loop) {
60
- const runId = options.runId ?? crypto.randomUUID();
61
- const lifecycle = new RunLifecycle(runId, workflowName, mode, deps);
62
72
  return await traceAsync('workflow.run', async () => {
63
- await lifecycle.persistence.createRun(lifecycle.runRecord(options.metadata));
73
+ const startedAt = new Date().toISOString();
74
+ const proposed = RunLifecycle.runRecord(options.runId ?? crypto.randomUUID(), workflowName, mode, startedAt, options.metadata, options.externalKey);
75
+ let record = proposed;
76
+ if (options.externalKey === undefined) {
77
+ await deps.persistence.createRun(proposed);
78
+ }
79
+ else {
80
+ record = await deps.persistence.createOrAttachRun(proposed);
81
+ }
82
+ const extKey = record.external_key ?? undefined;
83
+ const lifecycle = new RunLifecycle(record.id, workflowName, mode, deps, extKey);
64
84
  lifecycle.logger.info('workflow run started');
65
85
  addSpanEvent('workflow.run.started', {
66
86
  workflowName,
67
87
  mode,
68
- runId,
88
+ runId: lifecycle.runId,
69
89
  dryRun: options.dryRun ?? false,
70
90
  });
71
91
  void lifecycle.events?.emit('workflow.run.started', {
72
92
  workflowName,
73
93
  mode,
74
- runId,
94
+ runId: lifecycle.runId,
75
95
  dryRun: options.dryRun ?? false,
96
+ externalKey: extKey,
76
97
  });
77
98
  return await loop(lifecycle);
78
- }, { attributes: { 'workflow.name': workflowName, 'workflow.mode': mode, 'workflow.run_id': runId } });
99
+ }, { attributes: { 'workflow.name': workflowName, 'workflow.mode': mode } });
100
+ }
101
+ /**
102
+ * Resume an existing run without creating a new record. Used by driver resume paths.
103
+ * The caller is responsible for ensuring the run exists and emitting the resumed event.
104
+ */
105
+ static async resume(workflowName, mode, deps, runId, externalKey, loop) {
106
+ return await traceAsync('workflow.run', async () => {
107
+ const lifecycle = new RunLifecycle(runId, workflowName, mode, deps, externalKey);
108
+ lifecycle.logger.info('workflow run resumed');
109
+ return await loop(lifecycle);
110
+ }, { attributes: { 'workflow.name': workflowName, 'workflow.mode': mode } });
79
111
  }
80
112
  /** Persist the current state/node snapshot and mark its phase running. */
81
113
  async enter(stateOrNodeId, transitionsTaken) {
@@ -106,6 +138,7 @@ export class RunLifecycle {
106
138
  from,
107
139
  to,
108
140
  trigger,
141
+ externalKey: this.externalKey,
109
142
  });
110
143
  }
111
144
  /** Finalize the run as succeeded and return its result. */
@@ -114,7 +147,12 @@ export class RunLifecycle {
114
147
  await this.persistence.finalizeRun(this.runId, 'done', new Date().toISOString());
115
148
  this.logger.info('workflow run done', { finalState, transitionsTaken });
116
149
  addSpanEvent('workflow.run.done', { runId: this.runId, finalState, transitionsTaken });
117
- void this.events?.emit('workflow.run.done', { runId: this.runId, finalState, transitionsTaken });
150
+ void this.events?.emit('workflow.run.done', {
151
+ runId: this.runId,
152
+ finalState,
153
+ transitionsTaken,
154
+ externalKey: this.externalKey,
155
+ });
118
156
  return this.result('done', finalState, transitionsTaken);
119
157
  }
120
158
  /** Finalize the run as failed and return its result. */
@@ -122,10 +160,34 @@ export class RunLifecycle {
122
160
  await this.persistence.savePhase(this.runId, finalState, 'failed');
123
161
  await this.persistence.finalizeRun(this.runId, 'failed', new Date().toISOString());
124
162
  addSpanEvent('workflow.run.failed', { runId: this.runId, finalState, reason });
125
- void this.events?.emit('workflow.run.failed', { runId: this.runId, finalState, reason });
163
+ void this.events?.emit('workflow.run.failed', {
164
+ runId: this.runId,
165
+ finalState,
166
+ reason,
167
+ externalKey: this.externalKey,
168
+ });
126
169
  this.logger.warn('workflow run failed', { finalState, transitionsTaken, reason });
127
170
  return this.result('failed', finalState, transitionsTaken, reason);
128
171
  }
172
+ /** Finalize the run as paused and return its result. */
173
+ async pause(stateOrNodeId, transitionsTaken) {
174
+ await this.persistence.savePhase(this.runId, stateOrNodeId, 'paused');
175
+ await this.persistence.finalizeRun(this.runId, 'paused', new Date().toISOString());
176
+ this.logger.info('workflow run paused', { stateOrNodeId, transitionsTaken });
177
+ addSpanEvent('workflow.run.paused', { runId: this.runId, node: stateOrNodeId, transitionsTaken });
178
+ void this.events?.emit('workflow.run.paused', {
179
+ runId: this.runId,
180
+ node: stateOrNodeId,
181
+ transitionsTaken,
182
+ externalKey: this.externalKey,
183
+ });
184
+ return this.result('paused', stateOrNodeId, transitionsTaken);
185
+ }
186
+ /** Emit the resumed event (called by WorkflowService after re-creating a lifecycle for resume). */
187
+ emitResumed(node) {
188
+ addSpanEvent('workflow.run.resumed', { runId: this.runId, node });
189
+ void this.events?.emit('workflow.run.resumed', { runId: this.runId, node, externalKey: this.externalKey });
190
+ }
129
191
  /** Emit action-level observability before a host action is invoked. */
130
192
  actionStart(stateOrNodeId, kind) {
131
193
  addSpanEvent('workflow.action.start', { runId: this.runId, node: stateOrNodeId, kind });
@@ -166,6 +228,7 @@ export class RunLifecycle {
166
228
  to,
167
229
  kind,
168
230
  passed,
231
+ externalKey: this.externalKey,
169
232
  });
170
233
  }
171
234
  /** Emit when an interactive HITL prompt is presented. */
@@ -187,15 +250,16 @@ export class RunLifecycle {
187
250
  ...(reason === undefined ? {} : { reason }),
188
251
  };
189
252
  }
190
- runRecord(metadata) {
253
+ static runRecord(runId, workflowName, mode, startedAt, metadata, externalKey) {
191
254
  return {
192
- id: this.runId,
193
- workflow_name: this.workflowName,
194
- mode: this.mode,
255
+ id: runId,
256
+ workflow_name: workflowName,
257
+ mode,
195
258
  status: 'running',
196
- started_at: this.startedAt,
259
+ started_at: startedAt,
197
260
  metadata_json: JSON.stringify(metadata ?? {}),
198
261
  completed_at: null,
262
+ external_key: externalKey ?? null,
199
263
  };
200
264
  }
201
265
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema-sql.d.ts","sourceRoot":"","sources":["../src/schema-sql.ts"],"names":[],"mappings":"AAAA,6IAA6I;AAC7I,eAAO,MAAM,0BAA0B,QA8D/B,CAAC"}
1
+ {"version":3,"file":"schema-sql.d.ts","sourceRoot":"","sources":["../src/schema-sql.ts"],"names":[],"mappings":"AAAA,6IAA6I;AAC7I,eAAO,MAAM,0BAA0B,QAmE/B,CAAC"}
@@ -6,6 +6,7 @@ CREATE TABLE IF NOT EXISTS runs (
6
6
  mode TEXT,
7
7
  status TEXT NOT NULL,
8
8
  agent TEXT,
9
+ external_key TEXT,
9
10
  started_at TEXT NOT NULL,
10
11
  completed_at TEXT,
11
12
  metadata_json TEXT NOT NULL DEFAULT '{}',
@@ -13,6 +14,10 @@ CREATE TABLE IF NOT EXISTS runs (
13
14
  updated_at INTEGER NOT NULL DEFAULT 0
14
15
  );
15
16
 
17
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_runs_external_key
18
+ ON runs (workflow_name, external_key)
19
+ WHERE external_key IS NOT NULL;
20
+
16
21
  CREATE TABLE IF NOT EXISTS phase_runs (
17
22
  id TEXT PRIMARY KEY,
18
23
  run_id TEXT NOT NULL,
package/dist/schema.d.ts CHANGED
@@ -50,6 +50,7 @@ export declare const StateMachineWorkflowDefSchema: z.ZodObject<{
50
50
  continue: "continue";
51
51
  }>>;
52
52
  }, z.core.$strip>>>;
53
+ pause: z.ZodOptional<z.ZodBoolean>;
53
54
  }, z.core.$strict>>;
54
55
  transitions: z.ZodArray<z.ZodObject<{
55
56
  from: z.ZodString;
@@ -97,6 +98,7 @@ export declare const TransitionFlowWorkflowDefSchema: z.ZodObject<{
97
98
  continue: "continue";
98
99
  }>>;
99
100
  }, z.core.$strip>>;
101
+ pause: z.ZodOptional<z.ZodBoolean>;
100
102
  }, z.core.$strict>>;
101
103
  edges: z.ZodArray<z.ZodObject<{
102
104
  from: z.ZodString;
@@ -145,6 +147,7 @@ export declare const WorkflowDefSchema: z.ZodUnion<readonly [z.ZodObject<{
145
147
  continue: "continue";
146
148
  }>>;
147
149
  }, z.core.$strip>>>;
150
+ pause: z.ZodOptional<z.ZodBoolean>;
148
151
  }, z.core.$strict>>;
149
152
  transitions: z.ZodArray<z.ZodObject<{
150
153
  from: z.ZodString;
@@ -190,6 +193,7 @@ export declare const WorkflowDefSchema: z.ZodUnion<readonly [z.ZodObject<{
190
193
  continue: "continue";
191
194
  }>>;
192
195
  }, z.core.$strip>>;
196
+ pause: z.ZodOptional<z.ZodBoolean>;
193
197
  }, z.core.$strict>>;
194
198
  edges: z.ZodArray<z.ZodObject<{
195
199
  from: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyBxB,kDAAkD;AAClD,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,cAAc;;;iBAGzB,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAoC7B,CAAC;AAEd,2DAA2D;AAC3D,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmC/B,CAAC;AAEd,iEAAiE;AACjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAA4E,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyBxB,kDAAkD;AAClD,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,cAAc;;;iBAGzB,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAsC7B,CAAC;AAEd,2DAA2D;AAC3D,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAqC/B,CAAC;AAEd,iEAAiE;AACjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAA4E,CAAC"}
package/dist/schema.js CHANGED
@@ -50,6 +50,8 @@ export const StateMachineWorkflowDefSchema = z
50
50
  description: z.string().optional(),
51
51
  onEnter: z.array(ActionDefSchema).optional(),
52
52
  onExit: z.array(ActionDefSchema).optional(),
53
+ /** When true, the engine pauses the run at this state instead of auto-advancing. */
54
+ pause: z.boolean().optional(),
53
55
  })
54
56
  .strict()),
55
57
  transitions: z.array(z
@@ -84,6 +86,8 @@ export const TransitionFlowWorkflowDefSchema = z
84
86
  description: z.string().optional(),
85
87
  type: z.enum(['action', 'gate', 'parallel', 'decision']).optional(),
86
88
  action: ActionDefSchema.optional(),
89
+ /** When true, the engine pauses the run at this node instead of auto-advancing. */
90
+ pause: z.boolean().optional(),
87
91
  })
88
92
  .strict()),
89
93
  edges: z.array(z
package/dist/service.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { WorkflowEngineHost } from './host';
2
- import type { WorkflowDef, WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunResult } from './types';
2
+ import type { StateMachineWorkflowDef, TransitionRequestResult, WorkflowDef, WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunRecord, WorkflowRunResult } from './types';
3
3
  /** High-level workflow service for loading, running, and listing persisted workflow runs. */
4
4
  export declare class WorkflowService {
5
5
  private readonly host;
6
6
  private readonly persistence;
7
+ /** Per-run serialization: ensures concurrent requestTransition calls serialize. */
8
+ private readonly runLocks;
7
9
  constructor(host: WorkflowEngineHost, persistence: WorkflowPersistenceAdapter);
8
10
  /** Load a workflow file and validate it. */
9
11
  load(path: string): Promise<WorkflowDef>;
@@ -12,6 +14,38 @@ export declare class WorkflowService {
12
14
  /** Load and run a workflow file. */
13
15
  runFile(path: string, options?: WorkflowRunOptions): Promise<WorkflowRunResult>;
14
16
  /** List persisted workflow runs. */
15
- listRuns(): Promise<readonly import("./types").WorkflowRunRecord[]>;
17
+ listRuns(): Promise<readonly WorkflowRunRecord[]>;
18
+ /** Find a run by its external key within a workflow definition. */
19
+ findRunByKey(workflowName: string, externalKey: string): Promise<WorkflowRunRecord | undefined>;
20
+ /** Create a new run or attach to an existing one identified by external key. */
21
+ createOrAttachRun(record: WorkflowRunRecord): Promise<WorkflowRunRecord>;
22
+ /** Force-set the current state of a run (consumer-side authority reconciliation). */
23
+ reseedRun(workflow: WorkflowDef, runId: string, newState: string, options?: WorkflowRunOptions): Promise<void>;
24
+ reseedRun(runId: string, newState: string, options?: WorkflowRunOptions): Promise<void>;
25
+ /** Reject a reseed target the workflow definition does not allow (state-machine states only). */
26
+ private assertReseedTargetDeclared;
27
+ /** Persist the reseed and emit the corrective event with the run's external key. */
28
+ private commitReseed;
29
+ /** Resume a paused run, continuing execution from where it stopped. */
30
+ resumeRun(workflow: WorkflowDef, runId: string, options?: WorkflowRunOptions): Promise<WorkflowRunResult>;
31
+ /** List runs currently paused. Optional filters and ordering. */
32
+ listPausedRuns(options?: {
33
+ workflowName?: string;
34
+ limit?: number;
35
+ }): Promise<readonly WorkflowRunRecord[]>;
36
+ /**
37
+ * Request an external state transition on a run. Evaluates whether the
38
+ * transition exists and its guard passes; commits or denies atomically.
39
+ * Concurrent requests on the same run serialize — the loser re-evaluates
40
+ * against the new state.
41
+ */
42
+ requestTransition(workflow: StateMachineWorkflowDef, runId: string, toState: string, options?: WorkflowRunOptions): Promise<TransitionRequestResult>;
43
+ private evaluateAndCommit;
44
+ /**
45
+ * Build a `TransitionDenied` result and emit its `workflow.transition.denied`
46
+ * event in one place — the single denial seam for the external-transition path,
47
+ * so the event payload and the returned reason can never drift apart.
48
+ */
49
+ private denyTransition;
16
50
  }
17
51
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9G,6FAA6F;AAC7F,qBAAa,eAAe;IAEpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,IAAI,EAAE,kBAAkB,EACxB,WAAW,EAAE,0BAA0B;IAG5D,4CAA4C;IACtC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAI9C,iDAAiD;IAC3C,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU9F,oCAAoC;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIzF,oCAAoC;IAC9B,QAAQ;CAGjB"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAIjD,OAAO,KAAK,EACR,uBAAuB,EAEvB,uBAAuB,EACvB,WAAW,EACX,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACpB,MAAM,SAAS,CAAC;AAEjB,6FAA6F;AAC7F,qBAAa,eAAe;IAKpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IALhC,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;gBAGxC,IAAI,EAAE,kBAAkB,EACxB,WAAW,EAAE,0BAA0B;IAG5D,4CAA4C;IACtC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAI9C,iDAAiD;IAC3C,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU9F,oCAAoC;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIzF,oCAAoC;IAC9B,QAAQ;IAId,mEAAmE;IAC7D,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIrG,gFAAgF;IAC1E,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI9E,qFAAqF;IAC/E,SAAS,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC7B,OAAO,CAAC,IAAI,CAAC;IACV,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7F,iGAAiG;IACjG,OAAO,CAAC,0BAA0B;IASlC,oFAAoF;YACtE,YAAY;IAY1B,uEAAuE;IACjE,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAgC/G,iEAAiE;IAC3D,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;IAIhH;;;;;OAKG;IACG,iBAAiB,CACnB,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC7B,OAAO,CAAC,uBAAuB,CAAC;YAsBrB,iBAAiB;IA4E/B;;;;OAIG;IACH,OAAO,CAAC,cAAc;CAiBzB"}