@gobing-ai/ts-dual-workflow-engine 0.4.7 → 0.4.9

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.
package/dist/host.d.ts CHANGED
@@ -1,10 +1,13 @@
1
+ import type { BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
1
2
  import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
2
3
  import { type CapabilityOrigin } from '@gobing-ai/ts-runtime/extension';
3
4
  import type { ActionResult, ActionRunContext, ActionRunner, GuardContext, GuardEvaluationResult, GuardRunner } from './types';
4
5
  /** Registry owner for workflow actions and guards. */
5
6
  export declare class WorkflowEngineHost {
7
+ readonly lifecycleBus?: EventBus<BusLifecycleEvents> | undefined;
6
8
  private readonly actions;
7
9
  private readonly guards;
10
+ constructor(lifecycleBus?: EventBus<BusLifecycleEvents> | undefined);
8
11
  /** Register or replace an action runner. */
9
12
  registerAction(action: ActionRunner, origin?: CapabilityOrigin): this;
10
13
  /** Register or replace a guard runner. */
@@ -31,6 +34,7 @@ export declare class WorkflowEngineHost {
31
34
  /** Create a workflow host with built-in note, shell, always, and action-ok capabilities. */
32
35
  export declare function createDefaultWorkflowEngineHost(options?: {
33
36
  processExecutor?: ProcessExecutor;
37
+ lifecycleBus?: EventBus<BusLifecycleEvents>;
34
38
  }): WorkflowEngineHost;
35
39
  /** Built-in action that records a note and emits a workflow.hitl.note event. */
36
40
  export declare class NoteActionRunner implements ActionRunner {
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
package/dist/host.js CHANGED
@@ -1,10 +1,14 @@
1
- import { NodeProcessExecutor } from '@gobing-ai/ts-runtime';
1
+ import { nodeBunFactory } from '@gobing-ai/ts-runtime';
2
2
  import { CapabilityRegistry } from '@gobing-ai/ts-runtime/extension';
3
3
  import { WorkflowValidationError } from './errors.js';
4
4
  /** Registry owner for workflow actions and guards. */
5
5
  export class WorkflowEngineHost {
6
+ lifecycleBus;
6
7
  actions = new CapabilityRegistry('workflow action');
7
8
  guards = new CapabilityRegistry('workflow guard');
9
+ constructor(lifecycleBus) {
10
+ this.lifecycleBus = lifecycleBus;
11
+ }
8
12
  /** Register or replace an action runner. */
9
13
  registerAction(action, origin = 'extension') {
10
14
  this.actions.register(action.kind, action, origin);
@@ -61,11 +65,11 @@ export class WorkflowEngineHost {
61
65
  }
62
66
  /** Create a workflow host with built-in note, shell, always, and action-ok capabilities. */
63
67
  export function createDefaultWorkflowEngineHost(options = {}) {
64
- const host = new WorkflowEngineHost();
68
+ const host = new WorkflowEngineHost(options.lifecycleBus);
65
69
  host.registerAction(new NoteActionRunner(), 'builtin');
66
- host.registerAction(new ShellActionRunner(options.processExecutor ?? new NodeProcessExecutor()), 'builtin');
70
+ host.registerAction(new ShellActionRunner(options.processExecutor ?? nodeBunFactory.createProcessExecutor()), 'builtin');
67
71
  host.registerAction(new EventEmitActionRunner(), 'builtin');
68
- host.registerGuard(new ShellGuardRunner(options.processExecutor ?? new NodeProcessExecutor()), 'builtin');
72
+ host.registerGuard(new ShellGuardRunner(options.processExecutor ?? nodeBunFactory.createProcessExecutor()), 'builtin');
69
73
  host.registerGuard({ kind: 'always', evaluate: async () => true }, 'builtin');
70
74
  host.registerGuard({ kind: 'never', evaluate: async () => false }, 'builtin');
71
75
  host.registerGuard({
@@ -1,4 +1,4 @@
1
- import { type EventBus, type Logger } from '@gobing-ai/ts-infra';
1
+ import { type BusLifecycleEvents, EventBus, type Logger } from '@gobing-ai/ts-infra';
2
2
  import type { WorkflowEngineEvents } from './events';
3
3
  import type { WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunResult, WorkflowStatus } from './types';
4
4
  /** Workflow dialect carried on every run, span, and persisted record. */
@@ -16,6 +16,8 @@ export interface RunLifecycleDeps {
16
16
  readonly logger?: Logger;
17
17
  /** Optional event bus for structured in-process run observability. */
18
18
  readonly events?: EventBus<WorkflowEngineEvents>;
19
+ /** Parent bus used when RunLifecycle must construct its own workflow event bus. */
20
+ readonly lifecycleBus?: EventBus<BusLifecycleEvents>;
19
21
  }
20
22
  /**
21
23
  * Owns the run-level bookkeeping shared by both drivers: run identity, the
@@ -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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"}
@@ -1,4 +1,4 @@
1
- import { addSpanEvent, getLogger, traceAsync } from '@gobing-ai/ts-infra';
1
+ import { addSpanEvent, EventBus, getLogger, traceAsync, } from '@gobing-ai/ts-infra';
2
2
  import { getProcessEnv } from '@gobing-ai/ts-runtime';
3
3
  /** Keys of the runtime builtin namespace, single-sourced for resolver + validator. */
4
4
  export const RUNTIME_BUILTIN_KEYS = [
@@ -48,7 +48,9 @@ export class RunLifecycle {
48
48
  this.runId = runId;
49
49
  this.externalKey = externalKey;
50
50
  this.persistence = deps.persistence;
51
- this.events = deps.events;
51
+ this.events =
52
+ deps.events ??
53
+ (deps.lifecycleBus ? new EventBus({ lifecycleBus: deps.lifecycleBus }) : undefined);
52
54
  this.logger = (deps.logger ?? getLogger('workflow')).child({ runId, workflow: workflowName, mode });
53
55
  }
54
56
  /**
package/dist/service.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
1
2
  import type { WorkflowEngineHost } from './host';
2
3
  import type { StateMachineWorkflowDef, TransitionRequestResult, WorkflowDef, WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunRecord, WorkflowRunResult } from './types';
3
4
  /** High-level workflow service for loading, running, and listing persisted workflow runs. */
@@ -6,7 +7,18 @@ export declare class WorkflowService {
6
7
  private readonly persistence;
7
8
  /** Per-run serialization: ensures concurrent requestTransition calls serialize. */
8
9
  private readonly runLocks;
9
- constructor(host: WorkflowEngineHost, persistence: WorkflowPersistenceAdapter);
10
+ private readonly lifecycleBus;
11
+ constructor(host: WorkflowEngineHost, persistence: WorkflowPersistenceAdapter,
12
+ /**
13
+ * Optional lifecycle bus to bridge `workflow.*` events into the
14
+ * application System Events stream (R3). When a run's `events` is
15
+ * omitted the service constructs an internal
16
+ * `EventBus<WorkflowEngineEvents>` parented to this bus so workflow
17
+ * events appear in the JSONL log.
18
+ */
19
+ lifecycleBus?: EventBus<BusLifecycleEvents>);
20
+ /** Resolve a run's event bus: caller-supplied wins; otherwise parent to the service lifecycle bus. */
21
+ private resolveEvents;
10
22
  /** Load a workflow file and validate it. */
11
23
  load(path: string): Promise<WorkflowDef>;
12
24
  /** Run an already-loaded workflow definition. */
@@ -1 +1 @@
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;AAGjB,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;IA+E/B;;;;OAIG;IACH,OAAO,CAAC,cAAc;CAiBzB"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIxE,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;AAGjB,6FAA6F;AAC7F,qBAAa,eAAe;IAMpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IANhC,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2C;gBAGnD,IAAI,EAAE,kBAAkB,EACxB,WAAW,EAAE,0BAA0B;IACxD;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC;IAK/C,sGAAsG;IACtG,OAAO,CAAC,aAAa;IASrB,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;IAc9F,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;IAiC/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;IAgF/B;;;;OAIG;IACH,OAAO,CAAC,cAAc;CAiBzB"}
package/dist/service.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { EventBus } from '@gobing-ai/ts-infra';
1
2
  import { loadWorkflowDef } from './config.js';
2
3
  import { FSMError, WorkflowResumeError } from './errors.js';
3
4
  import { RunLifecycle } from './run-lifecycle.js';
@@ -10,9 +11,24 @@ export class WorkflowService {
10
11
  persistence;
11
12
  /** Per-run serialization: ensures concurrent requestTransition calls serialize. */
12
13
  runLocks = new Map();
13
- constructor(host, persistence) {
14
+ lifecycleBus;
15
+ constructor(host, persistence,
16
+ /**
17
+ * Optional lifecycle bus to bridge `workflow.*` events into the
18
+ * application System Events stream (R3). When a run's `events` is
19
+ * omitted the service constructs an internal
20
+ * `EventBus<WorkflowEngineEvents>` parented to this bus so workflow
21
+ * events appear in the JSONL log.
22
+ */
23
+ lifecycleBus) {
14
24
  this.host = host;
15
25
  this.persistence = persistence;
26
+ this.lifecycleBus = lifecycleBus ?? host.lifecycleBus;
27
+ }
28
+ /** Resolve a run's event bus: caller-supplied wins; otherwise parent to the service lifecycle bus. */
29
+ resolveEvents(events) {
30
+ return (events ??
31
+ (this.lifecycleBus ? new EventBus({ lifecycleBus: this.lifecycleBus }) : undefined));
16
32
  }
17
33
  /** Load a workflow file and validate it. */
18
34
  async load(path) {
@@ -20,10 +36,17 @@ export class WorkflowService {
20
36
  }
21
37
  /** Run an already-loaded workflow definition. */
22
38
  async run(workflow, options = {}) {
39
+ const events = this.resolveEvents(options.events);
23
40
  if (workflow.kind === 'transition-flow') {
24
- return await new TransitionFlowDriver({ host: this.host, persistence: this.persistence }).run(workflow, options);
41
+ return await new TransitionFlowDriver({ host: this.host, persistence: this.persistence }).run(workflow, {
42
+ ...options,
43
+ events,
44
+ });
25
45
  }
26
- return await new StateMachineDriver({ host: this.host, persistence: this.persistence }).run(workflow, options);
46
+ return await new StateMachineDriver({ host: this.host, persistence: this.persistence }).run(workflow, {
47
+ ...options,
48
+ events,
49
+ });
27
50
  }
28
51
  /** Load and run a workflow file. */
29
52
  async runFile(path, options = {}) {
@@ -76,7 +99,7 @@ export class WorkflowService {
76
99
  const run = await this.persistence.loadRun(runId);
77
100
  const extKey = run?.external_key ?? undefined;
78
101
  const result = await this.persistence.reseedRun(runId, newState);
79
- void options?.events?.emit('workflow.run.reseeded', {
102
+ void this.resolveEvents(options?.events)?.emit('workflow.run.reseeded', {
80
103
  runId,
81
104
  fromState: result.fromState ?? '',
82
105
  toState: result.toState,
@@ -99,18 +122,19 @@ export class WorkflowService {
99
122
  // Re-open the run as running.
100
123
  const extKey = run.external_key ?? undefined;
101
124
  await this.persistence.finalizeRun(runId, 'running', '');
102
- void options?.events?.emit('workflow.run.resumed', { runId, node: currentState, externalKey: extKey });
125
+ const events = this.resolveEvents(options?.events);
126
+ void events?.emit('workflow.run.resumed', { runId, node: currentState, externalKey: extKey });
103
127
  // Resume through the appropriate driver, starting from the paused state (skip on-enter).
104
128
  if (workflow.kind === 'transition-flow') {
105
129
  return await new TransitionFlowDriver({
106
130
  host: this.host,
107
131
  persistence: this.persistence,
108
- }).resume(workflow, runId, currentState, extKey, options);
132
+ }).resume(workflow, runId, currentState, extKey, { ...options, events });
109
133
  }
110
134
  return await new StateMachineDriver({
111
135
  host: this.host,
112
136
  persistence: this.persistence,
113
- }).resume(workflow, runId, currentState, extKey, options);
137
+ }).resume(workflow, runId, currentState, extKey, { ...options, events });
114
138
  }
115
139
  /** List runs currently paused. Optional filters and ordering. */
116
140
  async listPausedRuns(options) {
@@ -154,14 +178,15 @@ export class WorkflowService {
154
178
  detail: `No state recorded for run "${runId}"`,
155
179
  };
156
180
  }
181
+ const events = this.resolveEvents(options?.events);
157
182
  // An external transition is a single guarded hop on an existing run, not a
158
183
  // run itself — borrow a span-free lifecycle so the transition persist+emit
159
184
  // mechanics reuse the same seam the drivers use (no new workflow.run span).
160
- const lifecycle = RunLifecycle.forExternalTransition(workflow.name, runId, { persistence: this.persistence, events: options?.events }, extKey);
185
+ const lifecycle = RunLifecycle.forExternalTransition(workflow.name, runId, { persistence: this.persistence, events }, extKey);
161
186
  // Find the matching transition from current state to requested state.
162
187
  const transition = workflow.transitions.find((t) => t.from === currentState && t.to === toState);
163
188
  if (transition === undefined) {
164
- return this.denyTransition(options, runId, currentState, toState, extKey, {
189
+ return this.denyTransition(events, runId, currentState, toState, extKey, {
165
190
  reason: 'no-such-transition',
166
191
  detail: `No transition from "${currentState}" to "${toState}"`,
167
192
  });
@@ -183,7 +208,7 @@ export class WorkflowService {
183
208
  });
184
209
  lifecycle.guardEvaluated(currentState, toState, transition.guard.kind, guardResult.passed);
185
210
  if (!guardResult.passed) {
186
- return this.denyTransition(options, runId, currentState, toState, extKey, {
211
+ return this.denyTransition(events, runId, currentState, toState, extKey, {
187
212
  reason: 'guard-failed',
188
213
  detail: `Guard "${transition.guard.kind}" denied transition from "${currentState}" to "${toState}"`,
189
214
  guardKind: transition.guard.kind,
@@ -196,7 +221,7 @@ export class WorkflowService {
196
221
  // record — external transitions don't drive phase tracking.
197
222
  const trigger = transition.trigger ?? null;
198
223
  await lifecycle.commitHop(currentState, toState, trigger, 0);
199
- void options?.events?.emit('workflow.transition.requested', {
224
+ void events?.emit('workflow.transition.requested', {
200
225
  runId,
201
226
  from: currentState,
202
227
  to: toState,
@@ -210,8 +235,8 @@ export class WorkflowService {
210
235
  * event in one place — the single denial seam for the external-transition path,
211
236
  * so the event payload and the returned reason can never drift apart.
212
237
  */
213
- denyTransition(options, runId, from, to, externalKey, denial) {
214
- void options?.events?.emit('workflow.transition.denied', {
238
+ denyTransition(events, runId, from, to, externalKey, denial) {
239
+ void events?.emit('workflow.transition.denied', {
215
240
  runId,
216
241
  from,
217
242
  to,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-dual-workflow-engine",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "@gobing-ai/ts-dual-workflow-engine — State-machine and transition-flow workflow runtime.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -48,9 +48,9 @@
48
48
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-dual-workflow-engine-v<version> && git push --tags' && exit 1"
49
49
  },
50
50
  "dependencies": {
51
- "@gobing-ai/ts-db": "^0.4.7",
52
- "@gobing-ai/ts-infra": "^0.4.7",
53
- "@gobing-ai/ts-runtime": "^0.4.7",
51
+ "@gobing-ai/ts-db": "^0.4.9",
52
+ "@gobing-ai/ts-infra": "^0.4.9",
53
+ "@gobing-ai/ts-runtime": "^0.4.9",
54
54
  "zod": "^4.1.0"
55
55
  },
56
56
  "devDependencies": {
package/src/host.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { NodeProcessExecutor, type ProcessExecutor } from '@gobing-ai/ts-runtime';
1
+ import type { BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
2
+ import { nodeBunFactory, type ProcessExecutor } from '@gobing-ai/ts-runtime';
2
3
  import { type CapabilityOrigin, CapabilityRegistry } from '@gobing-ai/ts-runtime/extension';
3
4
  import { WorkflowValidationError } from './errors';
4
5
  import type {
@@ -15,6 +16,8 @@ export class WorkflowEngineHost {
15
16
  private readonly actions = new CapabilityRegistry<ActionRunner>('workflow action');
16
17
  private readonly guards = new CapabilityRegistry<GuardRunner>('workflow guard');
17
18
 
19
+ constructor(readonly lifecycleBus?: EventBus<BusLifecycleEvents>) {}
20
+
18
21
  /** Register or replace an action runner. */
19
22
  registerAction(action: ActionRunner, origin: CapabilityOrigin = 'extension'): this {
20
23
  this.actions.register(action.kind, action, origin);
@@ -84,13 +87,19 @@ export class WorkflowEngineHost {
84
87
 
85
88
  /** Create a workflow host with built-in note, shell, always, and action-ok capabilities. */
86
89
  export function createDefaultWorkflowEngineHost(
87
- options: { processExecutor?: ProcessExecutor } = {},
90
+ options: { processExecutor?: ProcessExecutor; lifecycleBus?: EventBus<BusLifecycleEvents> } = {},
88
91
  ): WorkflowEngineHost {
89
- const host = new WorkflowEngineHost();
92
+ const host = new WorkflowEngineHost(options.lifecycleBus);
90
93
  host.registerAction(new NoteActionRunner(), 'builtin');
91
- host.registerAction(new ShellActionRunner(options.processExecutor ?? new NodeProcessExecutor()), 'builtin');
94
+ host.registerAction(
95
+ new ShellActionRunner(options.processExecutor ?? nodeBunFactory.createProcessExecutor()),
96
+ 'builtin',
97
+ );
92
98
  host.registerAction(new EventEmitActionRunner(), 'builtin');
93
- host.registerGuard(new ShellGuardRunner(options.processExecutor ?? new NodeProcessExecutor()), 'builtin');
99
+ host.registerGuard(
100
+ new ShellGuardRunner(options.processExecutor ?? nodeBunFactory.createProcessExecutor()),
101
+ 'builtin',
102
+ );
94
103
  host.registerGuard({ kind: 'always', evaluate: async () => true }, 'builtin');
95
104
  host.registerGuard({ kind: 'never', evaluate: async () => false }, 'builtin');
96
105
  host.registerGuard(
@@ -1,4 +1,11 @@
1
- import { addSpanEvent, type EventBus, getLogger, type Logger, traceAsync } from '@gobing-ai/ts-infra';
1
+ import {
2
+ addSpanEvent,
3
+ type BusLifecycleEvents,
4
+ EventBus,
5
+ getLogger,
6
+ type Logger,
7
+ traceAsync,
8
+ } from '@gobing-ai/ts-infra';
2
9
  import { getProcessEnv } from '@gobing-ai/ts-runtime';
3
10
  import type { WorkflowEngineEvents } from './events';
4
11
  import type {
@@ -61,6 +68,8 @@ export interface RunLifecycleDeps {
61
68
  readonly logger?: Logger;
62
69
  /** Optional event bus for structured in-process run observability. */
63
70
  readonly events?: EventBus<WorkflowEngineEvents>;
71
+ /** Parent bus used when RunLifecycle must construct its own workflow event bus. */
72
+ readonly lifecycleBus?: EventBus<BusLifecycleEvents>;
64
73
  }
65
74
 
66
75
  /**
@@ -86,7 +95,9 @@ export class RunLifecycle {
86
95
  this.runId = runId;
87
96
  this.externalKey = externalKey;
88
97
  this.persistence = deps.persistence;
89
- this.events = deps.events;
98
+ this.events =
99
+ deps.events ??
100
+ (deps.lifecycleBus ? new EventBus<WorkflowEngineEvents>({ lifecycleBus: deps.lifecycleBus }) : undefined);
90
101
  this.logger = (deps.logger ?? getLogger('workflow')).child({ runId, workflow: workflowName, mode });
91
102
  }
92
103
 
package/src/service.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { type BusLifecycleEvents, EventBus } from '@gobing-ai/ts-infra';
1
2
  import { loadWorkflowDef } from './config';
2
3
  import { FSMError, WorkflowResumeError } from './errors';
4
+ import type { WorkflowEngineEvents } from './events';
3
5
  import type { WorkflowEngineHost } from './host';
4
6
  import { RunLifecycle } from './run-lifecycle';
5
7
  import { StateMachineDriver } from './state-machine';
@@ -20,11 +22,32 @@ import { resolveTemplates } from './variables';
20
22
  export class WorkflowService {
21
23
  /** Per-run serialization: ensures concurrent requestTransition calls serialize. */
22
24
  private readonly runLocks = new Map<string, Promise<void>>();
25
+ private readonly lifecycleBus: EventBus<BusLifecycleEvents> | undefined;
23
26
 
24
27
  constructor(
25
28
  private readonly host: WorkflowEngineHost,
26
29
  private readonly persistence: WorkflowPersistenceAdapter,
27
- ) {}
30
+ /**
31
+ * Optional lifecycle bus to bridge `workflow.*` events into the
32
+ * application System Events stream (R3). When a run's `events` is
33
+ * omitted the service constructs an internal
34
+ * `EventBus<WorkflowEngineEvents>` parented to this bus so workflow
35
+ * events appear in the JSONL log.
36
+ */
37
+ lifecycleBus?: EventBus<BusLifecycleEvents>,
38
+ ) {
39
+ this.lifecycleBus = lifecycleBus ?? host.lifecycleBus;
40
+ }
41
+
42
+ /** Resolve a run's event bus: caller-supplied wins; otherwise parent to the service lifecycle bus. */
43
+ private resolveEvents(
44
+ events: EventBus<WorkflowEngineEvents> | undefined,
45
+ ): EventBus<WorkflowEngineEvents> | undefined {
46
+ return (
47
+ events ??
48
+ (this.lifecycleBus ? new EventBus<WorkflowEngineEvents>({ lifecycleBus: this.lifecycleBus }) : undefined)
49
+ );
50
+ }
28
51
 
29
52
  /** Load a workflow file and validate it. */
30
53
  async load(path: string): Promise<WorkflowDef> {
@@ -33,13 +56,17 @@ export class WorkflowService {
33
56
 
34
57
  /** Run an already-loaded workflow definition. */
35
58
  async run(workflow: WorkflowDef, options: WorkflowRunOptions = {}): Promise<WorkflowRunResult> {
59
+ const events = this.resolveEvents(options.events);
36
60
  if (workflow.kind === 'transition-flow') {
37
- return await new TransitionFlowDriver({ host: this.host, persistence: this.persistence }).run(
38
- workflow,
39
- options,
40
- );
61
+ return await new TransitionFlowDriver({ host: this.host, persistence: this.persistence }).run(workflow, {
62
+ ...options,
63
+ events,
64
+ });
41
65
  }
42
- return await new StateMachineDriver({ host: this.host, persistence: this.persistence }).run(workflow, options);
66
+ return await new StateMachineDriver({ host: this.host, persistence: this.persistence }).run(workflow, {
67
+ ...options,
68
+ events,
69
+ });
43
70
  }
44
71
 
45
72
  /** Load and run a workflow file. */
@@ -114,7 +141,7 @@ export class WorkflowService {
114
141
  const run = await this.persistence.loadRun(runId);
115
142
  const extKey = run?.external_key ?? undefined;
116
143
  const result = await this.persistence.reseedRun(runId, newState);
117
- void options?.events?.emit('workflow.run.reseeded', {
144
+ void this.resolveEvents(options?.events)?.emit('workflow.run.reseeded', {
118
145
  runId,
119
146
  fromState: result.fromState ?? '',
120
147
  toState: result.toState,
@@ -140,19 +167,20 @@ export class WorkflowService {
140
167
  // Re-open the run as running.
141
168
  const extKey = run.external_key ?? undefined;
142
169
  await this.persistence.finalizeRun(runId, 'running', '');
143
- void options?.events?.emit('workflow.run.resumed', { runId, node: currentState, externalKey: extKey });
170
+ const events = this.resolveEvents(options?.events);
171
+ void events?.emit('workflow.run.resumed', { runId, node: currentState, externalKey: extKey });
144
172
 
145
173
  // Resume through the appropriate driver, starting from the paused state (skip on-enter).
146
174
  if (workflow.kind === 'transition-flow') {
147
175
  return await new TransitionFlowDriver({
148
176
  host: this.host,
149
177
  persistence: this.persistence,
150
- }).resume(workflow, runId, currentState, extKey, options);
178
+ }).resume(workflow, runId, currentState, extKey, { ...options, events });
151
179
  }
152
180
  return await new StateMachineDriver({
153
181
  host: this.host,
154
182
  persistence: this.persistence,
155
- }).resume(workflow as StateMachineWorkflowDef, runId, currentState, extKey, options);
183
+ }).resume(workflow as StateMachineWorkflowDef, runId, currentState, extKey, { ...options, events });
156
184
  }
157
185
 
158
186
  /** List runs currently paused. Optional filters and ordering. */
@@ -211,6 +239,7 @@ export class WorkflowService {
211
239
  detail: `No state recorded for run "${runId}"`,
212
240
  };
213
241
  }
242
+ const events = this.resolveEvents(options?.events);
214
243
 
215
244
  // An external transition is a single guarded hop on an existing run, not a
216
245
  // run itself — borrow a span-free lifecycle so the transition persist+emit
@@ -218,14 +247,14 @@ export class WorkflowService {
218
247
  const lifecycle = RunLifecycle.forExternalTransition(
219
248
  workflow.name,
220
249
  runId,
221
- { persistence: this.persistence, events: options?.events },
250
+ { persistence: this.persistence, events },
222
251
  extKey,
223
252
  );
224
253
 
225
254
  // Find the matching transition from current state to requested state.
226
255
  const transition = workflow.transitions.find((t) => t.from === currentState && t.to === toState);
227
256
  if (transition === undefined) {
228
- return this.denyTransition(options, runId, currentState, toState, extKey, {
257
+ return this.denyTransition(events, runId, currentState, toState, extKey, {
229
258
  reason: 'no-such-transition',
230
259
  detail: `No transition from "${currentState}" to "${toState}"`,
231
260
  });
@@ -248,7 +277,7 @@ export class WorkflowService {
248
277
  });
249
278
  lifecycle.guardEvaluated(currentState, toState, transition.guard.kind, guardResult.passed);
250
279
  if (!guardResult.passed) {
251
- return this.denyTransition(options, runId, currentState, toState, extKey, {
280
+ return this.denyTransition(events, runId, currentState, toState, extKey, {
252
281
  reason: 'guard-failed',
253
282
  detail: `Guard "${transition.guard.kind}" denied transition from "${currentState}" to "${toState}"`,
254
283
  guardKind: transition.guard.kind,
@@ -262,7 +291,7 @@ export class WorkflowService {
262
291
  // record — external transitions don't drive phase tracking.
263
292
  const trigger = transition.trigger ?? null;
264
293
  await lifecycle.commitHop(currentState, toState, trigger, 0);
265
- void options?.events?.emit('workflow.transition.requested', {
294
+ void events?.emit('workflow.transition.requested', {
266
295
  runId,
267
296
  from: currentState,
268
297
  to: toState,
@@ -278,14 +307,14 @@ export class WorkflowService {
278
307
  * so the event payload and the returned reason can never drift apart.
279
308
  */
280
309
  private denyTransition(
281
- options: WorkflowRunOptions | undefined,
310
+ events: EventBus<WorkflowEngineEvents> | undefined,
282
311
  runId: string,
283
312
  from: string,
284
313
  to: string,
285
314
  externalKey: string | undefined,
286
315
  denial: Omit<TransitionDenied, 'allowed'>,
287
316
  ): TransitionDenied {
288
- void options?.events?.emit('workflow.transition.denied', {
317
+ void events?.emit('workflow.transition.denied', {
289
318
  runId,
290
319
  from,
291
320
  to,